// ============================================================================ // // Copyright (c) 1997-2002 The CGAL Consortium // // This software and related documentation is part of an INTERNAL release // of the Computational Geometry Algorithms Library (CGAL). It is not // intended for general use. // // ---------------------------------------------------------------------------- // // release : $CGAL_Revision: CGAL-I $ // release_date : $CGAL_Date$ // // file : include/CGAL/QPE_solver.C // package : $CGAL_Package: QP_engine $ // chapter : Quadratic Programming Engine // // revision : 3.0alpha // revision_date : 2003/07 // // author(s) : Sven Schönherr // coordinator : ETH Zürich (Bernd Gärtner ) // // implementation: Quadratic Programming Engine - Solver // ============================================================================ CGAL_BEGIN_NAMESPACE // ============================= // class implementation (cont'd) // ============================= // creation & initialization // ------------------------- // creation template < class Rep_ > QPE_solver:: QPE_solver( ) : et0( 0), et1( 1), et2( 2), strategyP( static_cast< Pricing_strategy*>( 0)), inv_M_B( vout4), d( inv_M_B.denominator()), m_phase( -1), is_phaseI( false), is_phaseII( false), is_LP( check_tag( Is_linear())), is_QP( ! is_LP), no_ineq( check_tag( Has_no_inequalities())), has_ineq( ! no_ineq) { } // set-up of QP template < class Rep_ > void QPE_solver:: set( int n, int m, typename QPE_solver::A_iterator A_it, typename QPE_solver::B_iterator b_it, typename QPE_solver::C_iterator c_it, typename QPE_solver::D_iterator D_it, typename QPE_solver::Row_type_iterator Row_type_it) { // store QP CGAL_qpe_precondition( n > 0); CGAL_qpe_precondition( m > 0); qp_n = n; qp_m = m; qp_A = A_it; qp_b = b_it; qp_c = c_it; qp_D = D_it; qp_r = Row_type_it; l = std::min( n, m); // set up slack variables and auxiliary problem // -------------------------------------------- // clear slack and artificial part of `A', if necessary if ( ! slack_A.empty()) slack_A.clear(); if ( ! art_A.empty()) art_A.clear(); if ( ! art_s.empty()) art_s.clear(); // reserve memory for slack and artificial part of `A' if ( has_ineq) { unsigned int art_size = std::count( qp_r, qp_r+qp_m, Rep::EQUAL); slack_A.reserve( qp_m - art_size); art_A.reserve( art_size); art_s.insert ( art_s.end(), qp_m, A_entry( 0)); } else { art_A.reserve( qp_m); } // initialize slack and artificial part of `A' B_entry b0( 0), b_max( b0); C_entry c1( 1); int i_max = -1; for ( int i = 0; i < qp_m; ++i) { if ( has_ineq && ( qp_r[ i] != Rep::EQUAL)) { // slack variable if ( qp_r[ i] == Rep::LESS_EQUAL) { // '<=' if ( qp_b[ i] < b0) { // special entry '< -0' art_s[ i] = -c1; if ( -qp_b[ i] > b_max) { i_max = slack_A.size(); b_max = -qp_b[ i]; } } // store slack column slack_A.push_back( std::make_pair( i, false)); } else { // '>=' if ( qp_b[ i] > b0) { // special entry '> +0' art_s[ i] = c1; if ( qp_b[ i] > b_max) { i_max = slack_A.size(); b_max = qp_b[ i]; } } // store slack column slack_A.push_back( std::make_pair( i, true)); } } else { // artificial variable art_A.push_back( std::make_pair( i, qp_b[ i] < b0)); } } // special artificial column needed? if ( i_max >= 0) { art_s_i = -i_max; } else { art_s_i = -1; art_s.clear(); } // diagnostic output CGAL_qpe_debug { if ( vout.verbose()) { if ( vout2.verbose()) { vout2.out() << "======" << std::endl << "Set-Up" << std::endl << "======" << std::endl; } vout.out() << "[ " << ( is_LP ? "LP" : "QP") << ", " << n << " variables, " << m << " constraints"; if ( vout2.verbose() && ( ! slack_A.empty())) { vout2.out() << " (" << slack_A.size() << " inequalities)"; } vout.out() << " ]" << std::endl; if ( vout2.verbose()) { if ( is_QP) { vout2.out() << "flag: D " << ( check_tag( Is_symmetric()) ? "" : "not ") << "symmetric" << std::endl; } vout2.out() << "flag: " << ( has_ineq ? "has" : "no") << " inequality constraints" << std::endl; if ( vout4.verbose()) print_program(); } } } // set up pricing strategy if ( strategyP != static_cast< Pricing_strategy*>( 0)) { strategyP->set( *this, vout2); } // set up basis inverse inv_M_B.set( qp_n, qp_m); // set phase m_phase = 0; is_phaseI = false; is_phaseII = false; } // initialization (phase I) template < class Rep_ > void QPE_solver:: init( ) { CGAL_qpe_debug { vout2 << std::endl << "==============" << std::endl << "Initialization" << std::endl << "==============" << std::endl; } // set status m_phase = 1; m_status = UPDATE; m_pivots = 0; is_phaseI = true; // initial basis and basis inverse init_basis(); // initial solution init_solution(); // initialize additional variables init_additional_variables(); // initialize pricing strategy CGAL_qpe_precondition( strategyP != static_cast< Pricing_strategy*>( 0)); strategyP->init( 0); } // initial basis and basis inverse template < class Rep_ > void QPE_solver:: init_basis( ) { int i, s_i = -1; int j, s = slack_A.size(); // has special artificial column? if ( ! art_s.empty()) { s_i = -art_s_i; art_s_i = qp_n+slack_A.size()+art_A.size(); art_A.push_back( std::make_pair( s_i, slack_A[ s_i].first)); } // initialize indices of basic variables if ( ! in_B.empty()) in_B.clear(); in_B.reserve( qp_n+slack_A.size()+art_A.size()); in_B.insert( in_B.end(), qp_n, -1); // no original variable is basic init_basis__slack_variables( s_i, Has_no_inequalities()); if ( ! B_O.empty()) B_O.clear(); B_O.reserve( qp_n); // all artificial variables are for ( i = 0, j = qp_n+s; i < (int)art_A.size(); ++i, ++j) { // basic B_O.push_back( j); in_B .push_back( i); } art_basic = art_A.size(); // initialize indices of 'basic' and 'nonbasic' constraints init_basis__constraints( s_i, Has_no_inequalities()); // diagnostic output CGAL_qpe_debug { if ( vout.verbose()) print_basis(); } // initialize basis inverse inv_M_B.init( art_A.size(), art_A.begin()); } template < class Rep_ > // has ineq. void QPE_solver:: init_basis__slack_variables( int s_i, Tag_false) { int s = slack_A.size(); // reserve memory if ( ! B_S.empty()) B_S.clear(); B_S.reserve( s); // (almost) all slack variables are basic for ( i = 0, j = qp_n; i < s; ++i, ++j) { if ( i != s_i) { in_B .push_back( B_S.size()); B_S.push_back( j); } else { in_B .push_back( -1); } } } template < class Rep_ > // has ineq. void QPE_solver:: init_basis__constraints( int s_i, Tag_false) { // reserve memory if ( ! C .empty()) C .clear(); if ( ! in_C .empty()) in_C .clear(); if ( ! S_B.empty()) S_B.clear(); C.reserve( l); S_B.reserve( slack_A.size()); // store constraints' indices in_C.insert( in_C.end(), qp_m, -1); if ( s_i >= 0) s_i = slack_A[ s_i].first; for ( i = 0, j = 0; i < qp_m; ++i) { if ( qp_r[ i] == Rep::EQUAL) { // equal. constraints are basic C.push_back( i); in_C[ i] = j; ++j; } else { // ineq. constrai. are nonbasic if ( i != s_i) S_B.push_back( i); } } if ( s_i >= 0) { // special ineq. con. is basic C.push_back( s_i); in_C[ s_i] = j; } } // initial solution template < class Rep_ > void QPE_solver:: init_solution( ) { // initialize exact version of `qp_b' restricted to basic constraints C // (implicit conversion to ET) if ( ! b_C.empty()) b_C.clear(); init_solution__b_C( Has_no_inequalities()); // initialize exact version of `-aux_c' restricted to basic variables B_O if ( ! minus_c_B.empty()) minus_c_B.clear(); minus_c_B.insert( minus_c_B.end(), l, -et1); if ( art_s_i > 0) minus_c_B[ art_A.size()-1] *= ET( qp_n+qp_m); // allocate memory for current solution if ( ! lambda.empty()) lambda.clear(); if ( ! x_B_O .empty()) x_B_O .clear(); if ( ! x_B_S .empty()) x_B_S .clear(); lambda.insert( lambda.end(), l, et0); x_B_O .insert( x_B_O .end(), l, et0); x_B_S .insert( x_B_S .end(), slack_A.size(), et0); // compute initial solution compute_solution(); // diagnostic output CGAL_qpe_debug { if ( vout.verbose()) print_solution(); } } // initialize additional variables template < class Rep_ > void QPE_solver:: init_additional_variables( ) { if ( ! A_Cj.empty()) A_Cj.clear(); A_Cj.insert( A_Cj.end(), l, et0); if ( ! two_D_Bj.empty()) two_D_Bj.clear(); two_D_Bj.insert( two_D_Bj.end(), l, et0); if ( ! q_lambda.empty()) q_lambda.clear(); q_lambda.insert( q_lambda.end(), l, et0); if ( ! q_x_O.empty()) q_x_O.clear(); q_x_O.insert( q_x_O.end(), l, et0); if ( ! q_x_S.empty()) q_x_S.clear(); q_x_S.insert( q_x_S.end(), slack_A.size(), et0); if ( ! tmp_l.empty()) tmp_l.clear(); tmp_l.insert( tmp_l.end(), l, et0); if ( ! tmp_x.empty()) tmp_x.clear(); tmp_x.insert( tmp_x.end(), l, et0); } // transition (to phase II) // ------------------------ template < class Rep > void QPE_solver:: transition( ) { CGAL_qpe_debug { if ( vout.verbose()) { vout2 << std::endl << "----------------------" << std::endl << 'T'; vout1 << "[ t"; vout << "ransition to phase II"; vout1 << " ]"; vout << std::endl; vout2 << "----------------------"; } } // update status m_phase = 2; is_phaseI = false; is_phaseII = true; // remove artificial variables in_B.erase( in_B.begin()+qp_n+slack_A.size(), in_B.end()); // update basis inverse CGAL_qpe_debug { vout4 << std::endl << "basis-inverse:" << std::endl; } transition( Is_linear()); check_basis_inverse(); // initialize exact version of `-qp_c' (implicit conversion to ET) C_by_index_accessor c_accessor( qp_c); std::transform( C_by_index_iterator( B_O.begin(), c_accessor), C_by_index_iterator( B_O.end (), c_accessor), minus_c_B.begin(), std::negate()); // compute initial solution of phase II compute_solution(); // diagnostic output CGAL_qpe_debug { if ( vout.verbose()) print_solution(); } // notify pricing strategy strategyP->transition(); } // access // ------ // numerator of current solution template < class Rep_ > QPE_solver::ET QPE_solver:: solution_numerator( ) const { Index_iterator i_it; Value_const_iterator x_i_it, c_it; ET s, z = et0; int i; // foreach i x_i_it = x_B_O.begin(); c_it = minus_c_B .begin(); for ( i_it = B_O.begin(); i_it != B_O.end(); ++i_it, ++x_i_it, ++c_it){ i = *i_it; // quadratic part s = et0; if ( is_QP && is_phaseII) { // foreach j < i s += std::inner_product( x_B_O.begin(), x_i_it, D_pairwise_iterator( B_O.begin(), D_pairwise_accessor( qp_D, i)), et0); // D_{i,i} x_i s += ET( qp_D[ i][ i]) * *x_i_it; } // linear part s -= d * *c_it; // accumulate z += s * *x_i_it; } return z; } // pivot step // ---------- template < class Rep_ > void QPE_solver:: pivot_step( ) { ++m_pivots; // diagnostic output CGAL_qpe_debug { vout2 << std::endl << "==========" << std::endl << "Pivot Step" << std::endl << "==========" << std::endl; vout << "[ phase " << ( is_phaseI ? "I" : "II") << ", iteration " << m_pivots << " ]" << std::endl; } // pricing // ------- pricing(); // check for optimality if ( j < 0) { if ( is_phaseI) { // phase I // problem is infeasible m_phase = 3; m_status = INFEASIBLE; CGAL_qpe_debug { vout1 << " "; vout << "problem is INFEASIBLE" << std::endl; } } else { // phase II // optimal solution found m_phase = 3; m_status = OPTIMAL; CGAL_qpe_debug { vout1 << " "; vout2 << std::endl; vout << "solution is OPTIMAL" << std::endl; } } return; } // ratio test & update (step 1) // ---------------------------- // initialize ratio test ratio_test_init(); // diagnostic output CGAL_qpe_debug { if ( vout2.verbose() && is_QP && is_phaseII) { vout2.out() << std::endl << "----------------------------" << std::endl << "Ratio Test & Update (Step 1)" << std::endl << "----------------------------" << std::endl; } } // loop (step 1) do { // ratio test ratio_test_1(); // check for unboundedness if ( q_i == et0) { m_phase = 3; m_status = UNBOUNDED; CGAL_qpe_debug { vout1 << " "; vout << "problem is UNBOUNDED" << std::endl; } return; } // update update_1(); } while ( j >= 0); // ratio test & update (step 2) // ---------------------------- if ( i >= 0) { // diagnostic output CGAL_qpe_debug { vout2 << std::endl << "----------------------------" << std::endl << "Ratio Test & Update (Step 2)" << std::endl << "----------------------------" << std::endl; } // compute index of entering variable j += in_B.size(); // loop (step 2) while ( ( i >= 0) && basis_matrix_stays_regular()) { // update update_2( Is_linear()); // ratio test ratio_test_2( Is_linear()); } } // ratio test & update (step 3) // ---------------------------- CGAL_qpe_assertion_msg( i < 0, "Step 3 should never be reached!"); // diagnostic output CGAL_qpe_debug { if ( vout1.verbose()) print_basis(); if ( vout .verbose()) print_solution(); } // transition to phase II (if possible) // ------------------------------------ if ( is_phaseI && ( art_basic == 0)) { CGAL_qpe_debug { if ( vout2.verbose()) { vout2.out() << std::endl << "all artificial variables are nonbasic" << std::endl; } } transition(); } } // pricing template < class Rep_ > void QPE_solver:: pricing( ) { // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) { vout2 << std::endl << "-------" << std::endl << "Pricing" << std::endl << "-------" << std::endl; } } // call pricing strategy j = strategyP->pricing(); // diagnostic output CGAL_qpe_debug { if ( vout.verbose()) { if ( j < 0) { vout2 << "entering variable: none" << std::endl; } else { vout1 << " "; vout << "entering"; vout2 << " variable"; vout << ": "; vout << j; vout2 << " (" << variable_type( j) << ')' << std::endl; } } } } // initialization of ratio-test template < class Rep_ > void QPE_solver:: ratio_test_init( ) { // store exact version of `A_Cj' (implicit conversion) ratio_test_init__A_Cj( A_Cj.begin(), j, Has_no_inequalities()); // store exact version of `2 D_{B_O,j}' ratio_test_init__2_D_Bj( two_D_Bj.begin(), j, Is_linear()); } template < class Rep_ > // no ineq. void QPE_solver:: ratio_test_init__A_Cj( Value_iterator A_Cj_it, int j_, Tag_true) { // store exact version of `A_Cj' (implicit conversion) if ( j_ < qp_n) { // original variable std::copy( qp_A[ j_], qp_A[ j_]+qp_m, A_Cj_it); } else { // artificial variable unsigned int k = j_; k -= qp_n; std::fill_n( A_Cj_it, qp_m, et0); A_Cj_it[ k] = ( art_A[ k].second ? -et1 : et1); } } template < class Rep_ > // has ineq. void QPE_solver:: ratio_test_init__A_Cj( Value_iterator A_Cj_it, int j_, Tag_false) { // store exact version of `A_Cj' (implicit conversion) if ( j_ < qp_n) { // original variable A_by_index_accessor a_accessor( qp_A[ j_]); std::copy( A_by_index_iterator( C.begin(), a_accessor), A_by_index_iterator( C.end (), a_accessor), A_Cj_it); } else { unsigned int k = j_; k -= qp_n; std::fill_n( A_Cj_it, C.size(), et0); if ( k < slack_A.size()) { // slack variable A_Cj_it[ in_C[ slack_A[ k].first]] = ( slack_A[ k].second ? -et1 : et1); } else { // artificial variable k -= slack_A.size(); if ( j_ != art_s_i) { // normal art. A_Cj_it[ in_C[ art_A[ k].first]] = ( art_A[ k].second ? -et1 : et1); } else { // special art. S_by_index_accessor s_accessor( art_s.begin()); std::copy( S_by_index_iterator( C.begin(), s_accessor), S_by_index_iterator( C.end (), s_accessor), A_Cj_it); } } } } // ratio test (step 1) template < class Rep_ > void QPE_solver:: ratio_test_1( ) { // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) { vout2.out() << std::endl; if ( is_LP || is_phaseI) { vout2.out() << "----------" << std::endl << "Ratio Test" << std::endl << "----------" << std::endl; } else { vout2.out() << "Ratio Test (Step 1)" << std::endl << "-------------------" << std::endl; } if ( vout3.verbose()) { vout3.out() << " A_Cj: "; std::copy( A_Cj.begin(), A_Cj.begin()+C.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl; if ( is_QP && is_phaseII) { vout3.out() << " 2 D_Bj: "; std::copy( two_D_Bj.begin(), two_D_Bj.begin()+B_O.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl; } vout3.out() << std::endl; } } } // compute `q_lambda' and `q_x' ratio_test_1__q_x_O( Is_linear()); ratio_test_1__q_x_S( Has_no_inequalities()); // diagnostic output CGAL_qpe_debug { if ( vout3.verbose()) { if ( is_QP && is_phaseII) { vout3.out() << "q_lambda: "; std::copy( q_lambda.begin(), q_lambda.begin()+C.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl; } vout3.out() << " q_x_O: "; std::copy( q_x_O.begin(), q_x_O.begin()+B_O.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl; if ( has_ineq) { vout3.out() << " q_x_S: "; std::copy( q_x_S.begin(), q_x_S.begin()+B_S.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl; } vout3.out() << std::endl; } } // check `t_i's x_i = et1; // trick: initialize q_i = et0; // minimum with +oo // what happens, if all original variables are nonbasic? ratio_test_1__t_i( B_O.begin(), B_O.end(), x_B_O.begin(), q_x_O.begin(), Tag_false()); ratio_test_1__t_i( B_S.begin(), B_S.end(), x_B_S.begin(), q_x_S.begin(), Has_no_inequalities()); // check `t_j' ratio_test_1__t_j( Is_linear()); // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) { for ( unsigned int k = 0; k < B_O.size(); ++k) { vout2.out() << "t_O_" << k << ": " << x_B_O[ k] << '/' << q_x_O[ k] << ( ( q_i > et0) && ( i == B_O[ k]) ? " *" : "") << std::endl; } if ( has_ineq) { for ( unsigned int k = 0; k < B_S.size(); ++k) { vout2.out() << "t_S_" << k << ": " << x_B_S[ k] << '/' << q_x_S[ k] << ( ( q_i > et0) && ( i == B_S[ k]) ? " *":"") << std::endl; } } if ( is_QP && is_phaseII) { vout2.out() << std::endl << " t_j: " << mu << '/' << nu << ( ( q_i > et0) && ( i < 0) ? " *" : "") << std::endl; } vout2.out() << std::endl; } if ( q_i > et0) { if ( i < 0) { vout2 << "leaving variable: none" << std::endl; } else { vout1 << ", "; vout << "leaving"; vout2 << " variable"; vout << ": "; vout << i; if ( vout2.verbose()) { if ( ( i < qp_n) || ( i >= (int)( qp_n+slack_A.size()))) { vout2.out() << " (= B_O[ " << in_B[ i] << "]: " << variable_type( i) << ')' << std::endl; } else { vout2.out() << " (= B_S[ " << in_B[ i] << "]: slack)" << std::endl; } } } } } } template < class Rep_ > // QP case void QPE_solver:: ratio_test_2( Tag_false) { // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) { vout2.out() << std::endl << "Ratio Test (Step 2)" << std::endl << "-------------------" << std::endl; } } // get `q_x' /* if ( is_QP && is_phaseII) { // QP && phase II inv_M_B.multiply ( A_Cj.begin(), two_D_Bj.begin(), q_lambda.begin(), q_x_O.begin()); } else { // LP || phase I inv_M_B.multiply_x( A_Cj.begin(), q_x_O.begin()); } multiply__A_S_BxB_O( q_x_O.begin(), q_x_S.begin()); */ // diagnostic output CGAL_qpe_debug { if ( vout3.verbose()) { vout3.out() << " q_x_O: "; std::copy( q_x_O.begin(), q_x_O.begin()+B_O.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl; if ( has_ineq) { vout3.out() << " q_x_S: "; std::copy( q_x_S.begin(), q_x_S.begin()+B_S.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl; } vout3.out() << std::endl; } } /* // check `t_i's x_i = et1; // trick: initialize q_i = et0; // minimum with +oo Value_iterator x_it = x_B_O.begin(); Value_iterator q_it = q_x_O.begin(); Index_iterator i_it; for ( i_it = B_O.begin(); i_it != B_O.end(); ++i_it, ++x_it, ++q_it) { if ( ( *q_it > et0) && ( ( *x_it * q_i) < ( x_i * *q_it))) { i = *i_it; x_i = *x_it; q_i = *q_it; } } x_it = x_B_S.begin(); q_it = q_x_S.begin(); for ( i_it = B_S.begin(); i_it != B_S.end(); ++i_it, ++x_it, ++q_it) { if ( ( *q_it > et0) && ( ( *x_it * q_i) < ( x_i * *q_it))) { i = *i_it; x_i = *x_it; q_i = *q_it; } } CGAL_qpe_debug { if ( vout2.verbose()) { for ( unsigned int k = 0; k < B_O.size(); ++k) { vout2.out() << "t_O_" << k << ": " << x_B_O[ k] << '/' << q_x_O[ k] << ( ( q_i > et0) && ( i == B_O[ k]) ? " *" : "") << std::endl; } for ( unsigned int k = 0; k < B_S.size(); ++k) { vout2.out() << "t_S_" << k << ": " << x_B_S[ k] << '/' << q_x_S[ k] << ( ( q_i > et0) && ( i == B_S[ k]) ? " *" : "") << std::endl; } vout2.out() << std::endl; } if ( q_i > et0) { if ( i < 0) { vout2 << "leaving variable: none" << std::endl; } else { vout1 << ", "; vout << "leaving"; vout2 << " variable"; vout << ": "; vout << i; if ( vout2.verbose()) { if ( ( i < qp_n) || ( i >= (int)( qp_n+slack_A.size()))) { vout2.out() << " (= B_O[ " << in_B[ i] << "]: " << variable_type( i) << ')' << std::endl; } else { vout2.out() << " (= B_S[ " << in_B[ i] << "]: slack)" << std::endl; } } } } } */ } // update (step 1) template < class Rep_ > void QPE_solver:: update_1( ) { CGAL_qpe_debug { if ( vout2.verbose()) { vout2.out() << std::endl; if ( is_LP || is_phaseI) { vout2.out() << "------" << std::endl << "Update" << std::endl << "------" << std::endl; } else { vout2.out() << "Update (Step 1)" << std::endl << "---------------" << std::endl; } } } // update basis & basis inverse update_1( Is_linear()); check_basis_inverse(); // compute current solution compute_solution(); } // update (step 2) template < class Rep_ > // QP case void QPE_solver:: update_2( Tag_false) { CGAL_qpe_debug { vout2 << std::endl << "Update (Step 2)" << std::endl << "---------------" << std::endl; } // leave variable from basis leave_variable(); check_basis_inverse(); // compute current solution compute_solution(); } // replace variable in basis template < class Rep_ > void QPE_solver:: replace_variable( ) { CGAL_qpe_debug { vout2 << "<--> nonbasic (" << variable_type( j) << ") variable " << j << " replaces basic (" << variable_type( i) << ") variable " << i << std::endl << std::endl; } // replace variable replace_variable( Has_no_inequalities()); // pivot step done i = j = -1; } template < class Rep_ > void QPE_solver:: replace_variable_original_original( ) { int k = in_B[ i]; // replace original variable [ in: j | out: i ] in_B [ i] = -1; in_B [ j] = k; B_O[ k] = j; minus_c_B[ k] = ( is_phaseI ? ( j < qp_n ? et0 : -et1) : -ET( qp_c[ j])); if ( is_phaseI) { if ( j >= qp_n) ++art_basic; if ( i >= qp_n) --art_basic; } // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) print_basis(); } // update basis inverse inv_M_B.enter_original_leave_original( q_x_O.begin(), k); } template < class Rep_ > void QPE_solver:: replace_variable_slack_slack( ) { int k = in_B[ i]; // replace slack variable [ in: j | out: i ] in_B [ i] = -1; in_B [ j] = k; B_S[ k] = j; S_B[ k] = slack_A[ j-qp_n].first; // replace inequality constraint [ in: i | out: j ] int old_row = S_B[ k]; int new_row = slack_A[ i-qp_n].first; k = in_C[ old_row]; in_C[ old_row] = -1; in_C[ new_row] = k; C[ k ] = new_row; b_C[ k] = ET( qp_b[ new_row]); // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) print_basis(); } // update basis inverse A_row_by_index_accessor a_accessor( A_accessor( qp_A, 0, qp_n), new_row); std::copy( A_row_by_index_iterator( B_O.begin(), a_accessor), A_row_by_index_iterator( B_O.end (), a_accessor), tmp_x.begin()); if ( art_s_i > 0) { // special artificial tmp_x[ in_B[ art_s_i]] = ET( art_s[ new_row]); } inv_M_B.enter_slack_leave_slack( tmp_x.begin(), k); } template < class Rep_ > void QPE_solver:: replace_variable_slack_original( ) { int k = in_B[ i]; // leave original variable [ out: i ] in_B [ i ] = -1; in_B [ B_O.back()] = k; B_O[ k] = B_O.back(); B_O.pop_back(); minus_c_B[ k] = minus_c_B[ B_O.size()]; if ( is_phaseI && ( i >= qp_n)) --art_basic; // enter slack variable [ in: j ] int old_row = slack_A[ j-qp_n].first; in_B [ j] = B_S.size(); B_S.push_back( j); S_B.push_back( old_row); // leave inequality constraint [ out: j ] int l = in_C[ old_row]; b_C[ l ] = b_C[ C.size()-1]; C[ l ] = C.back(); in_C[ C.back()] = l; in_C[ old_row ] = -1; C.pop_back(); // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) print_basis(); } // update basis inverse inv_M_B.swap_variable( k); inv_M_B.swap_constraint( l); inv_M_B.enter_slack_leave_original(); } template < class Rep_ > void QPE_solver:: replace_variable_original_slack( ) { int k = in_B[ i]; // enter original variable [ in: j ] minus_c_B[ B_O.size()] = ( is_phaseI ? ( j < qp_n ? et0 : -et1) : -ET( qp_c[ j])); in_B [ j] = B_O.size(); B_O.push_back( j); if ( is_phaseI && ( j >= qp_n)) ++art_basic; // leave slack variable [ out: i ] B_S[ k ] = B_S.back(); S_B[ k ] = S_B.back(); in_B [ B_S.back()] = k; in_B [ i ] = -1; B_S.pop_back(); S_B.pop_back(); // enter inequality constraint [ in: i ] int new_row = slack_A[ i-qp_n].first; b_C[ C.size()] = ET( qp_b[ new_row]); in_C[ new_row ] = C.size(); C.push_back( new_row); // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) print_basis(); } // update basis inverse A_row_by_index_accessor a_accessor( A_accessor( qp_A, 0, qp_n), new_row); std::copy( A_row_by_index_iterator( B_O.begin(), a_accessor), A_row_by_index_iterator( B_O.end (), a_accessor), tmp_x.begin()); if ( art_s_i > 0) { // special art. tmp_x[ in_B[ art_s_i]] = ET( art_s[ new_row]); } inv_M_B.enter_original_leave_slack( q_x_O.begin(), tmp_x.begin()); } // enter variable into basis template < class Rep_ > void QPE_solver:: enter_variable( ) { CGAL_qpe_debug { vout2 << "--> nonbasic (" << variable_type( j) << ") variable " << j << " enters basis" << std::endl << std::endl; } // update basis & basis inverse if ( no_ineq || ( j < qp_n)) { // original variable // enter original variable [ in: j ] in_B [ j] = B_O.size(); B_O.push_back( j); if ( minus_c_B.size() < B_O.size()) { minus_c_B.push_back( -ET( qp_c[ j])); q_x_O.push_back( et0); tmp_x.push_back( et0); } else { minus_c_B[ B_O.size()-1] = -ET( qp_c[ j]); } // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) print_basis(); } // update basis inverse inv_M_B.enter_original( q_lambda.begin(), q_x_O.begin(), -nu); } else { // slack variable // enter slack variable [ in: j ] in_B [ j] = B_S.size(); B_S.push_back( j); S_B.push_back( slack_A[ j-qp_n].first); // leave inequality constraint [ out: j ] int old_row = slack_A[ j-qp_n].first; C[ in_C[ old_row]] = C.back(); in_C[ C.back() ] = old_row; in_C[ old_row ] = -1; C.pop_back(); // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) print_basis(); } // update basis inverse inv_M_B.swap_constraint( old_row); inv_M_B.enter_slack(); } // variable entered j -= in_B.size(); } // leave variable from basis template < class Rep_ > void QPE_solver:: leave_variable( ) { CGAL_qpe_debug { vout2 << "<-- basic (" << variable_type( i) << ") variable " << i << " leaves basis" << std::endl << std::endl; } // update basis & basis inverse int k = in_B[ i]; if ( no_ineq || ( i < qp_n)) { // original variable // leave original variable [ out: i ] in_B [ i ] = -1; in_B [ B_O.back()] = k; B_O[ k] = B_O.back(); B_O.pop_back(); minus_c_B[ k] = minus_c_B[ B_O.size()]; // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) print_basis(); } // update basis inverse inv_M_B.swap_variable( k); inv_M_B.leave_original(); } else { // slack variable // leave slack variable [ out: i ] in_B [ i ] = -1; in_B [ B_S.back()] = k; B_S[ k] = B_S.back(); B_S.pop_back(); S_B[ k] = S_B.back(); S_B.pop_back(); // enter inequality constraint [ in: i ] int new_row = slack_A[ i-qp_n].first; b_C[ C.size()] = ET( qp_b[ new_row]); in_C[ new_row ] = C.size(); C.push_back( new_row); // diagnostic output CGAL_qpe_debug { if ( vout2.verbose()) print_basis(); } // update basis inverse A_row_by_index_accessor a_accessor( A_accessor( qp_A, 0, qp_n), new_row); std::copy( A_row_by_index_iterator( B_O.begin(), a_accessor), A_row_by_index_iterator( B_O.end (), a_accessor), tmp_x.begin()); inv_M_B.leave_slack( tmp_x.begin()); } // notify pricing strategy strategyP->leaving_basis( i); // variable left i = -1; } // compute solution template < class Rep_ > void QPE_solver:: compute_solution() { // compute current solution inv_M_B.multiply( b_C.begin(), minus_c_B.begin(), lambda.begin(), x_B_O.begin()); compute__x_B_S( Has_no_inequalities()); } template < class Rep_ > void QPE_solver:: multiply__A_S_BxB_O( Value_iterator in, Value_iterator out) const { // initialize std::fill_n( out, B_S.size(), et0); // foreach original column of A in B_O (artificial columns are zero in S_B A_column a_col; // except special) Index_iterator row_it, col_it; Value_iterator out_it; ET in_value; for ( col_it = B_O.begin(); col_it != B_O.end(); ++col_it, ++in) { in_value = *in; out_it = out; if ( *col_it < qp_n) { // original variable a_col = qp_A[ *col_it]; // foreach row of A in S_B for ( row_it = S_B.begin(); row_it != S_B.end(); ++row_it, ++out_it) { *out_it += ET( a_col[ *row_it]) * in_value; } } else { if ( *col_it == art_s_i) { // special artificial // foreach row of 'art_s' for ( row_it = S_B.begin(); row_it != S_B.end(); ++row_it, ++out_it) { *out_it += ET( art_s[ *row_it]) * in_value; } } } } } // check basis inverse template < class Rep_ > bool QPE_solver:: check_basis_inverse() { // diagnostic output CGAL_qpe_debug { vout4 << "check: " << std::flush; } bool ok = check_basis_inverse( Is_linear()); // diagnostic output CGAL_qpe_debug { if ( ok) vout4 << "ok" << std::endl; } return ok; } template < class Rep_ > // LP case bool QPE_solver:: check_basis_inverse( Tag_true) { CGAL_qpe_debug { vout4 << std::endl; } unsigned int row, rows = C.size(); unsigned int col, cols = B_O.size(); Index_iterator i_it = B_O.begin(); Value_iterator q_it; for ( col = 0; col < cols; ++col, ++i_it) { ratio_test_init__A_Cj( tmp_l.begin(), *i_it, Has_no_inequalities()); inv_M_B.multiply_x( tmp_l.begin(), q_x_O.begin()); CGAL_qpe_debug { if ( vout4.verbose()) { std::copy( tmp_l.begin(), tmp_l.begin()+rows, std::ostream_iterator( vout4.out(), " ")); vout4.out() << " || "; std::copy( q_x_O.begin(), q_x_O.begin()+cols, std::ostream_iterator( vout4.out(), " ")); vout4.out() << std::endl; } } q_it = q_x_O.begin(); for ( row = 0; row < rows; ++row, ++q_it) { if ( *q_it != ( row == col ? d : et0)) { if ( ! vout4.verbose()) { std::cerr << std::endl << "basis-inverse check: "; } std::cerr << "failed ( row=" << row << " | col=" << col << " )" << std::endl; } } } return true; } template < class Rep_ > // QP case bool QPE_solver:: check_basis_inverse( Tag_false) { unsigned int row, rows = C.size(); unsigned int col, cols = B_O.size(); Value_iterator v_it; Index_iterator i_it; CGAL_qpe_debug { vout4 << std::endl; } // left part of M_B std::fill_n( tmp_l.begin(), rows, et0); for ( col = 0; col < rows; ++col) { row = ( has_ineq ? C[ col] : col); v_it = tmp_x.begin(); for ( i_it = B_O.begin(); i_it != B_O.end(); ++i_it, ++v_it) { *v_it = ( *i_it < qp_n ? qp_A[ *i_it][ row] : art_A[ *i_it - qp_n].first != (int)row ? et0 : ( art_A[ *i_it - qp_n].second ? -et1 : et1)); } // ToDo: handling of special artificial [art_s_i] inv_M_B.multiply( tmp_l.begin(), tmp_x.begin(), q_lambda.begin(), q_x_O.begin()); CGAL_qpe_debug { if ( vout4.verbose()) { std::copy( tmp_l.begin(), tmp_l.begin()+rows, std::ostream_iterator( vout4.out(), " ")); vout4.out() << "| "; std::copy( tmp_x.begin(), tmp_x.begin()+cols, std::ostream_iterator( vout4.out(), " ")); vout4.out() << " || "; std::copy( q_lambda.begin(), q_lambda.begin()+rows, std::ostream_iterator( vout4.out(), " ")); vout4.out() << " | "; std::copy( q_x_O.begin(), q_x_O.begin()+cols, std::ostream_iterator( vout4.out(), " ")); vout4.out() << std::endl; } } v_it = q_lambda.begin(); for ( row = 0; row < rows; ++row, ++v_it) { if ( *v_it != ( row == col ? d : et0)) { if ( ! vout4.verbose()) { std::cerr << std::endl << "basis-inverse check: "; } std::cerr << "failed ( row=" << row << " | col=" << col << " )" << std::endl; // return false; } } v_it = std::find_if( q_x_O.begin(), q_x_O.begin()+cols, std::bind2nd( std::not_equal_to(), et0)); if ( v_it != q_x_O.begin()+cols) { if ( ! vout4.verbose()) { std::cerr << std::endl << "basis-inverse check: "; } std::cerr << "failed ( row=" << rows+(v_it-q_x_O.begin()) << " | col=" << col << " )" << std::endl; // return false; } } // right part of M_B if ( is_phaseI) std::fill_n( tmp_x.begin(), B_O.size(), et0); i_it = B_O.begin(); for ( col = 0; col < cols; ++col, ++i_it) { ratio_test_init__A_Cj ( tmp_l.begin(), *i_it, Has_no_inequalities()); ratio_test_init__2_D_Bj( tmp_x.begin(), *i_it, Tag_false()); inv_M_B.multiply( tmp_l.begin(), tmp_x.begin(), q_lambda.begin(), q_x_O.begin()); CGAL_qpe_debug { if ( vout4.verbose()) { std::copy( tmp_l.begin(), tmp_l.begin()+rows, std::ostream_iterator( vout4.out(), " ")); vout4.out() << "| "; std::copy( tmp_x.begin(), tmp_x.begin()+cols, std::ostream_iterator( vout4.out(), " ")); vout4.out() << " || "; std::copy( q_lambda.begin(), q_lambda.begin()+rows, std::ostream_iterator( vout4.out(), " ")); vout4.out() << " | "; std::copy( q_x_O.begin(), q_x_O.begin()+cols, std::ostream_iterator( vout4.out(), " ")); vout4.out() << std::endl; } } v_it = std::find_if( q_lambda.begin(), q_lambda.begin()+cols, std::bind2nd( std::not_equal_to(), et0)); if ( v_it != q_lambda.begin()+rows) { if ( ! vout4.verbose()) { std::cerr << std::endl << "basis-inverse check: "; } std::cerr << "failed ( row=" << v_it-q_lambda.begin() << " | col=" << col << " )" << std::endl; // return false; } v_it = q_x_O.begin(); for ( row = 0; row < cols; ++row, ++v_it) { if ( *v_it != ( row == col ? d : et0)) { if ( ! vout4.verbose()) { std::cerr << std::endl << "basis-inverse check: "; } std::cerr << "failed ( row=" << row+rows << " | col=" << col << " )" << std::endl; // return false; } } } return true; } // setting the pricing strategy template < class Rep_ > void QPE_solver:: set_pricing_strategy( Pricing_strategy& strategy) { CGAL_qpe_precondition( phase() != 1); CGAL_qpe_precondition( phase() != 2); strategyP = &strategy; if ( phase() != -1) strategyP->set( *this, vout2); } // diagnostic output // ----------------- template < class Rep_ > void QPE_solver:: set_verbosity( int verbose = 0, std::ostream& stream = std::cout) { vout = Verbose_ostream( verbose > 0, stream); vout1 = Verbose_ostream( verbose == 1, stream); vout2 = Verbose_ostream( verbose >= 2, stream); vout3 = Verbose_ostream( verbose >= 3, stream); vout4 = Verbose_ostream( verbose == 4, stream); } template < class Rep_ > void QPE_solver:: print_program( ) { int row, i; // objective function vout4.out() << std::endl << "objective function:" << std::endl; if ( is_QP) { vout4.out() << "--> output of MATRIX must go here <--"; vout4.out() << std::endl; } std::copy( qp_c, qp_c+qp_n, std::ostream_iterator( vout4.out(), " ")); vout4.out() << std::endl; vout4.out() << std::endl; // constraints vout4.out() << "constraints:" << std::endl; for ( row = 0; row < qp_m; ++row) { // original variables for ( i = 0; i < qp_n; ++i) vout4.out() << qp_A[ i][ row] << ' '; // slack variables if ( ! slack_A.empty()) { vout4.out() << " | "; for ( i = 0; i < (int)slack_A.size(); ++i) { vout4.out() << ( slack_A[ i].first != row ? " 0" : ( slack_A[ i].second ? "-1" : "+1")) << ' '; } } // artificial variables if ( ! art_A.empty()) { vout4.out() << " | "; for ( i = 0; i < (int)art_A.size(); ++i) { vout4.out() << ( art_A[ i].first != row ? " 0" : ( art_A[ i].second ? "-1" : "+1")) << ' '; } } if ( ! art_s.empty()) vout4.out() << " | " << art_s[ row] << ' '; // rhs vout4.out() << " | " << ( qp_r[ row] == Rep::EQUAL ? ' ' : ( qp_r[ row] == Rep::LESS_EQUAL ? '<' : '>')) << "= " << qp_b[ row] << std::endl; } } template < class Rep_ > void QPE_solver:: print_basis( ) { vout1 << " basis: "; vout2 << "basic variables" << ( has_ineq ? " " : "") << ": "; std::copy( B_O.begin(), B_O.end (), std::ostream_iterator( vout.out(), " ")); if ( vout2.verbose()) { if ( has_ineq && ( ! slack_A.empty())) { vout2.out() << " | "; std::copy( B_S.begin(), B_S.end(), std::ostream_iterator( vout2.out(), " ")); } if ( is_phaseI) { vout2.out() << " (artificial: " << art_basic << ')'; } if ( vout3.verbose()) { vout3.out() << "\nin_B: "; std::copy( in_B.begin(), in_B.end (), std::ostream_iterator( vout3.out(), " ")); } if ( has_ineq) { vout2.out() << std::endl << "basic constraints: "; std::copy( C.begin(), C.begin()+qp_m-slack_A.size(), std::ostream_iterator( vout2.out(), " ")); if ( ! slack_A.empty()) { vout2.out() << " | "; std::copy( C.begin()+qp_m-slack_A.size(), C.end(), std::ostream_iterator( vout2.out(), " ")); } if ( vout3.verbose()) { vout3.out() << "\nin_C: "; std::copy( in_C.begin(), in_C.end (), std::ostream_iterator( vout3.out(), " ")); } } } vout.out() << std::endl; vout4 << std::endl << "basis-inverse:" << std::endl; } template < class Rep_ > void QPE_solver:: print_solution( ) { if ( vout3.verbose()) { vout3.out() << std::endl << " b_C: "; std::copy( b_C.begin(), b_C.begin()+C.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl << " -c_B_O: "; std::copy( minus_c_B.begin(), minus_c_B.begin()+B_O.size(), std::ostream_iterator( vout3.out()," ")); vout3.out() << std::endl; } if ( vout2.verbose()) { vout2.out() << std::endl << " lambda: "; std::copy( lambda.begin(), lambda.begin()+C.size(), std::ostream_iterator( vout2.out(), " ")); vout2.out() << std::endl << " x_B_O: "; std::copy( x_B_O.begin(), x_B_O.begin()+B_O.size(), std::ostream_iterator( vout2.out(), " ")); vout2.out() << std::endl; if ( has_ineq) { vout2.out() << " x_B_S: "; std::copy( x_B_S.begin(), x_B_S.begin()+B_S.size(), std::ostream_iterator( vout2.out()," ")); vout2.out() << std::endl; } vout2.out() << std::endl; } Quotient s = solution(); vout1 << " "; vout.out() << "solution: " << s << " ~= " << to_double( s) << std::endl; vout2 << std::endl; } template < class Rep_ > const char* QPE_solver:: variable_type( int k) const { return ( k < qp_n ? "original" : ( k < (int)( qp_n+slack_A.size()) ? "slack" : "artificial")); } CGAL_END_NAMESPACE // ===== EOF ================================================================== /* really not needed in 'update_1'? - basis has already minimal size or || ( B_O.size()==C.size()) */