open source driving agent
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2152 lines
48 KiB

/*
* This file is part of qpOASES.
*
* qpOASES -- An Implementation of the Online Active Set Strategy.
* Copyright (C) 2007-2008 by Hans Joachim Ferreau et al. All rights reserved.
*
* qpOASES is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* qpOASES is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with qpOASES; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/**
* \file SRC/QProblemB.cpp
* \author Hans Joachim Ferreau
* \version 1.3embedded
* \date 2007-2008
*
* Implementation of the QProblemB class which is able to use the newly
* developed online active set strategy for parametric quadratic programming.
*/
#include <QProblemB.hpp>
#include <stdio.h>
void printmatrix(char *name, double *A, int m, int n) {
int i, j;
printf("%s = [...\n", name);
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++)
printf(" % 9.4f", A[i*n+j]);
printf(",\n");
}
printf("];\n");
}
/*****************************************************************************
* P U B L I C *
*****************************************************************************/
/*
* Q P r o b l e m B
*/
QProblemB::QProblemB( )
{
/* reset global message handler */
getGlobalMessageHandler( )->reset( );
hasHessian = BT_FALSE;
bounds.init( 0 );
hasCholesky = BT_FALSE;
tau = 0.0;
hessianType = HST_POSDEF_NULLSPACE; /* Hessian is assumed to be positive definite by default */
infeasible = BT_FALSE;
unbounded = BT_FALSE;
status = QPS_NOTINITIALISED;
#ifdef PC_DEBUG
printlevel = PL_MEDIUM;
setPrintLevel( PL_MEDIUM );
#else
printlevel = QPOASES_PRINTLEVEL;
#endif
count = 0;
}
/*
* Q P r o b l e m B
*/
QProblemB::QProblemB( int _nV )
{
/* consistency check */
if ( _nV <= 0 )
{
_nV = 1;
THROWERROR( RET_INVALID_ARGUMENTS );
}
hasHessian = BT_FALSE;
/* reset global message handler */
getGlobalMessageHandler( )->reset( );
bounds.init( _nV );
hasCholesky = BT_FALSE;
tau = 0.0;
hessianType = HST_POSDEF_NULLSPACE; /* Hessian is assumed to be positive definite by default */
infeasible = BT_FALSE;
unbounded = BT_FALSE;
status = QPS_NOTINITIALISED;
#ifdef PC_DEBUG
printlevel = PL_MEDIUM;
setPrintLevel( PL_MEDIUM );
#else
printlevel = QPOASES_PRINTLEVEL;
#endif
count = 0;
}
/*
* Q P r o b l e m B
*/
QProblemB::QProblemB( const QProblemB& rhs )
{
int i, j;
int _nV = rhs.bounds.getNV( );
for( i=0; i<_nV; ++i )
for( j=0; j<_nV; ++j )
H[i*NVMAX + j] = rhs.H[i*NVMAX + j];
hasHessian = rhs.hasHessian;
for( i=0; i<_nV; ++i )
g[i] = rhs.g[i];
for( i=0; i<_nV; ++i )
lb[i] = rhs.lb[i];
for( i=0; i<_nV; ++i )
ub[i] = rhs.ub[i];
bounds = rhs.bounds;
for( i=0; i<_nV; ++i )
for( j=0; j<_nV; ++j )
R[i*NVMAX + j] = rhs.R[i*NVMAX + j];
hasCholesky = rhs.hasCholesky;
for( i=0; i<_nV; ++i )
x[i] = rhs.x[i];
for( i=0; i<_nV; ++i )
y[i] = rhs.y[i];
tau = rhs.tau;
hessianType = rhs.hessianType;
infeasible = rhs.infeasible;
unbounded = rhs.unbounded;
status = rhs.status;
printlevel = rhs.printlevel;
count = rhs.count;
}
/*
* ~ Q P r o b l e m B
*/
QProblemB::~QProblemB( )
{
}
/*
* o p e r a t o r =
*/
QProblemB& QProblemB::operator=( const QProblemB& rhs )
{
int i, j;
if ( this != &rhs )
{
int _nV = rhs.bounds.getNV( );
for( i=0; i<_nV; ++i )
for( j=0; j<_nV; ++j )
H[i*NVMAX + j] = rhs.H[i*NVMAX + j];
hasHessian = rhs.hasHessian;
for( i=0; i<_nV; ++i )
g[i] = rhs.g[i];
for( i=0; i<_nV; ++i )
lb[i] = rhs.lb[i];
for( i=0; i<_nV; ++i )
ub[i] = rhs.ub[i];
bounds = rhs.bounds;
for( i=0; i<_nV; ++i )
for( j=0; j<_nV; ++j )
R[i*NVMAX + j] = rhs.R[i*NVMAX + j];
hasCholesky = rhs.hasCholesky;
for( i=0; i<_nV; ++i )
x[i] = rhs.x[i];
for( i=0; i<_nV; ++i )
y[i] = rhs.y[i];
tau = rhs.tau;
hessianType = rhs.hessianType;
infeasible = rhs.infeasible;
unbounded = rhs.unbounded;
status = rhs.status;
printlevel = rhs.printlevel;
setPrintLevel( rhs.printlevel );
count = rhs.count;
}
return *this;
}
/*
* r e s e t
*/
returnValue QProblemB::reset( )
{
int i, j;
int nV = getNV( );
/** 0) Reset has Hessian flag. */
hasHessian = BT_FALSE;
/* 1) Reset bounds. */
bounds.init( nV );
/* 2) Reset Cholesky decomposition. */
for( i=0; i<nV; ++i )
for( j=0; j<nV; ++j )
R[i*NVMAX + j] = 0.0;
hasCholesky = BT_FALSE;
/* 3) Reset steplength and status flags. */
tau = 0.0;
hessianType = HST_POSDEF_NULLSPACE; /* Hessian is assumed to be positive definite by default */
infeasible = BT_FALSE;
unbounded = BT_FALSE;
status = QPS_NOTINITIALISED;
return SUCCESSFUL_RETURN;
}
/*
* i n i t
*/
returnValue QProblemB::init( const real_t* const _H, const real_t* const _g,
const real_t* const _lb, const real_t* const _ub,
int& nWSR, const real_t* const yOpt, real_t* const cputime
)
{
/* 1) Setup QP data. */
if (setupQPdata(_H, 0, _g, _lb, _ub) != SUCCESSFUL_RETURN)
return THROWERROR( RET_INVALID_ARGUMENTS );
/* 2) Call to main initialisation routine (without any additional information). */
return solveInitialQP(0, yOpt, 0, nWSR, cputime);
}
returnValue QProblemB::init( const real_t* const _H, const real_t* const _R, const real_t* const _g,
const real_t* const _lb, const real_t* const _ub,
int& nWSR, const real_t* const yOpt, real_t* const cputime
)
{
/* 1) Setup QP data. */
if (setupQPdata(_H, _R, _g, _lb, _ub) != SUCCESSFUL_RETURN)
return THROWERROR( RET_INVALID_ARGUMENTS );
/* 2) Call to main initialisation routine (without any additional information). */
return solveInitialQP(0, yOpt, 0, nWSR, cputime);
}
/*
* h o t s t a r t
*/
returnValue QProblemB::hotstart( const real_t* const g_new, const real_t* const lb_new, const real_t* const ub_new,
int& nWSR, real_t* const cputime
)
{
int l;
/* consistency check */
if ( ( getStatus( ) == QPS_NOTINITIALISED ) ||
( getStatus( ) == QPS_PREPARINGAUXILIARYQP ) ||
( getStatus( ) == QPS_PERFORMINGHOMOTOPY ) )
{
return THROWERROR( RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED );
}
/* start runtime measurement */
real_t starttime = 0.0;
if ( cputime != 0 )
starttime = getCPUtime( );
/* I) PREPARATIONS */
/* 1) Reset status flags and increase QP counter. */
infeasible = BT_FALSE;
unbounded = BT_FALSE;
++count;
/* 2) Allocate delta vectors of gradient and bounds. */
returnValue returnvalue;
BooleanType Delta_bB_isZero;
int FR_idx[NVMAX];
int FX_idx[NVMAX];
real_t delta_g[NVMAX];
real_t delta_lb[NVMAX];
real_t delta_ub[NVMAX];
real_t delta_xFR[NVMAX];
real_t delta_xFX[NVMAX];
real_t delta_yFX[NVMAX];
int BC_idx;
SubjectToStatus BC_status;
#ifdef PC_DEBUG
char messageString[80];
#endif
/* II) MAIN HOMOTOPY LOOP */
for( l=0; l<nWSR; ++l )
{
status = QPS_PERFORMINGHOMOTOPY;
if ( printlevel == PL_HIGH )
{
#ifdef PC_DEBUG
sprintf( messageString,"%d ...",l );
getGlobalMessageHandler( )->throwInfo( RET_ITERATION_STARTED,messageString,__FUNCTION__,__FILE__,__LINE__,VS_VISIBLE );
#endif
}
/* 1) Setup index arrays. */
if ( bounds.getFree( )->getNumberArray( FR_idx ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_HOTSTART_FAILED );
if ( bounds.getFixed( )->getNumberArray( FX_idx ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_HOTSTART_FAILED );
/* 2) Initialize shift direction of the gradient and the bounds. */
returnvalue = hotstart_determineDataShift( FX_idx,
g_new,lb_new,ub_new,
delta_g,delta_lb,delta_ub,
Delta_bB_isZero
);
if ( returnvalue != SUCCESSFUL_RETURN )
{
nWSR = l;
THROWERROR( RET_SHIFT_DETERMINATION_FAILED );
return returnvalue;
}
/* 3) Determination of step direction of X and Y. */
returnvalue = hotstart_determineStepDirection( FR_idx,FX_idx,
delta_g,delta_lb,delta_ub,
Delta_bB_isZero,
delta_xFX,delta_xFR,delta_yFX
);
if ( returnvalue != SUCCESSFUL_RETURN )
{
nWSR = l;
THROWERROR( RET_STEPDIRECTION_DETERMINATION_FAILED );
return returnvalue;
}
/* 4) Determination of step length TAU. */
returnvalue = hotstart_determineStepLength( FR_idx,FX_idx,
delta_lb,delta_ub,
delta_xFR,delta_yFX,
BC_idx,BC_status );
if ( returnvalue != SUCCESSFUL_RETURN )
{
nWSR = l;
THROWERROR( RET_STEPLENGTH_DETERMINATION_FAILED );
return returnvalue;
}
/* 5) Realization of the homotopy step. */
returnvalue = hotstart_performStep( FR_idx,FX_idx,
delta_g,delta_lb,delta_ub,
delta_xFX,delta_xFR,delta_yFX,
BC_idx,BC_status
);
if ( returnvalue != SUCCESSFUL_RETURN )
{
nWSR = l;
/* stop runtime measurement */
if ( cputime != 0 )
*cputime = getCPUtime( ) - starttime;
/* optimal solution found? */
if ( returnvalue == RET_OPTIMAL_SOLUTION_FOUND )
{
status = QPS_SOLVED;
if ( printlevel == PL_HIGH )
THROWINFO( RET_OPTIMAL_SOLUTION_FOUND );
#ifdef PC_DEBUG
if ( printIteration( l,BC_idx,BC_status ) != SUCCESSFUL_RETURN )
THROWERROR( RET_PRINT_ITERATION_FAILED ); /* do not pass this as return value! */
#endif
/* check KKT optimality conditions */
return checkKKTconditions( );
}
else
{
/* checks for infeasibility... */
if ( infeasible == BT_TRUE )
{
status = QPS_HOMOTOPYQPSOLVED;
return THROWERROR( RET_HOTSTART_STOPPED_INFEASIBILITY );
}
/* ...unboundedness... */
if ( unbounded == BT_TRUE ) /* not necessary since objective function convex! */
return THROWERROR( RET_HOTSTART_STOPPED_UNBOUNDEDNESS );
/* ... and throw unspecific error otherwise */
THROWERROR( RET_HOMOTOPY_STEP_FAILED );
return returnvalue;
}
}
/* 6) Output information of successful QP iteration. */
status = QPS_HOMOTOPYQPSOLVED;
#ifdef PC_DEBUG
if ( printIteration( l,BC_idx,BC_status ) != SUCCESSFUL_RETURN )
THROWERROR( RET_PRINT_ITERATION_FAILED ); /* do not pass this as return value! */
#endif
}
/* stop runtime measurement */
if ( cputime != 0 )
*cputime = getCPUtime( ) - starttime;
/* if programm gets to here, output information that QP could not be solved
* within the given maximum numbers of working set changes */
if ( printlevel == PL_HIGH )
{
#ifdef PC_DEBUG
sprintf( messageString,"(nWSR = %d)",nWSR );
return getGlobalMessageHandler( )->throwWarning( RET_MAX_NWSR_REACHED,messageString,__FUNCTION__,__FILE__,__LINE__,VS_VISIBLE );
#endif
}
/* Finally check KKT optimality conditions. */
returnValue returnvalueKKTcheck = checkKKTconditions( );
if ( returnvalueKKTcheck != SUCCESSFUL_RETURN )
return returnvalueKKTcheck;
else
return RET_MAX_NWSR_REACHED;
}
/*
* g e t N Z
*/
int QProblemB::getNZ( )
{
/* if no constraints are present: nZ=nFR */
return bounds.getFree( )->getLength( );
}
/*
* g e t O b j V a l
*/
real_t QProblemB::getObjVal( ) const
{
real_t objVal;
/* calculated optimal objective function value
* only if current QP has been solved */
if ( ( getStatus( ) == QPS_AUXILIARYQPSOLVED ) ||
( getStatus( ) == QPS_HOMOTOPYQPSOLVED ) ||
( getStatus( ) == QPS_SOLVED ) )
{
objVal = getObjVal( x );
}
else
{
objVal = INFTY;
}
return objVal;
}
/*
* g e t O b j V a l
*/
real_t QProblemB::getObjVal( const real_t* const _x ) const
{
int i, j;
int nV = getNV( );
real_t obj_tmp = 0.0;
for( i=0; i<nV; ++i )
{
obj_tmp += _x[i]*g[i];
for( j=0; j<nV; ++j )
obj_tmp += 0.5*_x[i]*H[i*NVMAX + j]*_x[j];
}
return obj_tmp;
}
/*
* g e t P r i m a l S o l u t i o n
*/
returnValue QProblemB::getPrimalSolution( real_t* const xOpt ) const
{
int i;
/* return optimal primal solution vector
* only if current QP has been solved */
if ( ( getStatus( ) == QPS_AUXILIARYQPSOLVED ) ||
( getStatus( ) == QPS_HOMOTOPYQPSOLVED ) ||
( getStatus( ) == QPS_SOLVED ) )
{
for( i=0; i<getNV( ); ++i )
xOpt[i] = x[i];
return SUCCESSFUL_RETURN;
}
else
{
return RET_QP_NOT_SOLVED;
}
}
/*
* g e t D u a l S o l u t i o n
*/
returnValue QProblemB::getDualSolution( real_t* const yOpt ) const
{
int i;
/* return optimal dual solution vector
* only if current QP has been solved */
if ( ( getStatus( ) == QPS_AUXILIARYQPSOLVED ) ||
( getStatus( ) == QPS_HOMOTOPYQPSOLVED ) ||
( getStatus( ) == QPS_SOLVED ) )
{
for( i=0; i<getNV( ); ++i )
yOpt[i] = y[i];
return SUCCESSFUL_RETURN;
}
else
{
return RET_QP_NOT_SOLVED;
}
}
/*
* s e t P r i n t L e v e l
*/
returnValue QProblemB::setPrintLevel( PrintLevel _printlevel )
{
#ifndef __MATLAB__
if ( ( printlevel >= PL_MEDIUM ) && ( printlevel != _printlevel ) )
THROWINFO( RET_PRINTLEVEL_CHANGED );
#endif
printlevel = _printlevel;
/* update message handler preferences */
switch ( printlevel )
{
case PL_NONE:
getGlobalMessageHandler( )->setErrorVisibilityStatus( VS_HIDDEN );
getGlobalMessageHandler( )->setWarningVisibilityStatus( VS_HIDDEN );
getGlobalMessageHandler( )->setInfoVisibilityStatus( VS_HIDDEN );
break;
case PL_LOW:
getGlobalMessageHandler( )->setErrorVisibilityStatus( VS_VISIBLE );
getGlobalMessageHandler( )->setWarningVisibilityStatus( VS_HIDDEN );
getGlobalMessageHandler( )->setInfoVisibilityStatus( VS_HIDDEN );
break;
default: /* PL_MEDIUM, PL_HIGH */
getGlobalMessageHandler( )->setErrorVisibilityStatus( VS_VISIBLE );
getGlobalMessageHandler( )->setWarningVisibilityStatus( VS_VISIBLE );
getGlobalMessageHandler( )->setInfoVisibilityStatus( VS_VISIBLE );
break;
}
return SUCCESSFUL_RETURN;
}
/*****************************************************************************
* P R O T E C T E D *
*****************************************************************************/
/*
* c h e c k F o r I d e n t i t y H e s s i a n
*/
returnValue QProblemB::checkForIdentityHessian( )
{
int i, j;
int nV = getNV( );
/* nothing to do as status flag remains unaltered
* if Hessian differs from identity matrix */
if ( hessianType == HST_IDENTITY )
return SUCCESSFUL_RETURN;
/* 1) If Hessian differs from identity matrix,
* return without changing the internal HessianType. */
for ( i=0; i<nV; ++i )
if ( getAbs( H[i*NVMAX + i] - 1.0 ) > EPS )
return SUCCESSFUL_RETURN;
for ( i=0; i<nV; ++i )
{
for ( j=0; j<i; ++j )
if ( ( getAbs( H[i*NVMAX + j] ) > EPS ) || ( getAbs( H[j*NVMAX + i] ) > EPS ) )
return SUCCESSFUL_RETURN;
}
/* 2) If this point is reached, Hessian equals the idetity matrix. */
hessianType = HST_IDENTITY;
return SUCCESSFUL_RETURN;
}
/*
* s e t u p S u b j e c t T o T y p e
*/
returnValue QProblemB::setupSubjectToType( )
{
int i;
int nV = getNV( );
/* 1) Check if lower bounds are present. */
bounds.setNoLower( BT_TRUE );
for( i=0; i<nV; ++i )
if ( lb[i] > -INFTY )
{
bounds.setNoLower( BT_FALSE );
break;
}
/* 2) Check if upper bounds are present. */
bounds.setNoUpper( BT_TRUE );
for( i=0; i<nV; ++i )
if ( ub[i] < INFTY )
{
bounds.setNoUpper( BT_FALSE );
break;
}
/* 3) Determine implicitly fixed and unbounded variables. */
int nFV = 0;
int nUV = 0;
for( i=0; i<nV; ++i )
if ( ( lb[i] < -INFTY + BOUNDTOL ) && ( ub[i] > INFTY - BOUNDTOL ) )
{
bounds.setType( i,ST_UNBOUNDED );
++nUV;
}
else
{
if ( lb[i] > ub[i] - BOUNDTOL )
{
bounds.setType( i,ST_EQUALITY );
++nFV;
}
else
{
bounds.setType( i,ST_BOUNDED );
}
}
/* 4) Set dimensions of bounds structure. */
bounds.setNFV( nFV );
bounds.setNUV( nUV );
bounds.setNBV( nV - nFV - nUV );
return SUCCESSFUL_RETURN;
}
/*
* c h o l e s k y D e c o m p o s i t i o n
*/
returnValue QProblemB::setupCholeskyDecomposition( )
{
int i, j, k, ii, jj;
int nV = getNV( );
int nFR = getNFR( );
/* If Hessian flag is false, it means that H & R already contain Cholesky
* factorization -- provided from outside. */
if (hasHessian == BT_FALSE)
return SUCCESSFUL_RETURN;
/* 1) Initialises R with all zeros. */
for( i=0; i<nV; ++i )
for( j=0; j<nV; ++j )
R[i*NVMAX + j] = 0.0;
/* 2) Calculate Cholesky decomposition of H (projected to free variables). */
if ( hessianType == HST_IDENTITY )
{
/* if Hessian is identity, so is its Cholesky factor. */
for( i=0; i<nFR; ++i )
R[i*NVMAX + i] = 1.0;
}
else
{
if ( nFR > 0 )
{
int FR_idx[NVMAX];
if ( bounds.getFree( )->getNumberArray( FR_idx ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INDEXLIST_CORRUPTED );
/* R'*R = H */
real_t sum;
real_t inv;
for( i=0; i<nFR; ++i )
{
/* j == i */
ii = FR_idx[i];
sum = H[ii*NVMAX + ii];
for( k=(i-1); k>=0; --k )
sum -= R[k*NVMAX + i] * R[k*NVMAX + i];
if ( sum > 0.0 )
{
R[i*NVMAX + i] = sqrt( sum );
inv = 1.0 / R[i*NVMAX + i];
}
else
{
hessianType = HST_SEMIDEF;
return THROWERROR( RET_HESSIAN_NOT_SPD );
}
/* j > i */
for( j=(i+1); j<nFR; ++j )
{
jj = FR_idx[j];
sum = H[jj*NVMAX + ii];
for( k=(i-1); k>=0; --k )
sum -= R[k*NVMAX + i] * R[k*NVMAX + j];
R[i*NVMAX + j] = sum * inv;
}
}
}
}
return SUCCESSFUL_RETURN;
}
/*
* s o l v e I n i t i a l Q P
*/
returnValue QProblemB::solveInitialQP( const real_t* const xOpt, const real_t* const yOpt,
const Bounds* const guessedBounds,
int& nWSR, real_t* const cputime
)
{
int i, nFR;
int nV = getNV( );
/* start runtime measurement */
real_t starttime = 0.0;
if ( cputime != 0 )
starttime = getCPUtime( );
status = QPS_NOTINITIALISED;
/* I) ANALYSE QP DATA: */
/* 1) Check if Hessian happens to be the identity matrix. */
if ( checkForIdentityHessian( ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INIT_FAILED );
/* 2) Setup type of bounds (i.e. unbounded, implicitly fixed etc.). */
if ( setupSubjectToType( ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INIT_FAILED );
status = QPS_PREPARINGAUXILIARYQP;
/* II) SETUP AUXILIARY QP WITH GIVEN OPTIMAL SOLUTION: */
/* 1) Setup bounds data structure. */
if ( bounds.setupAllFree( ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INIT_FAILED );
/* 2) Setup optimal primal/dual solution for auxiliary QP. */
if ( setupAuxiliaryQPsolution( xOpt,yOpt ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INIT_FAILED );
/* 3) Obtain linear independent working set for auxiliary QP. */
static Bounds auxiliaryBounds;
auxiliaryBounds.init( nV );
if ( obtainAuxiliaryWorkingSet( xOpt,yOpt,guessedBounds, &auxiliaryBounds ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INIT_FAILED );
/* 4) Setup working set of auxiliary QP and setup cholesky decomposition. */
if ( setupAuxiliaryWorkingSet( &auxiliaryBounds,BT_TRUE ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INIT_FAILED );
nFR = getNFR();
/* At the moment we can only provide a Cholesky of the Hessian if
* the solver is cold-started. */
if (hasCholesky == BT_FALSE || nFR != nV)
if (setupCholeskyDecomposition() != SUCCESSFUL_RETURN)
return THROWERROR( RET_INIT_FAILED_CHOLESKY );
/* 5) Store original QP formulation... */
real_t g_original[NVMAX];
real_t lb_original[NVMAX];
real_t ub_original[NVMAX];
for( i=0; i<nV; ++i )
g_original[i] = g[i];
for( i=0; i<nV; ++i )
lb_original[i] = lb[i];
for( i=0; i<nV; ++i )
ub_original[i] = ub[i];
/* ... and setup QP data of an auxiliary QP having an optimal solution
* as specified by the user (or xOpt = yOpt = 0, by default). */
if ( setupAuxiliaryQPgradient( ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INIT_FAILED );
if ( setupAuxiliaryQPbounds( BT_TRUE ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_INIT_FAILED );
status = QPS_AUXILIARYQPSOLVED;
/* III) SOLVE ACTUAL INITIAL QP: */
/* Use hotstart method to find the solution of the original initial QP,... */
returnValue returnvalue = hotstart( g_original,lb_original,ub_original, nWSR,0 );
/* ... check for infeasibility and unboundedness... */
if ( isInfeasible( ) == BT_TRUE )
return THROWERROR( RET_INIT_FAILED_INFEASIBILITY );
if ( isUnbounded( ) == BT_TRUE )
return THROWERROR( RET_INIT_FAILED_UNBOUNDEDNESS );
/* ... and internal errors. */
if ( ( returnvalue != SUCCESSFUL_RETURN ) && ( returnvalue != RET_MAX_NWSR_REACHED ) &&
( returnvalue != RET_INACCURATE_SOLUTION ) && ( returnvalue != RET_NO_SOLUTION ) )
return THROWERROR( RET_INIT_FAILED_HOTSTART );
/* stop runtime measurement */
if ( cputime != 0 )
*cputime = getCPUtime( ) - starttime;
if ( printlevel == PL_HIGH )
THROWINFO( RET_INIT_SUCCESSFUL );
return returnvalue;
}
/*
* o b t a i n A u x i l i a r y W o r k i n g S e t
*/
returnValue QProblemB::obtainAuxiliaryWorkingSet( const real_t* const xOpt, const real_t* const yOpt,
const Bounds* const guessedBounds, Bounds* auxiliaryBounds
) const
{
int i = 0;
int nV = getNV( );
/* 1) Ensure that desiredBounds is allocated (and different from guessedBounds). */
if ( ( auxiliaryBounds == 0 ) || ( auxiliaryBounds == guessedBounds ) )
return THROWERROR( RET_INVALID_ARGUMENTS );
/* 2) Setup working set for auxiliary initial QP. */
if ( guessedBounds != 0 )
{
/* If an initial working set is specific, use it!
* Moreover, add all implictly fixed variables if specified. */
for( i=0; i<nV; ++i )
{
if ( bounds.getType( i ) == ST_EQUALITY )
{
if ( auxiliaryBounds->setupBound( i,ST_LOWER ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
}
else
{
if ( auxiliaryBounds->setupBound( i,guessedBounds->getStatus( i ) ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
}
}
}
else /* No initial working set specified. */
{
if ( ( xOpt != 0 ) && ( yOpt == 0 ) )
{
/* Obtain initial working set by "clipping". */
for( i=0; i<nV; ++i )
{
if ( xOpt[i] <= lb[i] + BOUNDTOL )
{
if ( auxiliaryBounds->setupBound( i,ST_LOWER ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
continue;
}
if ( xOpt[i] >= ub[i] - BOUNDTOL )
{
if ( auxiliaryBounds->setupBound( i,ST_UPPER ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
continue;
}
/* Moreover, add all implictly fixed variables if specified. */
if ( bounds.getType( i ) == ST_EQUALITY )
{
if ( auxiliaryBounds->setupBound( i,ST_LOWER ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
}
else
{
if ( auxiliaryBounds->setupBound( i,ST_INACTIVE ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
}
}
}
if ( ( xOpt == 0 ) && ( yOpt != 0 ) )
{
/* Obtain initial working set in accordance to sign of dual solution vector. */
for( i=0; i<nV; ++i )
{
if ( yOpt[i] > ZERO )
{
if ( auxiliaryBounds->setupBound( i,ST_LOWER ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
continue;
}
if ( yOpt[i] < -ZERO )
{
if ( auxiliaryBounds->setupBound( i,ST_UPPER ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
continue;
}
/* Moreover, add all implictly fixed variables if specified. */
if ( bounds.getType( i ) == ST_EQUALITY )
{
if ( auxiliaryBounds->setupBound( i,ST_LOWER ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
}
else
{
if ( auxiliaryBounds->setupBound( i,ST_INACTIVE ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
}
}
}
/* If xOpt and yOpt are null pointer and no initial working is specified,
* start with empty working set (or implicitly fixed bounds only)
* for auxiliary QP. */
if ( ( xOpt == 0 ) && ( yOpt == 0 ) )
{
for( i=0; i<nV; ++i )
{
/* Only add all implictly fixed variables if specified. */
if ( bounds.getType( i ) == ST_EQUALITY )
{
if ( auxiliaryBounds->setupBound( i,ST_LOWER ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
}
else
{
if ( auxiliaryBounds->setupBound( i,ST_INACTIVE ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_OBTAINING_WORKINGSET_FAILED );
}
}
}
}
return SUCCESSFUL_RETURN;
}
/*
* s e t u p A u x i l i a r y W o r k i n g S e t
*/
returnValue QProblemB::setupAuxiliaryWorkingSet( const Bounds* const auxiliaryBounds,
BooleanType setupAfresh
)
{
int i;
int nV = getNV( );
/* consistency checks */
if ( auxiliaryBounds != 0 )
{
for( i=0; i<nV; ++i )
if ( ( bounds.getStatus( i ) == ST_UNDEFINED ) || ( auxiliaryBounds->getStatus( i ) == ST_UNDEFINED ) )
return THROWERROR( RET_UNKNOWN_BUG );
}
else
{
return THROWERROR( RET_INVALID_ARGUMENTS );
}
/* I) SETUP CHOLESKY FLAG:
* Cholesky decomposition shall only be updated if working set
* shall be updated (i.e. NOT setup afresh!) */
BooleanType updateCholesky;
if ( setupAfresh == BT_TRUE )
updateCholesky = BT_FALSE;
else
updateCholesky = BT_TRUE;
/* II) REMOVE FORMERLY ACTIVE BOUNDS (IF NECESSARY): */
if ( setupAfresh == BT_FALSE )
{
/* Remove all active bounds that shall be inactive AND
* all active bounds that are active at the wrong bound. */
for( i=0; i<nV; ++i )
{
if ( ( bounds.getStatus( i ) == ST_LOWER ) && ( auxiliaryBounds->getStatus( i ) != ST_LOWER ) )
if ( removeBound( i,updateCholesky ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_SETUP_WORKINGSET_FAILED );
if ( ( bounds.getStatus( i ) == ST_UPPER ) && ( auxiliaryBounds->getStatus( i ) != ST_UPPER ) )
if ( removeBound( i,updateCholesky ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_SETUP_WORKINGSET_FAILED );
}
}
/* III) ADD NEWLY ACTIVE BOUNDS: */
/* Add all inactive bounds that shall be active AND
* all formerly active bounds that have been active at the wrong bound. */
for( i=0; i<nV; ++i )
{
if ( ( bounds.getStatus( i ) == ST_INACTIVE ) && ( auxiliaryBounds->getStatus( i ) != ST_INACTIVE ) )
{
if ( addBound( i,auxiliaryBounds->getStatus( i ),updateCholesky ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_SETUP_WORKINGSET_FAILED );
}
}
return SUCCESSFUL_RETURN;
}
/*
* s e t u p A u x i l i a r y Q P s o l u t i o n
*/
returnValue QProblemB::setupAuxiliaryQPsolution( const real_t* const xOpt, const real_t* const yOpt
)
{
int i;
int nV = getNV( );
/* Setup primal/dual solution vectors for auxiliary initial QP:
* if a null pointer is passed, a zero vector is assigned;
* old solution vector is kept if pointer to internal solution vector is passed. */
if ( xOpt != 0 )
{
if ( xOpt != x )
for( i=0; i<nV; ++i )
x[i] = xOpt[i];
}
else
{
for( i=0; i<nV; ++i )
x[i] = 0.0;
}
if ( yOpt != 0 )
{
if ( yOpt != y )
for( i=0; i<nV; ++i )
y[i] = yOpt[i];
}
else
{
for( i=0; i<nV; ++i )
y[i] = 0.0;
}
return SUCCESSFUL_RETURN;
}
/*
* s e t u p A u x i l i a r y Q P g r a d i e n t
*/
returnValue QProblemB::setupAuxiliaryQPgradient( )
{
int i, j;
int nV = getNV( );
/* Setup gradient vector: g = -H*x + y'*Id. */
for ( i=0; i<nV; ++i )
{
/* y'*Id */
g[i] = y[i];
/* -H*x */
for ( j=0; j<nV; ++j )
g[i] -= H[i*NVMAX + j] * x[j];
}
return SUCCESSFUL_RETURN;
}
/*
* s e t u p A u x i l i a r y Q P b o u n d s
*/
returnValue QProblemB::setupAuxiliaryQPbounds( BooleanType useRelaxation )
{
int i;
int nV = getNV( );
/* Setup bound vectors. */
for ( i=0; i<nV; ++i )
{
switch ( bounds.getStatus( i ) )
{
case ST_INACTIVE:
if ( useRelaxation == BT_TRUE )
{
if ( bounds.getType( i ) == ST_EQUALITY )
{
lb[i] = x[i];
ub[i] = x[i];
}
else
{
lb[i] = x[i] - BOUNDRELAXATION;
ub[i] = x[i] + BOUNDRELAXATION;
}
}
break;
case ST_LOWER:
lb[i] = x[i];
if ( bounds.getType( i ) == ST_EQUALITY )
{
ub[i] = x[i];
}
else
{
if ( useRelaxation == BT_TRUE )
ub[i] = x[i] + BOUNDRELAXATION;
}
break;
case ST_UPPER:
ub[i] = x[i];
if ( bounds.getType( i ) == ST_EQUALITY )
{
lb[i] = x[i];
}
else
{
if ( useRelaxation == BT_TRUE )
lb[i] = x[i] - BOUNDRELAXATION;
}
break;
default:
return THROWERROR( RET_UNKNOWN_BUG );
}
}
return SUCCESSFUL_RETURN;
}
/*
* a d d B o u n d
*/
returnValue QProblemB::addBound( int number, SubjectToStatus B_status,
BooleanType updateCholesky
)
{
int i, j;
int nFR = getNFR( );
/* consistency check */
if ( ( getStatus( ) == QPS_NOTINITIALISED ) ||
( getStatus( ) == QPS_AUXILIARYQPSOLVED ) ||
( getStatus( ) == QPS_HOMOTOPYQPSOLVED ) ||
( getStatus( ) == QPS_SOLVED ) )
{
return THROWERROR( RET_UNKNOWN_BUG );
}
/* Perform cholesky updates only if QProblemB has been initialised! */
if ( ( getStatus( ) == QPS_PREPARINGAUXILIARYQP ) || ( updateCholesky == BT_FALSE ) )
{
/* UPDATE INDICES */
if ( bounds.moveFreeToFixed( number,B_status ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_ADDBOUND_FAILED );
return SUCCESSFUL_RETURN;
}
/* I) PERFORM CHOLESKY UPDATE: */
/* 1) Index of variable to be added within the list of free variables. */
int number_idx = bounds.getFree( )->getIndex( number );
real_t c, s;
/* 2) Use row-wise Givens rotations to restore upper triangular form of R. */
for( i=number_idx+1; i<nFR; ++i )
{
computeGivens( R[(i-1)*NVMAX + i],R[i*NVMAX + i], R[(i-1)*NVMAX + i],R[i*NVMAX + i],c,s );
for( j=(1+i); j<nFR; ++j ) /* last column of R is thrown away */
applyGivens( c,s,R[(i-1)*NVMAX + j],R[i*NVMAX + j], R[(i-1)*NVMAX + j],R[i*NVMAX + j] );
}
/* 3) Delete <number_idx>th column and ... */
for( i=0; i<nFR-1; ++i )
for( j=number_idx+1; j<nFR; ++j )
R[i*NVMAX + j-1] = R[i*NVMAX + j];
/* ... last column of R. */
for( i=0; i<nFR; ++i )
R[i*NVMAX + nFR-1] = 0.0;
/* II) UPDATE INDICES */
if ( bounds.moveFreeToFixed( number,B_status ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_ADDBOUND_FAILED );
return SUCCESSFUL_RETURN;
}
returnValue QProblemB::removeBound( int number,
BooleanType updateCholesky
)
{
int i, ii;
int nFR = getNFR( );
/* consistency check */
if ( ( getStatus( ) == QPS_NOTINITIALISED ) ||
( getStatus( ) == QPS_AUXILIARYQPSOLVED ) ||
( getStatus( ) == QPS_HOMOTOPYQPSOLVED ) ||
( getStatus( ) == QPS_SOLVED ) )
{
return THROWERROR( RET_UNKNOWN_BUG );
}
/* I) UPDATE INDICES */
if ( bounds.moveFixedToFree( number ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_REMOVEBOUND_FAILED );
/* Perform cholesky updates only if QProblemB has been initialised! */
if ( ( getStatus( ) == QPS_PREPARINGAUXILIARYQP ) || ( updateCholesky == BT_FALSE ) )
return SUCCESSFUL_RETURN;
/* II) PERFORM CHOLESKY UPDATE */
int FR_idx[NVMAX];
if ( bounds.getFree( )->getNumberArray( FR_idx ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_REMOVEBOUND_FAILED );
/* 1) Calculate new column of cholesky decomposition. */
real_t rhs[NVMAX];
real_t r[NVMAX];
real_t r0 = H[number*NVMAX + number];
for( i=0; i<nFR; ++i )
{
ii = FR_idx[i];
rhs[i] = H[number*NVMAX + ii];
}
if ( backsolveR( rhs,BT_TRUE,BT_TRUE,r ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_REMOVEBOUND_FAILED );
for( i=0; i<nFR; ++i )
r0 -= r[i]*r[i];
/* 2) Store new column into R. */
for( i=0; i<nFR; ++i )
R[i*NVMAX + nFR] = r[i];
if ( r0 > 0.0 )
R[nFR*NVMAX + nFR] = sqrt( r0 );
else
{
hessianType = HST_SEMIDEF;
return THROWERROR( RET_HESSIAN_NOT_SPD );
}
return SUCCESSFUL_RETURN;
}
/*
* b a c k s o l v e R (CODE DUPLICATED IN QProblem CLASS!!!)
*/
returnValue QProblemB::backsolveR( const real_t* const b, BooleanType transposed,
real_t* const a
)
{
/* Call standard backsolve procedure (i.e. removingBound == BT_FALSE). */
return backsolveR( b,transposed,BT_FALSE,a );
}
/*
* b a c k s o l v e R (CODE DUPLICATED IN QProblem CLASS!!!)
*/
returnValue QProblemB::backsolveR( const real_t* const b, BooleanType transposed,
BooleanType removingBound,
real_t* const a
)
{
int i, j;
int nR = getNZ( );
real_t sum;
/* if backsolve is called while removing a bound, reduce nZ by one. */
if ( removingBound == BT_TRUE )
--nR;
/* nothing to do */
if ( nR <= 0 )
return SUCCESSFUL_RETURN;
/* Solve Ra = b, where R might be transposed. */
if ( transposed == BT_FALSE )
{
/* solve Ra = b */
for( i=(nR-1); i>=0; --i )
{
sum = b[i];
for( j=(i+1); j<nR; ++j )
sum -= R[i*NVMAX + j] * a[j];
if ( getAbs( R[i*NVMAX + i] ) > ZERO )
a[i] = sum / R[i*NVMAX + i];
else
return THROWERROR( RET_DIV_BY_ZERO );
}
}
else
{
/* solve R^T*a = b */
for( i=0; i<nR; ++i )
{
sum = b[i];
for( j=0; j<i; ++j )
sum -= R[j*NVMAX + i] * a[j];
if ( getAbs( R[i*NVMAX + i] ) > ZERO )
a[i] = sum / R[i*NVMAX + i];
else
return THROWERROR( RET_DIV_BY_ZERO );
}
}
return SUCCESSFUL_RETURN;
}
/*
* h o t s t a r t _ d e t e r m i n e D a t a S h i f t
*/
returnValue QProblemB::hotstart_determineDataShift( const int* const FX_idx,
const real_t* const g_new, const real_t* const lb_new, const real_t* const ub_new,
real_t* const delta_g, real_t* const delta_lb, real_t* const delta_ub,
BooleanType& Delta_bB_isZero
)
{
int i, ii;
int nV = getNV( );
int nFX = getNFX( );
/* 1) Calculate shift directions. */
for( i=0; i<nV; ++i )
delta_g[i] = g_new[i] - g[i];
if ( lb_new != 0 )
{
for( i=0; i<nV; ++i )
delta_lb[i] = lb_new[i] - lb[i];
}
else
{
/* if no lower bounds exist, assume the new lower bounds to be -infinity */
for( i=0; i<nV; ++i )
delta_lb[i] = -INFTY - lb[i];
}
if ( ub_new != 0 )
{
for( i=0; i<nV; ++i )
delta_ub[i] = ub_new[i] - ub[i];
}
else
{
/* if no upper bounds exist, assume the new upper bounds to be infinity */
for( i=0; i<nV; ++i )
delta_ub[i] = INFTY - ub[i];
}
/* 2) Determine if active bounds are to be shifted. */
Delta_bB_isZero = BT_TRUE;
for ( i=0; i<nFX; ++i )
{
ii = FX_idx[i];
if ( ( getAbs( delta_lb[ii] ) > EPS ) || ( getAbs( delta_ub[ii] ) > EPS ) )
{
Delta_bB_isZero = BT_FALSE;
break;
}
}
return SUCCESSFUL_RETURN;
}
/*
* a r e B o u n d s C o n s i s t e n t
*/
BooleanType QProblemB::areBoundsConsistent( const real_t* const delta_lb, const real_t* const delta_ub
) const
{
int i;
/* Check if delta_lb[i] is greater than delta_ub[i]
* for a component i whose bounds are already (numerically) equal. */
for( i=0; i<getNV( ); ++i )
if ( ( lb[i] > ub[i] - BOUNDTOL ) && ( delta_lb[i] > delta_ub[i] + EPS ) )
return BT_FALSE;
return BT_TRUE;
}
/*
* s e t u p Q P d a t a
*/
returnValue QProblemB::setupQPdata( const real_t* const _H, const real_t* const _R, const real_t* const _g,
const real_t* const _lb, const real_t* const _ub
)
{
int i, j;
int nV = getNV( );
/* 1) Setup Hessian matrix and it's Cholesky factorization. */
if (_H != 0)
{
for( i=0; i<nV; ++i )
for( j=0; j<nV; ++j )
H[i*NVMAX + j] = _H[i*nV + j];
hasHessian = BT_TRUE;
}
else
hasHessian = BT_FALSE;
if (_R != 0)
{
for( i=0; i<nV; ++i )
for( j=0; j<nV; ++j )
R[i*NVMAX + j] = _R[i*nV + j];
hasCholesky = BT_TRUE;
/* If Hessian is not provided, store it's factorization in H, and that guy
* is going to be used for H * x products (R^T * R * x in this case). */
if (hasHessian == BT_FALSE)
for( i=0; i<nV; ++i )
for( j=0; j<nV; ++j )
H[i*NVMAX + j] = _R[i*nV + j];
}
else
hasCholesky = BT_FALSE;
if (hasHessian == BT_FALSE && hasCholesky == BT_FALSE)
return THROWERROR( RET_INVALID_ARGUMENTS );
/* 2) Setup gradient vector. */
if ( _g == 0 )
return THROWERROR( RET_INVALID_ARGUMENTS );
for( i=0; i<nV; ++i )
g[i] = _g[i];
/* 3) Setup lower bounds vector. */
if ( _lb != 0 )
{
for( i=0; i<nV; ++i )
lb[i] = _lb[i];
}
else
{
/* if no lower bounds are specified, set them to -infinity */
for( i=0; i<nV; ++i )
lb[i] = -INFTY;
}
/* 4) Setup upper bounds vector. */
if ( _ub != 0 )
{
for( i=0; i<nV; ++i )
ub[i] = _ub[i];
}
else
{
/* if no upper bounds are specified, set them to infinity */
for( i=0; i<nV; ++i )
ub[i] = INFTY;
}
//printmatrix( "H",H,nV,nV );
//printmatrix( "R",R,nV,nV );
//printmatrix( "g",g,1,nV );
//printmatrix( "lb",lb,1,nV );
//printmatrix( "ub",ub,1,nV );
return SUCCESSFUL_RETURN;
}
/*****************************************************************************
* P R I V A T E *
*****************************************************************************/
/*
* h o t s t a r t _ d e t e r m i n e S t e p D i r e c t i o n
*/
returnValue QProblemB::hotstart_determineStepDirection( const int* const FR_idx, const int* const FX_idx,
const real_t* const delta_g, const real_t* const delta_lb, const real_t* const delta_ub,
BooleanType Delta_bB_isZero,
real_t* const delta_xFX, real_t* const delta_xFR,
real_t* const delta_yFX
)
{
int i, j, ii, jj;
int nFR = getNFR( );
int nFX = getNFX( );
/* initialise auxiliary vectors */
real_t HMX_delta_xFX[NVMAX];
for( i=0; i<nFR; ++i )
HMX_delta_xFX[i] = 0.0;
/* I) DETERMINE delta_xFX */
if ( nFX > 0 )
{
for( i=0; i<nFX; ++i )
{
ii = FX_idx[i];
if ( bounds.getStatus( ii ) == ST_LOWER )
delta_xFX[i] = delta_lb[ii];
else
delta_xFX[i] = delta_ub[ii];
}
}
/* II) DETERMINE delta_xFR */
if ( nFR > 0 )
{
/* auxiliary variables */
real_t delta_xFRz_TMP[NVMAX];
real_t delta_xFRz_RHS[NVMAX];
/* Determine delta_xFRz. */
if ( Delta_bB_isZero == BT_FALSE )
{
for( i=0; i<nFR; ++i )
{
ii = FR_idx[i];
for( j=0; j<nFX; ++j )
{
jj = FX_idx[j];
HMX_delta_xFX[i] += H[ii*NVMAX + jj] * delta_xFX[j];
}
}
}
if ( Delta_bB_isZero == BT_TRUE )
{
for( j=0; j<nFR; ++j )
{
jj = FR_idx[j];
delta_xFRz_RHS[j] = delta_g[jj];
}
}
else
{
for( j=0; j<nFR; ++j )
{
jj = FR_idx[j];
delta_xFRz_RHS[j] = delta_g[jj] + HMX_delta_xFX[j]; /* *ZFR */
}
}
for( i=0; i<nFR; ++i )
delta_xFR[i] = -delta_xFRz_RHS[i];
if ( backsolveR( delta_xFR,BT_TRUE,delta_xFRz_TMP ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_STEPDIRECTION_FAILED_CHOLESKY );
if ( backsolveR( delta_xFRz_TMP,BT_FALSE,delta_xFR ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_STEPDIRECTION_FAILED_CHOLESKY );
}
/* III) DETERMINE delta_yFX */
if ( nFX > 0 )
{
for( i=0; i<nFX; ++i )
{
ii = FX_idx[i];
delta_yFX[i] = 0.0;
for( j=0; j<nFR; ++j )
{
jj = FR_idx[j];
delta_yFX[i] += H[ii*NVMAX + jj] * delta_xFR[j];
}
if ( Delta_bB_isZero == BT_FALSE )
{
for( j=0; j<nFX; ++j )
{
jj = FX_idx[j];
delta_yFX[i] += H[ii*NVMAX + jj] * delta_xFX[j];
}
}
delta_yFX[i] += delta_g[ii];
}
}
return SUCCESSFUL_RETURN;
}
/*
* h o t s t a r t _ d e t e r m i n e S t e p L e n g t h
*/
returnValue QProblemB::hotstart_determineStepLength( const int* const FR_idx, const int* const FX_idx,
const real_t* const delta_lb, const real_t* const delta_ub,
const real_t* const delta_xFR,
const real_t* const delta_yFX,
int& BC_idx, SubjectToStatus& BC_status
)
{
int i, ii;
int nFR = getNFR( );
int nFX = getNFX( );
real_t tau_tmp;
real_t tau_new = 1.0;
BC_idx = 0;
BC_status = ST_UNDEFINED;
/* I) DETERMINE MAXIMUM DUAL STEPLENGTH, i.e. ensure that
* active dual bounds remain valid (ignoring implicitly fixed variables): */
for( i=0; i<nFX; ++i )
{
ii = FX_idx[i];
if ( bounds.getType( ii ) != ST_EQUALITY )
{
if ( bounds.getStatus( ii ) == ST_LOWER )
{
/* 1) Active lower bounds. */
if ( ( delta_yFX[i] < -ZERO ) && ( y[ii] >= 0.0 ) )
{
tau_tmp = y[ii] / ( -delta_yFX[i] );
if ( tau_tmp < tau_new )
{
if ( tau_tmp >= 0.0 )
{
tau_new = tau_tmp;
BC_idx = ii;
BC_status = ST_INACTIVE;
}
}
}
}
else
{
/* 2) Active upper bounds. */
if ( ( delta_yFX[i] > ZERO ) && ( y[ii] <= 0.0 ) )
{
tau_tmp = y[ii] / ( -delta_yFX[i] );
if ( tau_tmp < tau_new )
{
if ( tau_tmp >= 0.0 )
{
tau_new = tau_tmp;
BC_idx = ii;
BC_status = ST_INACTIVE;
}
}
}
}
}
}
/* II) DETERMINE MAXIMUM PRIMAL STEPLENGTH, i.e. ensure that
* inactive bounds remain valid (ignoring unbounded variables). */
/* 1) Inactive lower bounds. */
if ( bounds.isNoLower( ) == BT_FALSE )
{
for( i=0; i<nFR; ++i )
{
ii = FR_idx[i];
if ( bounds.getType( ii ) != ST_UNBOUNDED )
{
if ( delta_lb[ii] > delta_xFR[i] )
{
if ( x[ii] > lb[ii] )
tau_tmp = ( x[ii] - lb[ii] ) / ( delta_lb[ii] - delta_xFR[i] );
else
tau_tmp = 0.0;
if ( tau_tmp < tau_new )
{
if ( tau_tmp >= 0.0 )
{
tau_new = tau_tmp;
BC_idx = ii;
BC_status = ST_LOWER;
}
}
}
}
}
}
/* 2) Inactive upper bounds. */
if ( bounds.isNoUpper( ) == BT_FALSE )
{
for( i=0; i<nFR; ++i )
{
ii = FR_idx[i];
if ( bounds.getType( ii ) != ST_UNBOUNDED )
{
if ( delta_ub[ii] < delta_xFR[i] )
{
if ( x[ii] < ub[ii] )
tau_tmp = ( x[ii] - ub[ii] ) / ( delta_ub[ii] - delta_xFR[i] );
else
tau_tmp = 0.0;
if ( tau_tmp < tau_new )
{
if ( tau_tmp >= 0.0 )
{
tau_new = tau_tmp;
BC_idx = ii;
BC_status = ST_UPPER;
}
}
}
}
}
}
/* III) SET MAXIMUM HOMOTOPY STEPLENGTH */
tau = tau_new;
if ( printlevel == PL_HIGH )
{
#ifdef PC_DEBUG
char messageString[80];
if ( BC_status == ST_UNDEFINED )
sprintf( messageString,"Stepsize is %.6e!",tau );
else
sprintf( messageString,"Stepsize is %.6e! (BC_idx = %d, BC_status = %d)",tau,BC_idx,BC_status );
getGlobalMessageHandler( )->throwInfo( RET_STEPSIZE_NONPOSITIVE,messageString,__FUNCTION__,__FILE__,__LINE__,VS_VISIBLE );
#endif
}
return SUCCESSFUL_RETURN;
}
/*
* h o t s t a r t _ p e r f o r m S t e p
*/
returnValue QProblemB::hotstart_performStep( const int* const FR_idx, const int* const FX_idx,
const real_t* const delta_g, const real_t* const delta_lb, const real_t* const delta_ub,
const real_t* const delta_xFX, const real_t* const delta_xFR,
const real_t* const delta_yFX,
int BC_idx, SubjectToStatus BC_status
)
{
int i, ii;
int nV = getNV( );
int nFR = getNFR( );
int nFX = getNFX( );
/* I) CHECK BOUNDS' CONSISTENCY */
if ( areBoundsConsistent( delta_lb,delta_ub ) == BT_FALSE )
{
infeasible = BT_TRUE;
tau = 0.0;
return THROWERROR( RET_QP_INFEASIBLE );
}
/* II) GO TO ACTIVE SET CHANGE */
if ( tau > ZERO )
{
/* 1) Perform step in primal und dual space. */
for( i=0; i<nFR; ++i )
{
ii = FR_idx[i];
x[ii] += tau*delta_xFR[i];
}
for( i=0; i<nFX; ++i )
{
ii = FX_idx[i];
x[ii] += tau*delta_xFX[i];
y[ii] += tau*delta_yFX[i];
}
/* 2) Shift QP data. */
for( i=0; i<nV; ++i )
{
g[i] += tau*delta_g[i];
lb[i] += tau*delta_lb[i];
ub[i] += tau*delta_ub[i];
}
}
else
{
/* print a stepsize warning if stepsize is zero */
#ifdef PC_DEBUG
char messageString[80];
sprintf( messageString,"Stepsize is %.6e",tau );
getGlobalMessageHandler( )->throwWarning( RET_STEPSIZE,messageString,__FUNCTION__,__FILE__,__LINE__,VS_VISIBLE );
#endif
}
/* setup output preferences */
#ifdef PC_DEBUG
char messageString[80];
VisibilityStatus visibilityStatus;
if ( printlevel == PL_HIGH )
visibilityStatus = VS_VISIBLE;
else
visibilityStatus = VS_HIDDEN;
#endif
/* III) UPDATE ACTIVE SET */
switch ( BC_status )
{
/* Optimal solution found as no working set change detected. */
case ST_UNDEFINED:
return RET_OPTIMAL_SOLUTION_FOUND;
/* Remove one variable from active set. */
case ST_INACTIVE:
#ifdef PC_DEBUG
sprintf( messageString,"bound no. %d.", BC_idx );
getGlobalMessageHandler( )->throwInfo( RET_REMOVE_FROM_ACTIVESET,messageString,__FUNCTION__,__FILE__,__LINE__,visibilityStatus );
#endif
if ( removeBound( BC_idx,BT_TRUE ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_REMOVE_FROM_ACTIVESET_FAILED );
y[BC_idx] = 0.0;
break;
/* Add one variable to active set. */
default:
#ifdef PC_DEBUG
if ( BC_status == ST_LOWER )
sprintf( messageString,"lower bound no. %d.", BC_idx );
else
sprintf( messageString,"upper bound no. %d.", BC_idx );
getGlobalMessageHandler( )->throwInfo( RET_ADD_TO_ACTIVESET,messageString,__FUNCTION__,__FILE__,__LINE__,visibilityStatus );
#endif
if ( addBound( BC_idx,BC_status,BT_TRUE ) != SUCCESSFUL_RETURN )
return THROWERROR( RET_ADD_TO_ACTIVESET_FAILED );
break;
}
return SUCCESSFUL_RETURN;
}
#ifdef PC_DEBUG /* Define print functions only for debugging! */
/*
* p r i n t I t e r a t i o n
*/
returnValue QProblemB::printIteration( int iteration,
int BC_idx, SubjectToStatus BC_status
)
{
char myPrintfString[160];
/* consistency check */
if ( iteration < 0 )
return THROWERROR( RET_INVALID_ARGUMENTS );
/* nothing to do */
if ( printlevel != PL_MEDIUM )
return SUCCESSFUL_RETURN;
/* 1) Print header at first iteration. */
if ( iteration == 0 )
{
sprintf( myPrintfString,"\n############## qpOASES -- QP NO.%4.1d ###############\n", count );
myPrintf( myPrintfString );
sprintf( myPrintfString," Iter | StepLength | Info | nFX \n" );
myPrintf( myPrintfString );
}
/* 2) Print iteration line. */
if ( BC_status == ST_UNDEFINED )
{
sprintf( myPrintfString," %4.1d | %1.5e | QP SOLVED | %4.1d \n", iteration,tau,getNFX( ) );
myPrintf( myPrintfString );
}
else
{
char info[8];
if ( BC_status == ST_INACTIVE )
sprintf( info,"REM BND" );
else
sprintf( info,"ADD BND" );
sprintf( myPrintfString," %4.1d | %1.5e | %s%4.1d | %4.1d \n", iteration,tau,info,BC_idx,getNFX( ) );
myPrintf( myPrintfString );
}
return SUCCESSFUL_RETURN;
}
#endif /* PC_DEBUG */
/*
* c h e c k K K T c o n d i t i o n s
*/
returnValue QProblemB::checkKKTconditions( )
{
#ifdef __PERFORM_KKT_TEST__
int i, j;
int nV = getNV( );
real_t tmp;
real_t maxKKTviolation = 0.0;
/* 1) Check for Hx + g - y*A' = 0 (here: A = Id). */
for( i=0; i<nV; ++i )
{
tmp = g[i];
for( j=0; j<nV; ++j )
tmp += H[i*nV + j] * x[j];
tmp -= y[i];
if ( getAbs( tmp ) > maxKKTviolation )
maxKKTviolation = getAbs( tmp );
}
/* 2) Check for lb <= x <= ub. */
for( i=0; i<nV; ++i )
{
if ( lb[i] - x[i] > maxKKTviolation )
maxKKTviolation = lb[i] - x[i];
if ( x[i] - ub[i] > maxKKTviolation )
maxKKTviolation = x[i] - ub[i];
}
/* 3) Check for correct sign of y and for complementary slackness. */
for( i=0; i<nV; ++i )
{
switch ( bounds.getStatus( i ) )
{
case ST_LOWER:
if ( -y[i] > maxKKTviolation )
maxKKTviolation = -y[i];
if ( getAbs( ( x[i] - lb[i] ) * y[i] ) > maxKKTviolation )
maxKKTviolation = getAbs( ( x[i] - lb[i] ) * y[i] );
break;
case ST_UPPER:
if ( y[i] > maxKKTviolation )
maxKKTviolation = y[i];
if ( getAbs( ( ub[i] - x[i] ) * y[i] ) > maxKKTviolation )
maxKKTviolation = getAbs( ( ub[i] - x[i] ) * y[i] );
break;
default: /* inactive */
if ( getAbs( y[i] ) > maxKKTviolation )
maxKKTviolation = getAbs( y[i] );
break;
}
}
if ( maxKKTviolation > CRITICALACCURACY )
return RET_NO_SOLUTION;
if ( maxKKTviolation > DESIREDACCURACY )
return RET_INACCURATE_SOLUTION;
#endif /* __PERFORM_KKT_TEST__ */
return SUCCESSFUL_RETURN;
}
/*
* end of file
*/