alpar@9: /* glpipm.c */ alpar@9: alpar@9: /*********************************************************************** alpar@9: * This code is part of GLPK (GNU Linear Programming Kit). alpar@9: * alpar@9: * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, alpar@9: * 2009, 2010, 2011 Andrew Makhorin, Department for Applied Informatics, alpar@9: * Moscow Aviation Institute, Moscow, Russia. All rights reserved. alpar@9: * E-mail: . alpar@9: * alpar@9: * GLPK is free software: you can redistribute it and/or modify it alpar@9: * under the terms of the GNU General Public License as published by alpar@9: * the Free Software Foundation, either version 3 of the License, or alpar@9: * (at your option) any later version. alpar@9: * alpar@9: * GLPK is distributed in the hope that it will be useful, but WITHOUT alpar@9: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY alpar@9: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public alpar@9: * License for more details. alpar@9: * alpar@9: * You should have received a copy of the GNU General Public License alpar@9: * along with GLPK. If not, see . alpar@9: ***********************************************************************/ alpar@9: alpar@9: #include "glpipm.h" alpar@9: #include "glpmat.h" alpar@9: alpar@9: #define ITER_MAX 100 alpar@9: /* maximal number of iterations */ alpar@9: alpar@9: struct csa alpar@9: { /* common storage area */ alpar@9: /*--------------------------------------------------------------*/ alpar@9: /* LP data */ alpar@9: int m; alpar@9: /* number of rows (equality constraints) */ alpar@9: int n; alpar@9: /* number of columns (structural variables) */ alpar@9: int *A_ptr; /* int A_ptr[1+m+1]; */ alpar@9: int *A_ind; /* int A_ind[A_ptr[m+1]]; */ alpar@9: double *A_val; /* double A_val[A_ptr[m+1]]; */ alpar@9: /* mxn-matrix A in storage-by-rows format */ alpar@9: double *b; /* double b[1+m]; */ alpar@9: /* m-vector b of right-hand sides */ alpar@9: double *c; /* double c[1+n]; */ alpar@9: /* n-vector c of objective coefficients; c[0] is constant term of alpar@9: the objective function */ alpar@9: /*--------------------------------------------------------------*/ alpar@9: /* LP solution */ alpar@9: double *x; /* double x[1+n]; */ alpar@9: double *y; /* double y[1+m]; */ alpar@9: double *z; /* double z[1+n]; */ alpar@9: /* current point in primal-dual space; the best point on exit */ alpar@9: /*--------------------------------------------------------------*/ alpar@9: /* control parameters */ alpar@9: const glp_iptcp *parm; alpar@9: /*--------------------------------------------------------------*/ alpar@9: /* working arrays and variables */ alpar@9: double *D; /* double D[1+n]; */ alpar@9: /* diagonal nxn-matrix D = X*inv(Z), where X = diag(x[j]) and alpar@9: Z = diag(z[j]) */ alpar@9: int *P; /* int P[1+m+m]; */ alpar@9: /* permutation mxm-matrix P used to minimize fill-in in Cholesky alpar@9: factorization */ alpar@9: int *S_ptr; /* int S_ptr[1+m+1]; */ alpar@9: int *S_ind; /* int S_ind[S_ptr[m+1]]; */ alpar@9: double *S_val; /* double S_val[S_ptr[m+1]]; */ alpar@9: double *S_diag; /* double S_diag[1+m]; */ alpar@9: /* symmetric mxm-matrix S = P*A*D*A'*P' whose upper triangular alpar@9: part without diagonal elements is stored in S_ptr, S_ind, and alpar@9: S_val in storage-by-rows format, diagonal elements are stored alpar@9: in S_diag */ alpar@9: int *U_ptr; /* int U_ptr[1+m+1]; */ alpar@9: int *U_ind; /* int U_ind[U_ptr[m+1]]; */ alpar@9: double *U_val; /* double U_val[U_ptr[m+1]]; */ alpar@9: double *U_diag; /* double U_diag[1+m]; */ alpar@9: /* upper triangular mxm-matrix U defining Cholesky factorization alpar@9: S = U'*U; its non-diagonal elements are stored in U_ptr, U_ind, alpar@9: U_val in storage-by-rows format, diagonal elements are stored alpar@9: in U_diag */ alpar@9: int iter; alpar@9: /* iteration number (0, 1, 2, ...); iter = 0 corresponds to the alpar@9: initial point */ alpar@9: double obj; alpar@9: /* current value of the objective function */ alpar@9: double rpi; alpar@9: /* relative primal infeasibility rpi = ||A*x-b||/(1+||b||) */ alpar@9: double rdi; alpar@9: /* relative dual infeasibility rdi = ||A'*y+z-c||/(1+||c||) */ alpar@9: double gap; alpar@9: /* primal-dual gap = |c'*x-b'*y|/(1+|c'*x|) which is a relative alpar@9: difference between primal and dual objective functions */ alpar@9: double phi; alpar@9: /* merit function phi = ||A*x-b||/max(1,||b||) + alpar@9: + ||A'*y+z-c||/max(1,||c||) + alpar@9: + |c'*x-b'*y|/max(1,||b||,||c||) */ alpar@9: double mu; alpar@9: /* duality measure mu = x'*z/n (used as barrier parameter) */ alpar@9: double rmu; alpar@9: /* rmu = max(||A*x-b||,||A'*y+z-c||)/mu */ alpar@9: double rmu0; alpar@9: /* the initial value of rmu on iteration 0 */ alpar@9: double *phi_min; /* double phi_min[1+ITER_MAX]; */ alpar@9: /* phi_min[k] = min(phi[k]), where phi[k] is the value of phi on alpar@9: k-th iteration, 0 <= k <= iter */ alpar@9: int best_iter; alpar@9: /* iteration number, on which the value of phi reached its best alpar@9: (minimal) value */ alpar@9: double *best_x; /* double best_x[1+n]; */ alpar@9: double *best_y; /* double best_y[1+m]; */ alpar@9: double *best_z; /* double best_z[1+n]; */ alpar@9: /* best point (in the sense of the merit function phi) which has alpar@9: been reached on iteration iter_best */ alpar@9: double best_obj; alpar@9: /* objective value at the best point */ alpar@9: double *dx_aff; /* double dx_aff[1+n]; */ alpar@9: double *dy_aff; /* double dy_aff[1+m]; */ alpar@9: double *dz_aff; /* double dz_aff[1+n]; */ alpar@9: /* affine scaling direction */ alpar@9: double alfa_aff_p, alfa_aff_d; alpar@9: /* maximal primal and dual stepsizes in affine scaling direction, alpar@9: on which x and z are still non-negative */ alpar@9: double mu_aff; alpar@9: /* duality measure mu_aff = x_aff'*z_aff/n in the boundary point alpar@9: x_aff' = x+alfa_aff_p*dx_aff, z_aff' = z+alfa_aff_d*dz_aff */ alpar@9: double sigma; alpar@9: /* Mehrotra's heuristic parameter (0 <= sigma <= 1) */ alpar@9: double *dx_cc; /* double dx_cc[1+n]; */ alpar@9: double *dy_cc; /* double dy_cc[1+m]; */ alpar@9: double *dz_cc; /* double dz_cc[1+n]; */ alpar@9: /* centering corrector direction */ alpar@9: double *dx; /* double dx[1+n]; */ alpar@9: double *dy; /* double dy[1+m]; */ alpar@9: double *dz; /* double dz[1+n]; */ alpar@9: /* final combined direction dx = dx_aff+dx_cc, dy = dy_aff+dy_cc, alpar@9: dz = dz_aff+dz_cc */ alpar@9: double alfa_max_p; alpar@9: double alfa_max_d; alpar@9: /* maximal primal and dual stepsizes in combined direction, on alpar@9: which x and z are still non-negative */ alpar@9: }; alpar@9: alpar@9: /*********************************************************************** alpar@9: * initialize - allocate and initialize common storage area alpar@9: * alpar@9: * This routine allocates and initializes the common storage area (CSA) alpar@9: * used by interior-point method routines. */ alpar@9: alpar@9: static void initialize(struct csa *csa) alpar@9: { int m = csa->m; alpar@9: int n = csa->n; alpar@9: int i; alpar@9: if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Matrix A has %d non-zeros\n", csa->A_ptr[m+1]-1); alpar@9: csa->D = xcalloc(1+n, sizeof(double)); alpar@9: /* P := I */ alpar@9: csa->P = xcalloc(1+m+m, sizeof(int)); alpar@9: for (i = 1; i <= m; i++) csa->P[i] = csa->P[m+i] = i; alpar@9: /* S := A*A', symbolically */ alpar@9: csa->S_ptr = xcalloc(1+m+1, sizeof(int)); alpar@9: csa->S_ind = adat_symbolic(m, n, csa->P, csa->A_ptr, csa->A_ind, alpar@9: csa->S_ptr); alpar@9: if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Matrix S = A*A' has %d non-zeros (upper triangle)\n", alpar@9: csa->S_ptr[m+1]-1 + m); alpar@9: /* determine P using specified ordering algorithm */ alpar@9: if (csa->parm->ord_alg == GLP_ORD_NONE) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Original ordering is being used\n"); alpar@9: for (i = 1; i <= m; i++) alpar@9: csa->P[i] = csa->P[m+i] = i; alpar@9: } alpar@9: else if (csa->parm->ord_alg == GLP_ORD_QMD) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Minimum degree ordering (QMD)...\n"); alpar@9: min_degree(m, csa->S_ptr, csa->S_ind, csa->P); alpar@9: } alpar@9: else if (csa->parm->ord_alg == GLP_ORD_AMD) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Approximate minimum degree ordering (AMD)...\n"); alpar@9: amd_order1(m, csa->S_ptr, csa->S_ind, csa->P); alpar@9: } alpar@9: else if (csa->parm->ord_alg == GLP_ORD_SYMAMD) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Approximate minimum degree ordering (SYMAMD)...\n") alpar@9: ; alpar@9: symamd_ord(m, csa->S_ptr, csa->S_ind, csa->P); alpar@9: } alpar@9: else alpar@9: xassert(csa != csa); alpar@9: /* S := P*A*A'*P', symbolically */ alpar@9: xfree(csa->S_ind); alpar@9: csa->S_ind = adat_symbolic(m, n, csa->P, csa->A_ptr, csa->A_ind, alpar@9: csa->S_ptr); alpar@9: csa->S_val = xcalloc(csa->S_ptr[m+1], sizeof(double)); alpar@9: csa->S_diag = xcalloc(1+m, sizeof(double)); alpar@9: /* compute Cholesky factorization S = U'*U, symbolically */ alpar@9: if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Computing Cholesky factorization S = L*L'...\n"); alpar@9: csa->U_ptr = xcalloc(1+m+1, sizeof(int)); alpar@9: csa->U_ind = chol_symbolic(m, csa->S_ptr, csa->S_ind, csa->U_ptr); alpar@9: if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Matrix L has %d non-zeros\n", csa->U_ptr[m+1]-1 + m); alpar@9: csa->U_val = xcalloc(csa->U_ptr[m+1], sizeof(double)); alpar@9: csa->U_diag = xcalloc(1+m, sizeof(double)); alpar@9: csa->iter = 0; alpar@9: csa->obj = 0.0; alpar@9: csa->rpi = 0.0; alpar@9: csa->rdi = 0.0; alpar@9: csa->gap = 0.0; alpar@9: csa->phi = 0.0; alpar@9: csa->mu = 0.0; alpar@9: csa->rmu = 0.0; alpar@9: csa->rmu0 = 0.0; alpar@9: csa->phi_min = xcalloc(1+ITER_MAX, sizeof(double)); alpar@9: csa->best_iter = 0; alpar@9: csa->best_x = xcalloc(1+n, sizeof(double)); alpar@9: csa->best_y = xcalloc(1+m, sizeof(double)); alpar@9: csa->best_z = xcalloc(1+n, sizeof(double)); alpar@9: csa->best_obj = 0.0; alpar@9: csa->dx_aff = xcalloc(1+n, sizeof(double)); alpar@9: csa->dy_aff = xcalloc(1+m, sizeof(double)); alpar@9: csa->dz_aff = xcalloc(1+n, sizeof(double)); alpar@9: csa->alfa_aff_p = 0.0; alpar@9: csa->alfa_aff_d = 0.0; alpar@9: csa->mu_aff = 0.0; alpar@9: csa->sigma = 0.0; alpar@9: csa->dx_cc = xcalloc(1+n, sizeof(double)); alpar@9: csa->dy_cc = xcalloc(1+m, sizeof(double)); alpar@9: csa->dz_cc = xcalloc(1+n, sizeof(double)); alpar@9: csa->dx = csa->dx_aff; alpar@9: csa->dy = csa->dy_aff; alpar@9: csa->dz = csa->dz_aff; alpar@9: csa->alfa_max_p = 0.0; alpar@9: csa->alfa_max_d = 0.0; alpar@9: return; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * A_by_vec - compute y = A*x alpar@9: * alpar@9: * This routine computes matrix-vector product y = A*x, where A is the alpar@9: * constraint matrix. */ alpar@9: alpar@9: static void A_by_vec(struct csa *csa, double x[], double y[]) alpar@9: { /* compute y = A*x */ alpar@9: int m = csa->m; alpar@9: int *A_ptr = csa->A_ptr; alpar@9: int *A_ind = csa->A_ind; alpar@9: double *A_val = csa->A_val; alpar@9: int i, t, beg, end; alpar@9: double temp; alpar@9: for (i = 1; i <= m; i++) alpar@9: { temp = 0.0; alpar@9: beg = A_ptr[i], end = A_ptr[i+1]; alpar@9: for (t = beg; t < end; t++) temp += A_val[t] * x[A_ind[t]]; alpar@9: y[i] = temp; alpar@9: } alpar@9: return; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * AT_by_vec - compute y = A'*x alpar@9: * alpar@9: * This routine computes matrix-vector product y = A'*x, where A' is a alpar@9: * matrix transposed to the constraint matrix A. */ alpar@9: alpar@9: static void AT_by_vec(struct csa *csa, double x[], double y[]) alpar@9: { /* compute y = A'*x, where A' is transposed to A */ alpar@9: int m = csa->m; alpar@9: int n = csa->n; alpar@9: int *A_ptr = csa->A_ptr; alpar@9: int *A_ind = csa->A_ind; alpar@9: double *A_val = csa->A_val; alpar@9: int i, j, t, beg, end; alpar@9: double temp; alpar@9: for (j = 1; j <= n; j++) y[j] = 0.0; alpar@9: for (i = 1; i <= m; i++) alpar@9: { temp = x[i]; alpar@9: if (temp == 0.0) continue; alpar@9: beg = A_ptr[i], end = A_ptr[i+1]; alpar@9: for (t = beg; t < end; t++) y[A_ind[t]] += A_val[t] * temp; alpar@9: } alpar@9: return; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * decomp_NE - numeric factorization of matrix S = P*A*D*A'*P' alpar@9: * alpar@9: * This routine implements numeric phase of Cholesky factorization of alpar@9: * the matrix S = P*A*D*A'*P', which is a permuted matrix of the normal alpar@9: * equation system. Matrix D is assumed to be already computed. */ alpar@9: alpar@9: static void decomp_NE(struct csa *csa) alpar@9: { adat_numeric(csa->m, csa->n, csa->P, csa->A_ptr, csa->A_ind, alpar@9: csa->A_val, csa->D, csa->S_ptr, csa->S_ind, csa->S_val, alpar@9: csa->S_diag); alpar@9: chol_numeric(csa->m, csa->S_ptr, csa->S_ind, csa->S_val, alpar@9: csa->S_diag, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag); alpar@9: return; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * solve_NE - solve normal equation system alpar@9: * alpar@9: * This routine solves the normal equation system: alpar@9: * alpar@9: * A*D*A'*y = h. alpar@9: * alpar@9: * It is assumed that the matrix A*D*A' has been previously factorized alpar@9: * by the routine decomp_NE. alpar@9: * alpar@9: * On entry the array y contains the vector of right-hand sides h. On alpar@9: * exit this array contains the computed vector of unknowns y. alpar@9: * alpar@9: * Once the vector y has been computed the routine checks for numeric alpar@9: * stability. If the residual vector: alpar@9: * alpar@9: * r = A*D*A'*y - h alpar@9: * alpar@9: * is relatively small, the routine returns zero, otherwise non-zero is alpar@9: * returned. */ alpar@9: alpar@9: static int solve_NE(struct csa *csa, double y[]) alpar@9: { int m = csa->m; alpar@9: int n = csa->n; alpar@9: int *P = csa->P; alpar@9: int i, j, ret = 0; alpar@9: double *h, *r, *w; alpar@9: /* save vector of right-hand sides h */ alpar@9: h = xcalloc(1+m, sizeof(double)); alpar@9: for (i = 1; i <= m; i++) h[i] = y[i]; alpar@9: /* solve normal equation system (A*D*A')*y = h */ alpar@9: /* since S = P*A*D*A'*P' = U'*U, then A*D*A' = P'*U'*U*P, so we alpar@9: have inv(A*D*A') = P'*inv(U)*inv(U')*P */ alpar@9: /* w := P*h */ alpar@9: w = xcalloc(1+m, sizeof(double)); alpar@9: for (i = 1; i <= m; i++) w[i] = y[P[i]]; alpar@9: /* w := inv(U')*w */ alpar@9: ut_solve(m, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag, w); alpar@9: /* w := inv(U)*w */ alpar@9: u_solve(m, csa->U_ptr, csa->U_ind, csa->U_val, csa->U_diag, w); alpar@9: /* y := P'*w */ alpar@9: for (i = 1; i <= m; i++) y[i] = w[P[m+i]]; alpar@9: xfree(w); alpar@9: /* compute residual vector r = A*D*A'*y - h */ alpar@9: r = xcalloc(1+m, sizeof(double)); alpar@9: /* w := A'*y */ alpar@9: w = xcalloc(1+n, sizeof(double)); alpar@9: AT_by_vec(csa, y, w); alpar@9: /* w := D*w */ alpar@9: for (j = 1; j <= n; j++) w[j] *= csa->D[j]; alpar@9: /* r := A*w */ alpar@9: A_by_vec(csa, w, r); alpar@9: xfree(w); alpar@9: /* r := r - h */ alpar@9: for (i = 1; i <= m; i++) r[i] -= h[i]; alpar@9: /* check for numeric stability */ alpar@9: for (i = 1; i <= m; i++) alpar@9: { if (fabs(r[i]) / (1.0 + fabs(h[i])) > 1e-4) alpar@9: { ret = 1; alpar@9: break; alpar@9: } alpar@9: } alpar@9: xfree(h); alpar@9: xfree(r); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * solve_NS - solve Newtonian system alpar@9: * alpar@9: * This routine solves the Newtonian system: alpar@9: * alpar@9: * A*dx = p alpar@9: * alpar@9: * A'*dy + dz = q alpar@9: * alpar@9: * Z*dx + X*dz = r alpar@9: * alpar@9: * where X = diag(x[j]), Z = diag(z[j]), by reducing it to the normal alpar@9: * equation system: alpar@9: * alpar@9: * (A*inv(Z)*X*A')*dy = A*inv(Z)*(X*q-r)+p alpar@9: * alpar@9: * (it is assumed that the matrix A*inv(Z)*X*A' has been factorized by alpar@9: * the routine decomp_NE). alpar@9: * alpar@9: * Once vector dy has been computed the routine computes vectors dx and alpar@9: * dz as follows: alpar@9: * alpar@9: * dx = inv(Z)*(X*(A'*dy-q)+r) alpar@9: * alpar@9: * dz = inv(X)*(r-Z*dx) alpar@9: * alpar@9: * The routine solve_NS returns the same code which was reported by the alpar@9: * routine solve_NE (see above). */ alpar@9: alpar@9: static int solve_NS(struct csa *csa, double p[], double q[], double r[], alpar@9: double dx[], double dy[], double dz[]) alpar@9: { int m = csa->m; alpar@9: int n = csa->n; alpar@9: double *x = csa->x; alpar@9: double *z = csa->z; alpar@9: int i, j, ret; alpar@9: double *w = dx; alpar@9: /* compute the vector of right-hand sides A*inv(Z)*(X*q-r)+p for alpar@9: the normal equation system */ alpar@9: for (j = 1; j <= n; j++) alpar@9: w[j] = (x[j] * q[j] - r[j]) / z[j]; alpar@9: A_by_vec(csa, w, dy); alpar@9: for (i = 1; i <= m; i++) dy[i] += p[i]; alpar@9: /* solve the normal equation system to compute vector dy */ alpar@9: ret = solve_NE(csa, dy); alpar@9: /* compute vectors dx and dz */ alpar@9: AT_by_vec(csa, dy, dx); alpar@9: for (j = 1; j <= n; j++) alpar@9: { dx[j] = (x[j] * (dx[j] - q[j]) + r[j]) / z[j]; alpar@9: dz[j] = (r[j] - z[j] * dx[j]) / x[j]; alpar@9: } alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * initial_point - choose initial point using Mehrotra's heuristic alpar@9: * alpar@9: * This routine chooses a starting point using a heuristic proposed in alpar@9: * the paper: alpar@9: * alpar@9: * S. Mehrotra. On the implementation of a primal-dual interior point alpar@9: * method. SIAM J. on Optim., 2(4), pp. 575-601, 1992. alpar@9: * alpar@9: * The starting point x in the primal space is chosen as a solution of alpar@9: * the following least squares problem: alpar@9: * alpar@9: * minimize ||x|| alpar@9: * alpar@9: * subject to A*x = b alpar@9: * alpar@9: * which can be computed explicitly as follows: alpar@9: * alpar@9: * x = A'*inv(A*A')*b alpar@9: * alpar@9: * Similarly, the starting point (y, z) in the dual space is chosen as alpar@9: * a solution of the following least squares problem: alpar@9: * alpar@9: * minimize ||z|| alpar@9: * alpar@9: * subject to A'*y + z = c alpar@9: * alpar@9: * which can be computed explicitly as follows: alpar@9: * alpar@9: * y = inv(A*A')*A*c alpar@9: * alpar@9: * z = c - A'*y alpar@9: * alpar@9: * However, some components of the vectors x and z may be non-positive alpar@9: * or close to zero, so the routine uses a Mehrotra's heuristic to find alpar@9: * a more appropriate starting point. */ alpar@9: alpar@9: static void initial_point(struct csa *csa) alpar@9: { int m = csa->m; alpar@9: int n = csa->n; alpar@9: double *b = csa->b; alpar@9: double *c = csa->c; alpar@9: double *x = csa->x; alpar@9: double *y = csa->y; alpar@9: double *z = csa->z; alpar@9: double *D = csa->D; alpar@9: int i, j; alpar@9: double dp, dd, ex, ez, xz; alpar@9: /* factorize A*A' */ alpar@9: for (j = 1; j <= n; j++) D[j] = 1.0; alpar@9: decomp_NE(csa); alpar@9: /* x~ = A'*inv(A*A')*b */ alpar@9: for (i = 1; i <= m; i++) y[i] = b[i]; alpar@9: solve_NE(csa, y); alpar@9: AT_by_vec(csa, y, x); alpar@9: /* y~ = inv(A*A')*A*c */ alpar@9: A_by_vec(csa, c, y); alpar@9: solve_NE(csa, y); alpar@9: /* z~ = c - A'*y~ */ alpar@9: AT_by_vec(csa, y,z); alpar@9: for (j = 1; j <= n; j++) z[j] = c[j] - z[j]; alpar@9: /* use Mehrotra's heuristic in order to choose more appropriate alpar@9: starting point with positive components of vectors x and z */ alpar@9: dp = dd = 0.0; alpar@9: for (j = 1; j <= n; j++) alpar@9: { if (dp < -1.5 * x[j]) dp = -1.5 * x[j]; alpar@9: if (dd < -1.5 * z[j]) dd = -1.5 * z[j]; alpar@9: } alpar@9: /* note that b = 0 involves x = 0, and c = 0 involves y = 0 and alpar@9: z = 0, so we need to be careful */ alpar@9: if (dp == 0.0) dp = 1.5; alpar@9: if (dd == 0.0) dd = 1.5; alpar@9: ex = ez = xz = 0.0; alpar@9: for (j = 1; j <= n; j++) alpar@9: { ex += (x[j] + dp); alpar@9: ez += (z[j] + dd); alpar@9: xz += (x[j] + dp) * (z[j] + dd); alpar@9: } alpar@9: dp += 0.5 * (xz / ez); alpar@9: dd += 0.5 * (xz / ex); alpar@9: for (j = 1; j <= n; j++) alpar@9: { x[j] += dp; alpar@9: z[j] += dd; alpar@9: xassert(x[j] > 0.0 && z[j] > 0.0); alpar@9: } alpar@9: return; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * basic_info - perform basic computations at the current point alpar@9: * alpar@9: * This routine computes the following quantities at the current point: alpar@9: * alpar@9: * 1) value of the objective function: alpar@9: * alpar@9: * F = c'*x + c[0] alpar@9: * alpar@9: * 2) relative primal infeasibility: alpar@9: * alpar@9: * rpi = ||A*x-b|| / (1+||b||) alpar@9: * alpar@9: * 3) relative dual infeasibility: alpar@9: * alpar@9: * rdi = ||A'*y+z-c|| / (1+||c||) alpar@9: * alpar@9: * 4) primal-dual gap (relative difference between the primal and the alpar@9: * dual objective function values): alpar@9: * alpar@9: * gap = |c'*x-b'*y| / (1+|c'*x|) alpar@9: * alpar@9: * 5) merit function: alpar@9: * alpar@9: * phi = ||A*x-b|| / max(1,||b||) + ||A'*y+z-c|| / max(1,||c||) + alpar@9: * alpar@9: * + |c'*x-b'*y| / max(1,||b||,||c||) alpar@9: * alpar@9: * 6) duality measure: alpar@9: * alpar@9: * mu = x'*z / n alpar@9: * alpar@9: * 7) the ratio of infeasibility to mu: alpar@9: * alpar@9: * rmu = max(||A*x-b||,||A'*y+z-c||) / mu alpar@9: * alpar@9: * where ||*|| denotes euclidian norm, *' denotes transposition. */ alpar@9: alpar@9: static void basic_info(struct csa *csa) alpar@9: { int m = csa->m; alpar@9: int n = csa->n; alpar@9: double *b = csa->b; alpar@9: double *c = csa->c; alpar@9: double *x = csa->x; alpar@9: double *y = csa->y; alpar@9: double *z = csa->z; alpar@9: int i, j; alpar@9: double norm1, bnorm, norm2, cnorm, cx, by, *work, temp; alpar@9: /* compute value of the objective function */ alpar@9: temp = c[0]; alpar@9: for (j = 1; j <= n; j++) temp += c[j] * x[j]; alpar@9: csa->obj = temp; alpar@9: /* norm1 = ||A*x-b|| */ alpar@9: work = xcalloc(1+m, sizeof(double)); alpar@9: A_by_vec(csa, x, work); alpar@9: norm1 = 0.0; alpar@9: for (i = 1; i <= m; i++) alpar@9: norm1 += (work[i] - b[i]) * (work[i] - b[i]); alpar@9: norm1 = sqrt(norm1); alpar@9: xfree(work); alpar@9: /* bnorm = ||b|| */ alpar@9: bnorm = 0.0; alpar@9: for (i = 1; i <= m; i++) bnorm += b[i] * b[i]; alpar@9: bnorm = sqrt(bnorm); alpar@9: /* compute relative primal infeasibility */ alpar@9: csa->rpi = norm1 / (1.0 + bnorm); alpar@9: /* norm2 = ||A'*y+z-c|| */ alpar@9: work = xcalloc(1+n, sizeof(double)); alpar@9: AT_by_vec(csa, y, work); alpar@9: norm2 = 0.0; alpar@9: for (j = 1; j <= n; j++) alpar@9: norm2 += (work[j] + z[j] - c[j]) * (work[j] + z[j] - c[j]); alpar@9: norm2 = sqrt(norm2); alpar@9: xfree(work); alpar@9: /* cnorm = ||c|| */ alpar@9: cnorm = 0.0; alpar@9: for (j = 1; j <= n; j++) cnorm += c[j] * c[j]; alpar@9: cnorm = sqrt(cnorm); alpar@9: /* compute relative dual infeasibility */ alpar@9: csa->rdi = norm2 / (1.0 + cnorm); alpar@9: /* by = b'*y */ alpar@9: by = 0.0; alpar@9: for (i = 1; i <= m; i++) by += b[i] * y[i]; alpar@9: /* cx = c'*x */ alpar@9: cx = 0.0; alpar@9: for (j = 1; j <= n; j++) cx += c[j] * x[j]; alpar@9: /* compute primal-dual gap */ alpar@9: csa->gap = fabs(cx - by) / (1.0 + fabs(cx)); alpar@9: /* compute merit function */ alpar@9: csa->phi = 0.0; alpar@9: csa->phi += norm1 / (bnorm > 1.0 ? bnorm : 1.0); alpar@9: csa->phi += norm2 / (cnorm > 1.0 ? cnorm : 1.0); alpar@9: temp = 1.0; alpar@9: if (temp < bnorm) temp = bnorm; alpar@9: if (temp < cnorm) temp = cnorm; alpar@9: csa->phi += fabs(cx - by) / temp; alpar@9: /* compute duality measure */ alpar@9: temp = 0.0; alpar@9: for (j = 1; j <= n; j++) temp += x[j] * z[j]; alpar@9: csa->mu = temp / (double)n; alpar@9: /* compute the ratio of infeasibility to mu */ alpar@9: csa->rmu = (norm1 > norm2 ? norm1 : norm2) / csa->mu; alpar@9: return; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * make_step - compute next point using Mehrotra's technique alpar@9: * alpar@9: * This routine computes the next point using the predictor-corrector alpar@9: * technique proposed in the paper: alpar@9: * alpar@9: * S. Mehrotra. On the implementation of a primal-dual interior point alpar@9: * method. SIAM J. on Optim., 2(4), pp. 575-601, 1992. alpar@9: * alpar@9: * At first, the routine computes so called affine scaling (predictor) alpar@9: * direction (dx_aff,dy_aff,dz_aff) which is a solution of the system: alpar@9: * alpar@9: * A*dx_aff = b - A*x alpar@9: * alpar@9: * A'*dy_aff + dz_aff = c - A'*y - z alpar@9: * alpar@9: * Z*dx_aff + X*dz_aff = - X*Z*e alpar@9: * alpar@9: * where (x,y,z) is the current point, X = diag(x[j]), Z = diag(z[j]), alpar@9: * e = (1,...,1)'. alpar@9: * alpar@9: * Then, the routine computes the centering parameter sigma, using the alpar@9: * following Mehrotra's heuristic: alpar@9: * alpar@9: * alfa_aff_p = inf{0 <= alfa <= 1 | x+alfa*dx_aff >= 0} alpar@9: * alpar@9: * alfa_aff_d = inf{0 <= alfa <= 1 | z+alfa*dz_aff >= 0} alpar@9: * alpar@9: * mu_aff = (x+alfa_aff_p*dx_aff)'*(z+alfa_aff_d*dz_aff)/n alpar@9: * alpar@9: * sigma = (mu_aff/mu)^3 alpar@9: * alpar@9: * where alfa_aff_p is the maximal stepsize along the affine scaling alpar@9: * direction in the primal space, alfa_aff_d is the maximal stepsize alpar@9: * along the same direction in the dual space. alpar@9: * alpar@9: * After determining sigma the routine computes so called centering alpar@9: * (corrector) direction (dx_cc,dy_cc,dz_cc) which is the solution of alpar@9: * the system: alpar@9: * alpar@9: * A*dx_cc = 0 alpar@9: * alpar@9: * A'*dy_cc + dz_cc = 0 alpar@9: * alpar@9: * Z*dx_cc + X*dz_cc = sigma*mu*e - X*Z*e alpar@9: * alpar@9: * Finally, the routine computes the combined direction alpar@9: * alpar@9: * (dx,dy,dz) = (dx_aff,dy_aff,dz_aff) + (dx_cc,dy_cc,dz_cc) alpar@9: * alpar@9: * and determines maximal primal and dual stepsizes along the combined alpar@9: * direction: alpar@9: * alpar@9: * alfa_max_p = inf{0 <= alfa <= 1 | x+alfa*dx >= 0} alpar@9: * alpar@9: * alfa_max_d = inf{0 <= alfa <= 1 | z+alfa*dz >= 0} alpar@9: * alpar@9: * In order to prevent the next point to be too close to the boundary alpar@9: * of the positive ortant, the routine decreases maximal stepsizes: alpar@9: * alpar@9: * alfa_p = gamma_p * alfa_max_p alpar@9: * alpar@9: * alfa_d = gamma_d * alfa_max_d alpar@9: * alpar@9: * where gamma_p and gamma_d are scaling factors, and computes the next alpar@9: * point: alpar@9: * alpar@9: * x_new = x + alfa_p * dx alpar@9: * alpar@9: * y_new = y + alfa_d * dy alpar@9: * alpar@9: * z_new = z + alfa_d * dz alpar@9: * alpar@9: * which becomes the current point on the next iteration. */ alpar@9: alpar@9: static int make_step(struct csa *csa) alpar@9: { int m = csa->m; alpar@9: int n = csa->n; alpar@9: double *b = csa->b; alpar@9: double *c = csa->c; alpar@9: double *x = csa->x; alpar@9: double *y = csa->y; alpar@9: double *z = csa->z; alpar@9: double *dx_aff = csa->dx_aff; alpar@9: double *dy_aff = csa->dy_aff; alpar@9: double *dz_aff = csa->dz_aff; alpar@9: double *dx_cc = csa->dx_cc; alpar@9: double *dy_cc = csa->dy_cc; alpar@9: double *dz_cc = csa->dz_cc; alpar@9: double *dx = csa->dx; alpar@9: double *dy = csa->dy; alpar@9: double *dz = csa->dz; alpar@9: int i, j, ret = 0; alpar@9: double temp, gamma_p, gamma_d, *p, *q, *r; alpar@9: /* allocate working arrays */ alpar@9: p = xcalloc(1+m, sizeof(double)); alpar@9: q = xcalloc(1+n, sizeof(double)); alpar@9: r = xcalloc(1+n, sizeof(double)); alpar@9: /* p = b - A*x */ alpar@9: A_by_vec(csa, x, p); alpar@9: for (i = 1; i <= m; i++) p[i] = b[i] - p[i]; alpar@9: /* q = c - A'*y - z */ alpar@9: AT_by_vec(csa, y,q); alpar@9: for (j = 1; j <= n; j++) q[j] = c[j] - q[j] - z[j]; alpar@9: /* r = - X * Z * e */ alpar@9: for (j = 1; j <= n; j++) r[j] = - x[j] * z[j]; alpar@9: /* solve the first Newtonian system */ alpar@9: if (solve_NS(csa, p, q, r, dx_aff, dy_aff, dz_aff)) alpar@9: { ret = 1; alpar@9: goto done; alpar@9: } alpar@9: /* alfa_aff_p = inf{0 <= alfa <= 1 | x + alfa*dx_aff >= 0} */ alpar@9: /* alfa_aff_d = inf{0 <= alfa <= 1 | z + alfa*dz_aff >= 0} */ alpar@9: csa->alfa_aff_p = csa->alfa_aff_d = 1.0; alpar@9: for (j = 1; j <= n; j++) alpar@9: { if (dx_aff[j] < 0.0) alpar@9: { temp = - x[j] / dx_aff[j]; alpar@9: if (csa->alfa_aff_p > temp) csa->alfa_aff_p = temp; alpar@9: } alpar@9: if (dz_aff[j] < 0.0) alpar@9: { temp = - z[j] / dz_aff[j]; alpar@9: if (csa->alfa_aff_d > temp) csa->alfa_aff_d = temp; alpar@9: } alpar@9: } alpar@9: /* mu_aff = (x+alfa_aff_p*dx_aff)' * (z+alfa_aff_d*dz_aff) / n */ alpar@9: temp = 0.0; alpar@9: for (j = 1; j <= n; j++) alpar@9: temp += (x[j] + csa->alfa_aff_p * dx_aff[j]) * alpar@9: (z[j] + csa->alfa_aff_d * dz_aff[j]); alpar@9: csa->mu_aff = temp / (double)n; alpar@9: /* sigma = (mu_aff/mu)^3 */ alpar@9: temp = csa->mu_aff / csa->mu; alpar@9: csa->sigma = temp * temp * temp; alpar@9: /* p = 0 */ alpar@9: for (i = 1; i <= m; i++) p[i] = 0.0; alpar@9: /* q = 0 */ alpar@9: for (j = 1; j <= n; j++) q[j] = 0.0; alpar@9: /* r = sigma * mu * e - X * Z * e */ alpar@9: for (j = 1; j <= n; j++) alpar@9: r[j] = csa->sigma * csa->mu - dx_aff[j] * dz_aff[j]; alpar@9: /* solve the second Newtonian system with the same coefficients alpar@9: but with altered right-hand sides */ alpar@9: if (solve_NS(csa, p, q, r, dx_cc, dy_cc, dz_cc)) alpar@9: { ret = 1; alpar@9: goto done; alpar@9: } alpar@9: /* (dx,dy,dz) = (dx_aff,dy_aff,dz_aff) + (dx_cc,dy_cc,dz_cc) */ alpar@9: for (j = 1; j <= n; j++) dx[j] = dx_aff[j] + dx_cc[j]; alpar@9: for (i = 1; i <= m; i++) dy[i] = dy_aff[i] + dy_cc[i]; alpar@9: for (j = 1; j <= n; j++) dz[j] = dz_aff[j] + dz_cc[j]; alpar@9: /* alfa_max_p = inf{0 <= alfa <= 1 | x + alfa*dx >= 0} */ alpar@9: /* alfa_max_d = inf{0 <= alfa <= 1 | z + alfa*dz >= 0} */ alpar@9: csa->alfa_max_p = csa->alfa_max_d = 1.0; alpar@9: for (j = 1; j <= n; j++) alpar@9: { if (dx[j] < 0.0) alpar@9: { temp = - x[j] / dx[j]; alpar@9: if (csa->alfa_max_p > temp) csa->alfa_max_p = temp; alpar@9: } alpar@9: if (dz[j] < 0.0) alpar@9: { temp = - z[j] / dz[j]; alpar@9: if (csa->alfa_max_d > temp) csa->alfa_max_d = temp; alpar@9: } alpar@9: } alpar@9: /* determine scale factors (not implemented yet) */ alpar@9: gamma_p = 0.90; alpar@9: gamma_d = 0.90; alpar@9: /* compute the next point */ alpar@9: for (j = 1; j <= n; j++) alpar@9: { x[j] += gamma_p * csa->alfa_max_p * dx[j]; alpar@9: xassert(x[j] > 0.0); alpar@9: } alpar@9: for (i = 1; i <= m; i++) alpar@9: y[i] += gamma_d * csa->alfa_max_d * dy[i]; alpar@9: for (j = 1; j <= n; j++) alpar@9: { z[j] += gamma_d * csa->alfa_max_d * dz[j]; alpar@9: xassert(z[j] > 0.0); alpar@9: } alpar@9: done: /* free working arrays */ alpar@9: xfree(p); alpar@9: xfree(q); alpar@9: xfree(r); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * terminate - deallocate common storage area alpar@9: * alpar@9: * This routine frees all memory allocated to the common storage area alpar@9: * used by interior-point method routines. */ alpar@9: alpar@9: static void terminate(struct csa *csa) alpar@9: { xfree(csa->D); alpar@9: xfree(csa->P); alpar@9: xfree(csa->S_ptr); alpar@9: xfree(csa->S_ind); alpar@9: xfree(csa->S_val); alpar@9: xfree(csa->S_diag); alpar@9: xfree(csa->U_ptr); alpar@9: xfree(csa->U_ind); alpar@9: xfree(csa->U_val); alpar@9: xfree(csa->U_diag); alpar@9: xfree(csa->phi_min); alpar@9: xfree(csa->best_x); alpar@9: xfree(csa->best_y); alpar@9: xfree(csa->best_z); alpar@9: xfree(csa->dx_aff); alpar@9: xfree(csa->dy_aff); alpar@9: xfree(csa->dz_aff); alpar@9: xfree(csa->dx_cc); alpar@9: xfree(csa->dy_cc); alpar@9: xfree(csa->dz_cc); alpar@9: return; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * ipm_main - main interior-point method routine alpar@9: * alpar@9: * This is a main routine of the primal-dual interior-point method. alpar@9: * alpar@9: * The routine ipm_main returns one of the following codes: alpar@9: * alpar@9: * 0 - optimal solution found; alpar@9: * 1 - problem has no feasible (primal or dual) solution; alpar@9: * 2 - no convergence; alpar@9: * 3 - iteration limit exceeded; alpar@9: * 4 - numeric instability on solving Newtonian system. alpar@9: * alpar@9: * In case of non-zero return code the routine returns the best point, alpar@9: * which has been reached during optimization. */ alpar@9: alpar@9: static int ipm_main(struct csa *csa) alpar@9: { int m = csa->m; alpar@9: int n = csa->n; alpar@9: int i, j, status; alpar@9: double temp; alpar@9: /* choose initial point using Mehrotra's heuristic */ alpar@9: if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Guessing initial point...\n"); alpar@9: initial_point(csa); alpar@9: /* main loop starts here */ alpar@9: if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Optimization begins...\n"); alpar@9: for (;;) alpar@9: { /* perform basic computations at the current point */ alpar@9: basic_info(csa); alpar@9: /* save initial value of rmu */ alpar@9: if (csa->iter == 0) csa->rmu0 = csa->rmu; alpar@9: /* accumulate values of min(phi[k]) and save the best point */ alpar@9: xassert(csa->iter <= ITER_MAX); alpar@9: if (csa->iter == 0 || csa->phi_min[csa->iter-1] > csa->phi) alpar@9: { csa->phi_min[csa->iter] = csa->phi; alpar@9: csa->best_iter = csa->iter; alpar@9: for (j = 1; j <= n; j++) csa->best_x[j] = csa->x[j]; alpar@9: for (i = 1; i <= m; i++) csa->best_y[i] = csa->y[i]; alpar@9: for (j = 1; j <= n; j++) csa->best_z[j] = csa->z[j]; alpar@9: csa->best_obj = csa->obj; alpar@9: } alpar@9: else alpar@9: csa->phi_min[csa->iter] = csa->phi_min[csa->iter-1]; alpar@9: /* display information at the current point */ alpar@9: if (csa->parm->msg_lev >= GLP_MSG_ON) alpar@9: xprintf("%3d: obj = %17.9e; rpi = %8.1e; rdi = %8.1e; gap =" alpar@9: " %8.1e\n", csa->iter, csa->obj, csa->rpi, csa->rdi, alpar@9: csa->gap); alpar@9: /* check if the current point is optimal */ alpar@9: if (csa->rpi < 1e-8 && csa->rdi < 1e-8 && csa->gap < 1e-8) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("OPTIMAL SOLUTION FOUND\n"); alpar@9: status = 0; alpar@9: break; alpar@9: } alpar@9: /* check if the problem has no feasible solution */ alpar@9: temp = 1e5 * csa->phi_min[csa->iter]; alpar@9: if (temp < 1e-8) temp = 1e-8; alpar@9: if (csa->phi >= temp) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("PROBLEM HAS NO FEASIBLE PRIMAL/DUAL SOLUTION\n") alpar@9: ; alpar@9: status = 1; alpar@9: break; alpar@9: } alpar@9: /* check for very slow convergence or divergence */ alpar@9: if (((csa->rpi >= 1e-8 || csa->rdi >= 1e-8) && csa->rmu / alpar@9: csa->rmu0 >= 1e6) || alpar@9: (csa->iter >= 30 && csa->phi_min[csa->iter] >= 0.5 * alpar@9: csa->phi_min[csa->iter - 30])) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("NO CONVERGENCE; SEARCH TERMINATED\n"); alpar@9: status = 2; alpar@9: break; alpar@9: } alpar@9: /* check for maximal number of iterations */ alpar@9: if (csa->iter == ITER_MAX) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("ITERATION LIMIT EXCEEDED; SEARCH TERMINATED\n"); alpar@9: status = 3; alpar@9: break; alpar@9: } alpar@9: /* start the next iteration */ alpar@9: csa->iter++; alpar@9: /* factorize normal equation system */ alpar@9: for (j = 1; j <= n; j++) csa->D[j] = csa->x[j] / csa->z[j]; alpar@9: decomp_NE(csa); alpar@9: /* compute the next point using Mehrotra's predictor-corrector alpar@9: technique */ alpar@9: if (make_step(csa)) alpar@9: { if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("NUMERIC INSTABILITY; SEARCH TERMINATED\n"); alpar@9: status = 4; alpar@9: break; alpar@9: } alpar@9: } alpar@9: /* restore the best point */ alpar@9: if (status != 0) alpar@9: { for (j = 1; j <= n; j++) csa->x[j] = csa->best_x[j]; alpar@9: for (i = 1; i <= m; i++) csa->y[i] = csa->best_y[i]; alpar@9: for (j = 1; j <= n; j++) csa->z[j] = csa->best_z[j]; alpar@9: if (csa->parm->msg_lev >= GLP_MSG_ALL) alpar@9: xprintf("Best point %17.9e was reached on iteration %d\n", alpar@9: csa->best_obj, csa->best_iter); alpar@9: } alpar@9: /* return to the calling program */ alpar@9: return status; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * ipm_solve - core LP solver based on the interior-point method alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * #include "glpipm.h" alpar@9: * int ipm_solve(glp_prob *P, const glp_iptcp *parm); alpar@9: * alpar@9: * DESCRIPTION alpar@9: * alpar@9: * The routine ipm_solve is a core LP solver based on the primal-dual alpar@9: * interior-point method. alpar@9: * alpar@9: * The routine assumes the following standard formulation of LP problem alpar@9: * to be solved: alpar@9: * alpar@9: * minimize alpar@9: * alpar@9: * F = c[0] + c[1]*x[1] + c[2]*x[2] + ... + c[n]*x[n] alpar@9: * alpar@9: * subject to linear constraints alpar@9: * alpar@9: * a[1,1]*x[1] + a[1,2]*x[2] + ... + a[1,n]*x[n] = b[1] alpar@9: * alpar@9: * a[2,1]*x[1] + a[2,2]*x[2] + ... + a[2,n]*x[n] = b[2] alpar@9: * alpar@9: * . . . . . . alpar@9: * alpar@9: * a[m,1]*x[1] + a[m,2]*x[2] + ... + a[m,n]*x[n] = b[m] alpar@9: * alpar@9: * and non-negative variables alpar@9: * alpar@9: * x[1] >= 0, x[2] >= 0, ..., x[n] >= 0 alpar@9: * alpar@9: * where: alpar@9: * F is the objective function; alpar@9: * x[1], ..., x[n] are (structural) variables; alpar@9: * c[0] is a constant term of the objective function; alpar@9: * c[1], ..., c[n] are objective coefficients; alpar@9: * a[1,1], ..., a[m,n] are constraint coefficients; alpar@9: * b[1], ..., b[n] are right-hand sides. alpar@9: * alpar@9: * The solution is three vectors x, y, and z, which are stored by the alpar@9: * routine in the arrays x, y, and z, respectively. These vectors alpar@9: * correspond to the best primal-dual point found during optimization. alpar@9: * They are approximate solution of the following system (which is the alpar@9: * Karush-Kuhn-Tucker optimality conditions): alpar@9: * alpar@9: * A*x = b (primal feasibility condition) alpar@9: * alpar@9: * A'*y + z = c (dual feasibility condition) alpar@9: * alpar@9: * x'*z = 0 (primal-dual complementarity condition) alpar@9: * alpar@9: * x >= 0, z >= 0 (non-negativity condition) alpar@9: * alpar@9: * where: alpar@9: * x[1], ..., x[n] are primal (structural) variables; alpar@9: * y[1], ..., y[m] are dual variables (Lagrange multipliers) for alpar@9: * equality constraints; alpar@9: * z[1], ..., z[n] are dual variables (Lagrange multipliers) for alpar@9: * non-negativity constraints. alpar@9: * alpar@9: * RETURNS alpar@9: * alpar@9: * 0 LP has been successfully solved. alpar@9: * alpar@9: * GLP_ENOCVG alpar@9: * No convergence. alpar@9: * alpar@9: * GLP_EITLIM alpar@9: * Iteration limit exceeded. alpar@9: * alpar@9: * GLP_EINSTAB alpar@9: * Numeric instability on solving Newtonian system. alpar@9: * alpar@9: * In case of non-zero return code the routine returns the best point, alpar@9: * which has been reached during optimization. */ alpar@9: alpar@9: int ipm_solve(glp_prob *P, const glp_iptcp *parm) alpar@9: { struct csa _dsa, *csa = &_dsa; alpar@9: int m = P->m; alpar@9: int n = P->n; alpar@9: int nnz = P->nnz; alpar@9: GLPROW *row; alpar@9: GLPCOL *col; alpar@9: GLPAIJ *aij; alpar@9: int i, j, loc, ret, *A_ind, *A_ptr; alpar@9: double dir, *A_val, *b, *c, *x, *y, *z; alpar@9: xassert(m > 0); alpar@9: xassert(n > 0); alpar@9: /* allocate working arrays */ alpar@9: A_ptr = xcalloc(1+m+1, sizeof(int)); alpar@9: A_ind = xcalloc(1+nnz, sizeof(int)); alpar@9: A_val = xcalloc(1+nnz, sizeof(double)); alpar@9: b = xcalloc(1+m, sizeof(double)); alpar@9: c = xcalloc(1+n, sizeof(double)); alpar@9: x = xcalloc(1+n, sizeof(double)); alpar@9: y = xcalloc(1+m, sizeof(double)); alpar@9: z = xcalloc(1+n, sizeof(double)); alpar@9: /* prepare rows and constraint coefficients */ alpar@9: loc = 1; alpar@9: for (i = 1; i <= m; i++) alpar@9: { row = P->row[i]; alpar@9: xassert(row->type == GLP_FX); alpar@9: b[i] = row->lb * row->rii; alpar@9: A_ptr[i] = loc; alpar@9: for (aij = row->ptr; aij != NULL; aij = aij->r_next) alpar@9: { A_ind[loc] = aij->col->j; alpar@9: A_val[loc] = row->rii * aij->val * aij->col->sjj; alpar@9: loc++; alpar@9: } alpar@9: } alpar@9: A_ptr[m+1] = loc; alpar@9: xassert(loc-1 == nnz); alpar@9: /* prepare columns and objective coefficients */ alpar@9: if (P->dir == GLP_MIN) alpar@9: dir = +1.0; alpar@9: else if (P->dir == GLP_MAX) alpar@9: dir = -1.0; alpar@9: else alpar@9: xassert(P != P); alpar@9: c[0] = dir * P->c0; alpar@9: for (j = 1; j <= n; j++) alpar@9: { col = P->col[j]; alpar@9: xassert(col->type == GLP_LO && col->lb == 0.0); alpar@9: c[j] = dir * col->coef * col->sjj; alpar@9: } alpar@9: /* allocate and initialize the common storage area */ alpar@9: csa->m = m; alpar@9: csa->n = n; alpar@9: csa->A_ptr = A_ptr; alpar@9: csa->A_ind = A_ind; alpar@9: csa->A_val = A_val; alpar@9: csa->b = b; alpar@9: csa->c = c; alpar@9: csa->x = x; alpar@9: csa->y = y; alpar@9: csa->z = z; alpar@9: csa->parm = parm; alpar@9: initialize(csa); alpar@9: /* solve LP with the interior-point method */ alpar@9: ret = ipm_main(csa); alpar@9: /* deallocate the common storage area */ alpar@9: terminate(csa); alpar@9: /* determine solution status */ alpar@9: if (ret == 0) alpar@9: { /* optimal solution found */ alpar@9: P->ipt_stat = GLP_OPT; alpar@9: ret = 0; alpar@9: } alpar@9: else if (ret == 1) alpar@9: { /* problem has no feasible (primal or dual) solution */ alpar@9: P->ipt_stat = GLP_NOFEAS; alpar@9: ret = 0; alpar@9: } alpar@9: else if (ret == 2) alpar@9: { /* no convergence */ alpar@9: P->ipt_stat = GLP_INFEAS; alpar@9: ret = GLP_ENOCVG; alpar@9: } alpar@9: else if (ret == 3) alpar@9: { /* iteration limit exceeded */ alpar@9: P->ipt_stat = GLP_INFEAS; alpar@9: ret = GLP_EITLIM; alpar@9: } alpar@9: else if (ret == 4) alpar@9: { /* numeric instability on solving Newtonian system */ alpar@9: P->ipt_stat = GLP_INFEAS; alpar@9: ret = GLP_EINSTAB; alpar@9: } alpar@9: else alpar@9: xassert(ret != ret); alpar@9: /* store row solution components */ alpar@9: for (i = 1; i <= m; i++) alpar@9: { row = P->row[i]; alpar@9: row->pval = row->lb; alpar@9: row->dval = dir * y[i] * row->rii; alpar@9: } alpar@9: /* store column solution components */ alpar@9: P->ipt_obj = P->c0; alpar@9: for (j = 1; j <= n; j++) alpar@9: { col = P->col[j]; alpar@9: col->pval = x[j] * col->sjj; alpar@9: col->dval = dir * z[j] / col->sjj; alpar@9: P->ipt_obj += col->coef * col->pval; alpar@9: } alpar@9: /* free working arrays */ alpar@9: xfree(A_ptr); alpar@9: xfree(A_ind); alpar@9: xfree(A_val); alpar@9: xfree(b); alpar@9: xfree(c); alpar@9: xfree(x); alpar@9: xfree(y); alpar@9: xfree(z); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /* eof */