861 lines
21 KiB
C
861 lines
21 KiB
C
|
|
/*
|
|
Quadradic Programming soliution.
|
|
|
|
Based on Luca Di Gaspero's QuadProgpp++, made available with
|
|
the following license:
|
|
|
|
MIT License
|
|
|
|
Copyright (c) 2007-2016 Luca Di Gaspero
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
Translation to C by Graeme W. Gill, Copyright 2017, also licensed under the
|
|
above license.
|
|
*/
|
|
|
|
/*
|
|
|
|
The quadprog_solve() function implements the algorithm of Goldfarb and Idnani
|
|
for the solution of a (convex) Quadratic Programming problem
|
|
by means of an active-set dual method.
|
|
|
|
The problem is in the form:
|
|
|
|
min 0.5 * x G x + g0 x
|
|
s.t.
|
|
CE^T x + ce0 = 0
|
|
CI^T x + ci0 >= 0
|
|
|
|
The matrix and vectors dimensions are as follows:
|
|
G: n * n
|
|
g0: n
|
|
|
|
CE: n * p
|
|
ce0: p
|
|
|
|
CI: n * m
|
|
ci0: m
|
|
|
|
x: n
|
|
|
|
References: D. Goldfarb, A. Idnani. A numerically stable dual method for solving
|
|
strictly convex quadratic programs. Mathematical Programming 27 (1983) pp. 1-33.
|
|
|
|
*/
|
|
|
|
#include "numsup.h"
|
|
#include "quadprog.h"
|
|
|
|
#undef TRACE_SOLVER /* Print progress */
|
|
|
|
/* Utility functions for updating some data needed by the solution method */
|
|
INLINE static void compute_d(double *d, double **J, double *np, int n);
|
|
INLINE static void update_z(double *z, double **J, double *d, int iq, int n);
|
|
INLINE static void update_r(double **R, double *r, double *d, int iq, int n);
|
|
static int add_constraint(double **R, double **J, double *d, int *piq, double *prnorm, int n);
|
|
INLINE static void delete_constraint(double **R, double **J, int *A, double *u,
|
|
int n, int p, int *piq, int l);
|
|
|
|
/* Utility functions for computing the Cholesky decomposition and solving */
|
|
/* linear systems */
|
|
static void cholesky_decomposition(double **A, int n);
|
|
static void cholesky_solve(double **L, double *x, double *b, int n);
|
|
INLINE static void forward_elimination(double **L, double *y, double *b, int n);
|
|
INLINE static void backward_elimination(double **U, double *x, double *y, int n);
|
|
|
|
/* Utility functions for computing the scalar product and the euclidean */
|
|
/* distance between two numbers */
|
|
INLINE static double scalar_product(double *x, double *y, int n);
|
|
INLINE static double distance(double a, double b);
|
|
|
|
/* Utility functions for printing vectors and matrices */
|
|
static void print_matrix(char* name, double **A, int n, int m);
|
|
|
|
static void print_vector(char* name, double *v, int n);
|
|
static void print_ivector(char* name, int *v, int n);
|
|
|
|
#define FMIN(A,B) ((A) < (B) ? (A) : (B))
|
|
|
|
#define FMAX(A,B) ((A) > (B) ? (A) : (B))
|
|
|
|
#define ASZ 10
|
|
#define VSZ 20
|
|
|
|
double quadprog( /* Return solution cost, QP_INFEASIBLE if infeasible/error */
|
|
double *x, /* Return x[n] value */
|
|
double **G, /* G[n][n] Quadratic combination matrix - modified */
|
|
double *g0, /* g0[n] Direct vector */
|
|
double **CE, /* CE[n][p] Equality constraint matrix */
|
|
double *ce0, /* ce0[p] Equality constraing constants */
|
|
double **CI, /* CI[n][m] Constraint matrix */
|
|
double *ci0, /* cie[m] Constraint constants */
|
|
int n, /* Number of variables */
|
|
int p, /* Number of equalities */
|
|
int m /* Number of constraints */
|
|
) {
|
|
int i, j, k, l; /* indices */
|
|
int ip; /* index of the constraint to be added to the active set */
|
|
double f_value = QP_INFEASIBLE, psi, c1, c2, sum, ss, R_norm;
|
|
double inf;
|
|
double t, t1, t2; /* t is the step length, which is the minimum of the partial */
|
|
/* step length t1 and the full step length t2 */
|
|
int q, iq, iter = 0;
|
|
|
|
double **R, **J;
|
|
double *s, *z, *r, *d, *np, *u, *x_old, *u_old;
|
|
int *A, *A_old, *iai;
|
|
short *iaexcl;
|
|
|
|
double *_R[ASZ], __R[ASZ][ASZ], *_J[ASZ], __J[ASZ][ASZ];
|
|
double _s[VSZ], _z[VSZ], _r[VSZ], _d[VSZ], _np[VSZ], _u[VSZ], _x_old[VSZ], _u_old[VSZ];
|
|
int _A[VSZ], _A_old[VSZ], _iai[VSZ];
|
|
short _iaexcl[VSZ];
|
|
|
|
/* Avoid malloc's if we can.. */
|
|
if (n <= ASZ) {
|
|
R = _R;
|
|
J = _J;
|
|
for (i = 0; i < n; i++) {
|
|
_R[i] = __R[i];
|
|
_J[i] = __J[i];
|
|
}
|
|
} else {
|
|
R = dmatrix(0, n-1, 0, n-1);
|
|
J = dmatrix(0, n-1, 0, n-1);
|
|
}
|
|
|
|
if (n <= VSZ) {
|
|
x_old = _x_old;
|
|
z = _z;
|
|
d = _d;
|
|
np = _np;
|
|
} else {
|
|
x_old = dvector(0, n-1);
|
|
z = dvector(0, n-1);
|
|
d = dvector(0, n-1);
|
|
np = dvector(0, n-1);
|
|
}
|
|
|
|
if ((m + p) <= VSZ) {
|
|
s = _s;
|
|
r = _r;
|
|
u = _u;
|
|
u_old = _u_old;
|
|
A = _A;
|
|
A_old = _A_old;
|
|
iai = _iai;
|
|
iaexcl = _iaexcl;
|
|
} else {
|
|
s = dvector(0, m + p-1);
|
|
r = dvector(0, m + p-1);
|
|
u = dvector(0, m + p-1);
|
|
u_old = dvector(0, m + p-1);
|
|
A = ivector(0, m + p-1);
|
|
A_old = ivector(0, m + p-1);
|
|
iai = ivector(0, m + p-1);
|
|
iaexcl = svector(0, m + p-1);
|
|
}
|
|
|
|
q = 0; /* size of the active set A (containing the indices of the active constraints) */
|
|
|
|
#ifdef TRACE_SOLVER
|
|
printf("\nStarting solve_quadprog\n");
|
|
print_matrix("G", G, n, n);
|
|
print_vector("g0", g0, n);
|
|
print_matrix("CE", CE, n, p);
|
|
print_vector("ce0", ce0, p);
|
|
print_matrix("CI", CI, n, m);
|
|
print_vector("ci0", ci0, m);
|
|
#endif
|
|
|
|
/*
|
|
* Preprocessing phase
|
|
*/
|
|
|
|
/* compute the trace of the original matrix G */
|
|
c1 = 0.0;
|
|
for (i = 0; i < n; i++)
|
|
c1 += G[i][i];
|
|
/* decompose the matrix G in the form L^T L */
|
|
cholesky_decomposition(G, n);
|
|
#ifdef TRACE_SOLVER
|
|
print_matrix("G", G, n, n);
|
|
#endif
|
|
|
|
/* initialize the matrix R */
|
|
for (i = 0; i < n; i++) {
|
|
d[i] = 0.0;
|
|
for (j = 0; j < n; j++)
|
|
R[i][j] = 0.0;
|
|
}
|
|
R_norm = 1.0; /* this variable will hold the norm of the matrix R */
|
|
|
|
/* compute the inverse of the factorized matrix G^-1, this is the initial value for H */
|
|
c2 = 0.0;
|
|
for (i = 0; i < n; i++) {
|
|
d[i] = 1.0;
|
|
forward_elimination(G, z, d, n);
|
|
for (j = 0; j < n; j++)
|
|
J[i][j] = z[j];
|
|
c2 += z[i];
|
|
d[i] = 0.0;
|
|
}
|
|
#ifdef TRACE_SOLVER
|
|
print_matrix("J", J, n, n);
|
|
#endif
|
|
|
|
/* c1 * c2 is an estimate for cond(G) */
|
|
|
|
/* Find the unconstrained minimizer of the quadratic form 0.5 * x G x + g0 x */
|
|
/* this is a feasible point in the dual space */
|
|
/* x = G^-1 * g0 */
|
|
cholesky_solve(G, x, g0, n);
|
|
for (i = 0; i < n; i++)
|
|
x[i] = -x[i];
|
|
/* and compute the current solution value */
|
|
f_value = 0.5 * scalar_product(g0, x, n);
|
|
#ifdef TRACE_SOLVER
|
|
printf("Unconstrained solution: f_value %f\n",f_value);
|
|
print_vector("x", x, n);
|
|
#endif
|
|
|
|
/* Add equality constraints to the working set A */
|
|
iq = 0;
|
|
for (i = 0; i < p; i++) {
|
|
for (j = 0; j < n; j++)
|
|
np[j] = CE[j][i];
|
|
compute_d(d, J, np, n);
|
|
update_z(z, J, d, iq, n);
|
|
update_r(R, r, d, iq, n);
|
|
#ifdef TRACE_SOLVER
|
|
print_matrix("R", R, n, n);
|
|
print_vector("z", z, n);
|
|
print_vector("r", r, iq);
|
|
print_vector("d", d, n);
|
|
#endif
|
|
|
|
/* compute full step length t2: i.e., the minimum step in primal space s.t. */
|
|
/* the contraint becomes feasible */
|
|
t2 = 0.0;
|
|
if (fabs(scalar_product(z, z, n)) > DBL_EPSILON) /* i.e. z != 0 */
|
|
t2 = (-scalar_product(np, x, n) - ce0[i]) / scalar_product(z, np, n);
|
|
|
|
/* set x = x + t2 * z */
|
|
for (k = 0; k < n; k++)
|
|
x[k] += t2 * z[k];
|
|
|
|
/* set u = u+ */
|
|
u[iq] = t2;
|
|
for (k = 0; k < iq; k++)
|
|
u[k] -= t2 * r[k];
|
|
|
|
/* compute the new solution value */
|
|
f_value += 0.5 * (t2 * t2) * scalar_product(z, np, n);
|
|
A[i] = -i - 1;
|
|
|
|
if (!add_constraint(R, J, d, &iq, &R_norm, n)) {
|
|
/* Equality constraints are linearly dependent */
|
|
#ifdef TRACE_SOLVER
|
|
printf("Constraints are linearly dependent\n");
|
|
#endif
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* set iai = K \ A */
|
|
for (i = 0; i < m; i++)
|
|
iai[i] = i;
|
|
|
|
/* Hmm. Use of crossing goto's is a little Fortran like... */
|
|
|
|
l1: iter++;
|
|
#ifdef TRACE_SOLVER
|
|
print_vector("x", x, n);
|
|
#endif
|
|
/* step 1: choose a violated constraint */
|
|
for (i = p; i < iq; i++) {
|
|
ip = A[i];
|
|
iai[ip] = -1;
|
|
}
|
|
|
|
/* compute s[x] = ci^T * x + ci0 for all elements of K \ A */
|
|
ss = 0.0;
|
|
psi = 0.0; /* this value will contain the sum of all infeasibilities */
|
|
ip = 0; /* ip will be the index of the chosen violated constraint */
|
|
for (i = 0; i < m; i++) {
|
|
iaexcl[i] = 1;
|
|
sum = 0.0;
|
|
for (j = 0; j < n; j++)
|
|
sum += CI[j][i] * x[j];
|
|
sum += ci0[i];
|
|
s[i] = sum;
|
|
psi += FMIN(0.0, sum);
|
|
}
|
|
#ifdef TRACE_SOLVER
|
|
print_vector("s", s, m);
|
|
#endif
|
|
|
|
if (fabs(psi) <= m * DBL_EPSILON * c1 * c2* 100.0) {
|
|
/* numerically there are no infeasibilities anymore */
|
|
#ifdef TRACE_SOLVER
|
|
printf("numerically there are no infeasibilities anymore\n");
|
|
#endif
|
|
q = iq;
|
|
goto done;
|
|
}
|
|
|
|
/* save old values for u and A */
|
|
for (i = 0; i < iq; i++) {
|
|
u_old[i] = u[i];
|
|
A_old[i] = A[i];
|
|
}
|
|
|
|
/* and for x */
|
|
for (i = 0; i < n; i++)
|
|
x_old[i] = x[i];
|
|
|
|
l2: /* Step 2: check for feasibility and determine a new S-pair */
|
|
for (i = 0; i < m; i++) {
|
|
if (s[i] < ss && iai[i] != -1 && iaexcl[i]) {
|
|
ss = s[i];
|
|
ip = i;
|
|
}
|
|
}
|
|
if (ss >= 0.0) {
|
|
#ifdef TRACE_SOLVER
|
|
printf(" ss %f >= 0.0\n",ss);
|
|
#endif
|
|
q = iq;
|
|
goto done;
|
|
}
|
|
|
|
/* set np = n[ip] */
|
|
for (i = 0; i < n; i++)
|
|
np[i] = CI[i][ip];
|
|
/* set u = [u 0]^T */
|
|
u[iq] = 0.0;
|
|
/* add ip to the active set A */
|
|
A[iq] = ip;
|
|
|
|
#ifdef TRACE_SOLVER
|
|
printf("Trying with constraint %d\n",ip);
|
|
print_vector("np", np, n);
|
|
#endif
|
|
|
|
l2a:
|
|
/* Step 2a: determine step direction */
|
|
/* compute z = H np: the step direction in the primal space (through J, see the paper) */
|
|
compute_d(d, J, np, n);
|
|
update_z(z, J, d, iq, n);
|
|
/* compute N* np (if q > 0): the negative of the step direction in the dual space */
|
|
update_r(R, r, d, iq, n);
|
|
#ifdef TRACE_SOLVER
|
|
printf("Step direction z\n");
|
|
print_vector("z", z, n);
|
|
print_vector("r", r, iq + 1);
|
|
print_vector("u", u, iq + 1);
|
|
print_vector("d", d, n);
|
|
print_ivector("A", A, iq + 1);
|
|
#endif
|
|
|
|
/* Step 2b: compute step length */
|
|
l = 0;
|
|
/* Compute t1: partial step length (maximum step in dual space */
|
|
/* without violating dual feasibility */
|
|
t1 = QP_INFEASIBLE; /* +inf */
|
|
/* find the index l s.t. it reaches the minimum of u+[x] / r */
|
|
for (k = p; k < iq; k++) {
|
|
if (r[k] > 0.0) {
|
|
if (u[k] / r[k] < t1) {
|
|
t1 = u[k] / r[k];
|
|
l = A[k];
|
|
}
|
|
}
|
|
}
|
|
/* Compute t2: full step length (minimum step in primal space */
|
|
/* such that the constraint ip becomes feasible */
|
|
if (fabs(scalar_product(z, z, n)) > DBL_EPSILON) { /* i.e. z != 0 */
|
|
t2 = -s[ip] / scalar_product(z, np, n);
|
|
if (t2 < 0) /* patch suggested by Takano Akio for handling numerical inconsistencies */
|
|
t2 = QP_INFEASIBLE;
|
|
} else
|
|
t2 = QP_INFEASIBLE; /* +inf */
|
|
|
|
/* the step is chosen as the minimum of t1 and t2 */
|
|
t = FMIN(t1, t2);
|
|
#ifdef TRACE_SOLVER
|
|
printf("Step size: %e (t1 = %e, t2 = %e\n",t,t1,t2);
|
|
#endif
|
|
|
|
/* Step 2c: determine new S-pair and take step: */
|
|
|
|
/* case (i): no step in primal or dual space */
|
|
if (t >= QP_INFEASIBLE) {
|
|
/* QPP is infeasible */
|
|
// FIXME: unbounded to raise
|
|
#ifdef TRACE_SOLVER
|
|
printf(" QPP is infeasible\n");
|
|
#endif
|
|
q = iq;
|
|
f_value = QP_INFEASIBLE;
|
|
goto done;
|
|
}
|
|
/* case (ii): step in dual space */
|
|
if (t2 >= QP_INFEASIBLE) {
|
|
/* set u = u + t * [-r 1] and drop constraint l from the active set A */
|
|
for (k = 0; k < iq; k++)
|
|
u[k] -= t * r[k];
|
|
u[iq] += t;
|
|
iai[l] = l;
|
|
delete_constraint(R, J, A, u, n, p, &iq, l);
|
|
#ifdef TRACE_SOLVER
|
|
printf(" in dual space: %f\n",f_value);
|
|
print_vector("x", x, n);
|
|
print_vector("z", z, n);
|
|
print_ivector("A", A, iq + 1);
|
|
#endif
|
|
goto l2a;
|
|
}
|
|
|
|
/* case (iii): step in primal and dual space */
|
|
|
|
/* set x = x + t * z */
|
|
for (k = 0; k < n; k++)
|
|
x[k] += t * z[k];
|
|
/* update the solution value */
|
|
f_value += t * scalar_product(z, np, n) * (0.5 * t + u[iq]);
|
|
/* u = u + t * [-r 1] */
|
|
for (k = 0; k < iq; k++)
|
|
u[k] -= t * r[k];
|
|
u[iq] += t;
|
|
#ifdef TRACE_SOLVER
|
|
printf(" in both spaces: %f\n",f_value);
|
|
print_vector("x", x, n);
|
|
print_vector("u", u, iq + 1);
|
|
print_vector("r", r, iq + 1);
|
|
print_ivector("A", A, iq + 1);
|
|
#endif
|
|
|
|
if (fabs(t - t2) < DBL_EPSILON) {
|
|
#ifdef TRACE_SOLVER
|
|
printf("Full step has been taken %f\n",t);
|
|
print_vector("x", x, n);
|
|
#endif
|
|
/* full step has been taken */
|
|
/* Add constraint ip to the active set*/
|
|
if (!add_constraint(R, J, d, &iq, &R_norm, n)) {
|
|
#ifdef TRACE_SOLVER
|
|
printf("add_constraint failed - removing constraint ant step\n",t);
|
|
#endif
|
|
iaexcl[ip] = 0;
|
|
delete_constraint(R, J, A, u, n, p, &iq, ip);
|
|
#ifdef TRACE_SOLVER
|
|
print_matrix("R", R, n, n);
|
|
print_ivector("A", A, iq);
|
|
print_ivector("iai", iai, m+p);
|
|
#endif
|
|
for (i = 0; i < m; i++)
|
|
iai[i] = i;
|
|
for (i = p; i < iq; i++) {
|
|
A[i] = A_old[i];
|
|
u[i] = u_old[i];
|
|
iai[A[i]] = -1;
|
|
}
|
|
for (i = 0; i < n; i++)
|
|
x[i] = x_old[i];
|
|
goto l2; /* go to step 2 */
|
|
} else
|
|
iai[ip] = -1;
|
|
#ifdef TRACE_SOLVER
|
|
print_matrix("R", R, n, n);
|
|
print_ivector("A", A, iq);
|
|
print_ivector("iai", iai, m + p);
|
|
#endif
|
|
goto l1;
|
|
}
|
|
|
|
/* a patial step has taken */
|
|
#ifdef TRACE_SOLVER
|
|
printf("Partial step has taken %f\n",t);
|
|
print_vector("x", x, n);
|
|
#endif
|
|
/* drop constraint l */
|
|
iai[l] = l;
|
|
delete_constraint(R, J, A, u, n, p, &iq, l);
|
|
#ifdef TRACE_SOLVER
|
|
print_matrix("R", R, n, n);
|
|
print_ivector("A", A, iq);
|
|
#endif
|
|
|
|
/* update s[ip] = CI * x + ci0 */
|
|
sum = 0.0;
|
|
for (k = 0; k < n; k++)
|
|
sum += CI[k][ip] * x[k];
|
|
s[ip] = sum + ci0[ip];
|
|
|
|
#ifdef TRACE_SOLVER
|
|
print_vector("s", s, m);
|
|
#endif
|
|
goto l2a;
|
|
|
|
done:;
|
|
|
|
/* Cleanup and return f value */
|
|
if (n > ASZ) {
|
|
free_dmatrix(R, 0, n-1, 0, n-1);
|
|
free_dmatrix(J, 0, n-1, 0, n-1);
|
|
}
|
|
|
|
if (n > VSZ) {
|
|
free_dvector(x_old, 0, n-1);
|
|
free_dvector(z, 0, n-1);
|
|
free_dvector(d, 0, n-1);
|
|
free_dvector(np, 0, n-1);
|
|
}
|
|
|
|
if ((m + p) > VSZ) {
|
|
free_dvector(s, 0, m + p-1);
|
|
free_dvector(r, 0, m + p-1);
|
|
free_dvector(u, 0, m + p-1);
|
|
free_dvector(u_old, 0, m + p-1);
|
|
free_ivector(A, 0, m + p-1);
|
|
free_ivector(A_old, 0, m + p-1);
|
|
free_ivector(iai, 0, m + p-1);
|
|
free_svector(iaexcl, 0, m + p-1);
|
|
}
|
|
|
|
#ifdef TRACE_SOLVER
|
|
printf("Returning f_value %e\n",f_value);
|
|
print_vector("Returning x", x, n);
|
|
#endif
|
|
|
|
return f_value;
|
|
}
|
|
|
|
INLINE static void compute_d(double *d, double **J, double *np, int n) {
|
|
int i, j;
|
|
double sum;
|
|
|
|
/* compute d = H^T * np */
|
|
for (i = 0; i < n; i++) {
|
|
sum = 0.0;
|
|
for (j = 0; j < n; j++)
|
|
sum += J[j][i] * np[j];
|
|
d[i] = sum;
|
|
}
|
|
}
|
|
|
|
INLINE static void update_z(double *z, double **J, double *d, int iq, int n) {
|
|
int i, j;
|
|
|
|
/* setting of z = H * d */
|
|
for (i = 0; i < n; i++) {
|
|
z[i] = 0.0;
|
|
for (j = iq; j < n; j++)
|
|
z[i] += J[i][j] * d[j];
|
|
}
|
|
}
|
|
|
|
INLINE static void update_r(double **R, double *r, double *d, int iq, int n) {
|
|
int i, j;
|
|
double sum;
|
|
|
|
/* setting of r = R^-1 d */
|
|
for (i = iq - 1; i >= 0; i--) {
|
|
sum = 0.0;
|
|
for (j = i + 1; j < iq; j++)
|
|
sum += R[i][j] * r[j];
|
|
r[i] = (d[i] - sum) / R[i][i];
|
|
}
|
|
}
|
|
|
|
static int add_constraint(double **R, double **J, double *d, int *piq, double *prnorm, int n) {
|
|
int iq = *piq;
|
|
int i, j, k;
|
|
double cc, ss, h, t1, t2, xny;
|
|
|
|
#ifdef TRACE_SOLVER
|
|
printf("Add constraint %d",iq);
|
|
#endif
|
|
|
|
/* we have to find the Givens rotation which will reduce the element */
|
|
/* d[j] to zero. if it is already zero we don't have to do anything, except */
|
|
/* of decreasing j */
|
|
for (j = n - 1; j >= iq + 1; j--) {
|
|
/* The Givens rotation is done with the matrix (cc cs, cs -cc). */
|
|
/* If cc is one, then element (j) of d is zero compared with element */
|
|
/* (j - 1). Hence we don't have to do anything. */
|
|
/* If cc is zero, then we just have to switch column (j) and column (j - 1) */
|
|
/* of J. Since we only switch columns in J, we have to be careful how we */
|
|
/* update d depending on the sign of gs. */
|
|
/* Otherwise we have to apply the Givens rotation to these columns. */
|
|
/* The i - 1 element of d has to be updated to h. */
|
|
cc = d[j - 1];
|
|
ss = d[j];
|
|
h = distance(cc, ss);
|
|
if (fabs(h) < DBL_EPSILON) /* h == 0 */
|
|
continue;
|
|
d[j] = 0.0;
|
|
ss = ss / h;
|
|
cc = cc / h;
|
|
if (cc < 0.0) {
|
|
cc = -cc;
|
|
ss = -ss;
|
|
d[j - 1] = -h;
|
|
} else
|
|
d[j - 1] = h;
|
|
xny = ss / (1.0 + cc);
|
|
for (k = 0; k < n; k++) {
|
|
t1 = J[k][j - 1];
|
|
t2 = J[k][j];
|
|
J[k][j - 1] = t1 * cc + t2 * ss;
|
|
J[k][j] = xny * (t1 + J[k][j - 1]) - t2;
|
|
}
|
|
}
|
|
/* update the number of constraints added*/
|
|
*piq = ++iq;
|
|
/* To update R we have to put the iq components of the d vector into column iq - 1 of R */
|
|
for (i = 0; i < iq; i++)
|
|
R[i][iq - 1] = d[i];
|
|
#ifdef TRACE_SOLVER
|
|
printf("/%d\n", iq);
|
|
print_matrix("R", R, iq, iq);
|
|
print_matrix("J", J, n, n);
|
|
print_vector("d", d, iq);
|
|
#endif
|
|
|
|
if (fabs(d[iq - 1]) <= DBL_EPSILON * *prnorm) {
|
|
/* problem degenerate */
|
|
return 0;
|
|
}
|
|
*prnorm = (double)FMAX(*prnorm, fabs(d[iq - 1]));
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void delete_constraint(double **R, double **J, int *A, double *u,
|
|
int n, int p, int *piq, int l) {
|
|
int iq = *piq;
|
|
int i, j, k, qq = -1; // just to prevent warnings from smart compilers
|
|
double cc, ss, h, xny, t1, t2;
|
|
|
|
#ifdef TRACE_SOLVER
|
|
printf("Delete constraint %d %d",l,iq);
|
|
#endif
|
|
|
|
/* Find the index qq for active constraint l to be removed */
|
|
for (i = p; i < iq; i++) {
|
|
if (A[i] == l) {
|
|
qq = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* remove the constraint from the active set and the duals */
|
|
for (i = qq; i < iq - 1; i++) {
|
|
A[i] = A[i + 1];
|
|
u[i] = u[i + 1];
|
|
for (j = 0; j < n; j++)
|
|
R[j][i] = R[j][i + 1];
|
|
}
|
|
|
|
A[iq - 1] = A[iq];
|
|
u[iq - 1] = u[iq];
|
|
A[iq] = 0;
|
|
u[iq] = 0.0;
|
|
for (j = 0; j < iq; j++)
|
|
R[j][iq - 1] = 0.0;
|
|
|
|
/* constraint has been fully removed */
|
|
*piq = --iq;
|
|
|
|
#ifdef TRACE_SOLVER
|
|
printf("/%d\n",iq);
|
|
#endif
|
|
|
|
if (iq == 0)
|
|
return;
|
|
|
|
for (j = qq; j < iq; j++) {
|
|
cc = R[j][j];
|
|
ss = R[j + 1][j];
|
|
h = distance(cc, ss);
|
|
if (fabs(h) < DBL_EPSILON) // h == 0
|
|
continue;
|
|
cc = cc / h;
|
|
ss = ss / h;
|
|
R[j + 1][j] = 0.0;
|
|
if (cc < 0.0) {
|
|
R[j][j] = -h;
|
|
cc = -cc;
|
|
ss = -ss;
|
|
} else
|
|
R[j][j] = h;
|
|
|
|
xny = ss / (1.0 + cc);
|
|
for (k = j + 1; k < iq; k++) {
|
|
t1 = R[j][k];
|
|
t2 = R[j + 1][k];
|
|
R[j][k] = t1 * cc + t2 * ss;
|
|
R[j + 1][k] = xny * (t1 + R[j][k]) - t2;
|
|
}
|
|
for (k = 0; k < n; k++) {
|
|
t1 = J[k][j];
|
|
t2 = J[k][j + 1];
|
|
J[k][j] = t1 * cc + t2 * ss;
|
|
J[k][j + 1] = xny * (J[k][j] + t1) - t2;
|
|
}
|
|
}
|
|
}
|
|
|
|
INLINE static double distance(double a, double b) {
|
|
double a1, b1, t;
|
|
a1 = fabs(a);
|
|
b1 = fabs(b);
|
|
if (a1 > b1) {
|
|
t = (b1 / a1);
|
|
return a1 * sqrt(1.0 + t * t);
|
|
} else if (b1 > a1) {
|
|
t = (a1 / b1);
|
|
return b1 * sqrt(1.0 + t * t);
|
|
}
|
|
return a1 * sqrt(2.0);
|
|
}
|
|
|
|
|
|
INLINE static double scalar_product(double *x, double *y, int n) {
|
|
int i;
|
|
double sum;
|
|
|
|
sum = 0.0;
|
|
for (i = 0; i < n; i++)
|
|
sum += x[i] * y[i];
|
|
return sum;
|
|
}
|
|
|
|
// !!! this doesn't work for semi-definite matricies, ie.
|
|
// they will have diagonal sum == 0.0 !!!
|
|
static void cholesky_decomposition(double **A, int n) {
|
|
int i, j, k;
|
|
double sum;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
for (j = i; j < n; j++) {
|
|
sum = A[i][j];
|
|
for (k = i - 1; k >= 0; k--)
|
|
sum -= A[i][k] * A[j][k];
|
|
#ifdef TRACE_SOLVER
|
|
printf("sum[%d][%d] = %f\n",i,j,sum);
|
|
#endif
|
|
if (i == j) {
|
|
if (sum <= 0.0)
|
|
{
|
|
// raise error
|
|
print_matrix("A", A, n, n);
|
|
error("QuadProg:cholesky decomposition, matrix is not postive definite, sum: %e",sum);
|
|
}
|
|
A[i][i] = sqrt(sum);
|
|
} else
|
|
A[j][i] = sum / A[i][i];
|
|
}
|
|
for (k = i + 1; k < n; k++)
|
|
A[i][k] = A[k][i];
|
|
}
|
|
}
|
|
|
|
static void cholesky_solve(double **L, double *x, double *b, int n) {
|
|
double *y, _y[VSZ];
|
|
|
|
if (n <= VSZ)
|
|
y = _y;
|
|
else
|
|
y = dvector(0, n-1);
|
|
|
|
/* Solve L * y = b */
|
|
forward_elimination(L, y, b, n);
|
|
|
|
/* Solve L^T * x = y */
|
|
backward_elimination(L, x, y, n);
|
|
|
|
if (n > VSZ)
|
|
free_dvector(y, 0, n-1);
|
|
}
|
|
|
|
INLINE static void forward_elimination(double **L, double *y, double *b, int n) {
|
|
int i, j;
|
|
|
|
y[0] = b[0] / L[0][0];
|
|
for (i = 1; i < n; i++) {
|
|
y[i] = b[i];
|
|
for (j = 0; j < i; j++)
|
|
y[i] -= L[i][j] * y[j];
|
|
y[i] = y[i] / L[i][i];
|
|
}
|
|
}
|
|
|
|
INLINE static void backward_elimination(double **U, double *x, double *y, int n) {
|
|
int i, j;
|
|
|
|
x[n - 1] = y[n - 1] / U[n - 1][n - 1];
|
|
for (i = n - 2; i >= 0; i--) {
|
|
x[i] = y[i];
|
|
for (j = i + 1; j < n; j++)
|
|
x[i] -= U[i][j] * x[j];
|
|
x[i] = x[i] / U[i][i];
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------- */
|
|
|
|
static void print_matrix(char *name, double **A, int n, int m) {
|
|
int i, j;
|
|
|
|
printf("%s: \n",name);
|
|
for (i = 0; i < n; i++) {
|
|
printf(" ");
|
|
for (j = 0; j < m; j++)
|
|
printf("%f%s", A[i][j], j < (m-1) ? ", " : "");
|
|
printf("\n");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void print_vector(char *name, double *v, int n) {
|
|
int i;
|
|
|
|
printf("%s: \n",name);
|
|
printf(" ");
|
|
for (i = 0; i < n; i++)
|
|
printf("%f%s", v[i], i < (n-1) ? ", " : "");
|
|
printf("\n\n");
|
|
}
|
|
|
|
static void print_ivector(char *name, int *v, int n) {
|
|
int i;
|
|
|
|
printf("%s: \n",name);
|
|
printf(" ");
|
|
for (i = 0; i < n; i++)
|
|
printf("%d%s", v[i], i < (n-1) ? ", " : "");
|
|
printf("\n\n");
|
|
}
|
|
|