123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840 |
- /*-----------------------------------------------------------------------------
- | Copyright (c) 2013-2017, Nucleic Development Team.
- |
- | Distributed under the terms of the Modified BSD License.
- |
- | The full license is in the file COPYING.txt, distributed with this software.
- |----------------------------------------------------------------------------*/
- #pragma once
- #include <algorithm>
- #include <limits>
- #include <memory>
- #include <vector>
- #include "constraint.h"
- #include "errors.h"
- #include "expression.h"
- #include "maptype.h"
- #include "row.h"
- #include "symbol.h"
- #include "term.h"
- #include "util.h"
- #include "variable.h"
- namespace kiwi
- {
- namespace impl
- {
- class SolverImpl
- {
- friend class DebugHelper;
- struct Tag
- {
- Symbol marker;
- Symbol other;
- };
- struct EditInfo
- {
- Tag tag;
- Constraint constraint;
- double constant;
- };
- typedef MapType<Variable, Symbol>::Type VarMap;
- typedef MapType<Symbol, Row*>::Type RowMap;
- typedef MapType<Constraint, Tag>::Type CnMap;
- typedef MapType<Variable, EditInfo>::Type EditMap;
- struct DualOptimizeGuard
- {
- DualOptimizeGuard( SolverImpl& impl ) : m_impl( impl ) {}
- ~DualOptimizeGuard() { m_impl.dualOptimize(); }
- SolverImpl& m_impl;
- };
- public:
- SolverImpl() : m_objective( new Row() ), m_id_tick( 1 ) {}
- ~SolverImpl() { clearRows(); }
- /* Add a constraint to the solver.
- Throws
- ------
- DuplicateConstraint
- The given constraint has already been added to the solver.
- UnsatisfiableConstraint
- The given constraint is required and cannot be satisfied.
- */
- void addConstraint( const Constraint& constraint )
- {
- if( m_cns.find( constraint ) != m_cns.end() )
- throw DuplicateConstraint( constraint );
- // Creating a row causes symbols to be reserved for the variables
- // in the constraint. If this method exits with an exception,
- // then its possible those variables will linger in the var map.
- // Since its likely that those variables will be used in other
- // constraints and since exceptional conditions are uncommon,
- // i'm not too worried about aggressive cleanup of the var map.
- Tag tag;
- std::auto_ptr<Row> rowptr( createRow( constraint, tag ) );
- Symbol subject( chooseSubject( *rowptr, tag ) );
- // If chooseSubject could not find a valid entering symbol, one
- // last option is available if the entire row is composed of
- // dummy variables. If the constant of the row is zero, then
- // this represents redundant constraints and the new dummy
- // marker can enter the basis. If the constant is non-zero,
- // then it represents an unsatisfiable constraint.
- if( subject.type() == Symbol::Invalid && allDummies( *rowptr ) )
- {
- if( !nearZero( rowptr->constant() ) )
- throw UnsatisfiableConstraint( constraint );
- else
- subject = tag.marker;
- }
- // If an entering symbol still isn't found, then the row must
- // be added using an artificial variable. If that fails, then
- // the row represents an unsatisfiable constraint.
- if( subject.type() == Symbol::Invalid )
- {
- if( !addWithArtificialVariable( *rowptr ) )
- throw UnsatisfiableConstraint( constraint );
- }
- else
- {
- rowptr->solveFor( subject );
- substitute( subject, *rowptr );
- m_rows[ subject ] = rowptr.release();
- }
- m_cns[ constraint ] = tag;
- // Optimizing after each constraint is added performs less
- // aggregate work due to a smaller average system size. It
- // also ensures the solver remains in a consistent state.
- optimize( *m_objective );
- }
- /* Remove a constraint from the solver.
- Throws
- ------
- UnknownConstraint
- The given constraint has not been added to the solver.
- */
- void removeConstraint( const Constraint& constraint )
- {
- CnMap::iterator cn_it = m_cns.find( constraint );
- if( cn_it == m_cns.end() )
- throw UnknownConstraint( constraint );
- Tag tag( cn_it->second );
- m_cns.erase( cn_it );
- // Remove the error effects from the objective function
- // *before* pivoting, or substitutions into the objective
- // will lead to incorrect solver results.
- removeConstraintEffects( constraint, tag );
- // If the marker is basic, simply drop the row. Otherwise,
- // pivot the marker into the basis and then drop the row.
- RowMap::iterator row_it = m_rows.find( tag.marker );
- if( row_it != m_rows.end() )
- {
- std::auto_ptr<Row> rowptr( row_it->second );
- m_rows.erase( row_it );
- }
- else
- {
- row_it = getMarkerLeavingRow( tag.marker );
- if( row_it == m_rows.end() )
- throw InternalSolverError( "failed to find leaving row" );
- Symbol leaving( row_it->first );
- std::auto_ptr<Row> rowptr( row_it->second );
- m_rows.erase( row_it );
- rowptr->solveFor( leaving, tag.marker );
- substitute( tag.marker, *rowptr );
- }
- // Optimizing after each constraint is removed ensures that the
- // solver remains consistent. It makes the solver api easier to
- // use at a small tradeoff for speed.
- optimize( *m_objective );
- }
- /* Test whether a constraint has been added to the solver.
- */
- bool hasConstraint( const Constraint& constraint ) const
- {
- return m_cns.find( constraint ) != m_cns.end();
- }
- /* Add an edit variable to the solver.
- This method should be called before the `suggestValue` method is
- used to supply a suggested value for the given edit variable.
- Throws
- ------
- DuplicateEditVariable
- The given edit variable has already been added to the solver.
- BadRequiredStrength
- The given strength is >= required.
- */
- void addEditVariable( const Variable& variable, double strength )
- {
- if( m_edits.find( variable ) != m_edits.end() )
- throw DuplicateEditVariable( variable );
- strength = strength::clip( strength );
- if( strength == strength::required )
- throw BadRequiredStrength();
- Constraint cn( Expression( variable ), OP_EQ, strength );
- addConstraint( cn );
- EditInfo info;
- info.tag = m_cns[ cn ];
- info.constraint = cn;
- info.constant = 0.0;
- m_edits[ variable ] = info;
- }
- /* Remove an edit variable from the solver.
- Throws
- ------
- UnknownEditVariable
- The given edit variable has not been added to the solver.
- */
- void removeEditVariable( const Variable& variable )
- {
- EditMap::iterator it = m_edits.find( variable );
- if( it == m_edits.end() )
- throw UnknownEditVariable( variable );
- removeConstraint( it->second.constraint );
- m_edits.erase( it );
- }
- /* Test whether an edit variable has been added to the solver.
- */
- bool hasEditVariable( const Variable& variable ) const
- {
- return m_edits.find( variable ) != m_edits.end();
- }
- /* Suggest a value for the given edit variable.
- This method should be used after an edit variable as been added to
- the solver in order to suggest the value for that variable.
- Throws
- ------
- UnknownEditVariable
- The given edit variable has not been added to the solver.
- */
- void suggestValue( const Variable& variable, double value )
- {
- EditMap::iterator it = m_edits.find( variable );
- if( it == m_edits.end() )
- throw UnknownEditVariable( variable );
- DualOptimizeGuard guard( *this );
- EditInfo& info = it->second;
- double delta = value - info.constant;
- info.constant = value;
- // Check first if the positive error variable is basic.
- RowMap::iterator row_it = m_rows.find( info.tag.marker );
- if( row_it != m_rows.end() )
- {
- if( row_it->second->add( -delta ) < 0.0 )
- m_infeasible_rows.push_back( row_it->first );
- return;
- }
- // Check next if the negative error variable is basic.
- row_it = m_rows.find( info.tag.other );
- if( row_it != m_rows.end() )
- {
- if( row_it->second->add( delta ) < 0.0 )
- m_infeasible_rows.push_back( row_it->first );
- return;
- }
- // Otherwise update each row where the error variables exist.
- RowMap::iterator end = m_rows.end();
- for( row_it = m_rows.begin(); row_it != end; ++row_it )
- {
- double coeff = row_it->second->coefficientFor( info.tag.marker );
- if( coeff != 0.0 &&
- row_it->second->add( delta * coeff ) < 0.0 &&
- row_it->first.type() != Symbol::External )
- m_infeasible_rows.push_back( row_it->first );
- }
- }
- /* Update the values of the external solver variables.
- */
- void updateVariables()
- {
- typedef RowMap::iterator row_iter_t;
- typedef VarMap::iterator var_iter_t;
- row_iter_t row_end = m_rows.end();
- var_iter_t var_end = m_vars.end();
- for( var_iter_t var_it = m_vars.begin(); var_it != var_end; ++var_it )
- {
- Variable& var( const_cast<Variable&>( var_it->first ) );
- row_iter_t row_it = m_rows.find( var_it->second );
- if( row_it == row_end )
- var.setValue( 0.0 );
- else
- var.setValue( row_it->second->constant() );
- }
- }
- /* Reset the solver to the empty starting condition.
- This method resets the internal solver state to the empty starting
- condition, as if no constraints or edit variables have been added.
- This can be faster than deleting the solver and creating a new one
- when the entire system must change, since it can avoid unecessary
- heap (de)allocations.
- */
- void reset()
- {
- clearRows();
- m_cns.clear();
- m_vars.clear();
- m_edits.clear();
- m_infeasible_rows.clear();
- m_objective.reset( new Row() );
- m_artificial.reset();
- m_id_tick = 1;
- }
- private:
- SolverImpl( const SolverImpl& );
- SolverImpl& operator=( const SolverImpl& );
- struct RowDeleter
- {
- template<typename T>
- void operator()( T& pair ) { delete pair.second; }
- };
- void clearRows()
- {
- std::for_each( m_rows.begin(), m_rows.end(), RowDeleter() );
- m_rows.clear();
- }
- /* Get the symbol for the given variable.
- If a symbol does not exist for the variable, one will be created.
- */
- Symbol getVarSymbol( const Variable& variable )
- {
- VarMap::iterator it = m_vars.find( variable );
- if( it != m_vars.end() )
- return it->second;
- Symbol symbol( Symbol::External, m_id_tick++ );
- m_vars[ variable ] = symbol;
- return symbol;
- }
- /* Create a new Row object for the given constraint.
- The terms in the constraint will be converted to cells in the row.
- Any term in the constraint with a coefficient of zero is ignored.
- This method uses the `getVarSymbol` method to get the symbol for
- the variables added to the row. If the symbol for a given cell
- variable is basic, the cell variable will be substituted with the
- basic row.
- The necessary slack and error variables will be added to the row.
- If the constant for the row is negative, the sign for the row
- will be inverted so the constant becomes positive.
- The tag will be updated with the marker and error symbols to use
- for tracking the movement of the constraint in the tableau.
- */
- Row* createRow( const Constraint& constraint, Tag& tag )
- {
- typedef std::vector<Term>::const_iterator iter_t;
- const Expression& expr( constraint.expression() );
- Row* row = new Row( expr.constant() );
- // Substitute the current basic variables into the row.
- iter_t end = expr.terms().end();
- for( iter_t it = expr.terms().begin(); it != end; ++it )
- {
- if( !nearZero( it->coefficient() ) )
- {
- Symbol symbol( getVarSymbol( it->variable() ) );
- RowMap::const_iterator row_it = m_rows.find( symbol );
- if( row_it != m_rows.end() )
- row->insert( *row_it->second, it->coefficient() );
- else
- row->insert( symbol, it->coefficient() );
- }
- }
- // Add the necessary slack, error, and dummy variables.
- switch( constraint.op() )
- {
- case OP_LE:
- case OP_GE:
- {
- double coeff = constraint.op() == OP_LE ? 1.0 : -1.0;
- Symbol slack( Symbol::Slack, m_id_tick++ );
- tag.marker = slack;
- row->insert( slack, coeff );
- if( constraint.strength() < strength::required )
- {
- Symbol error( Symbol::Error, m_id_tick++ );
- tag.other = error;
- row->insert( error, -coeff );
- m_objective->insert( error, constraint.strength() );
- }
- break;
- }
- case OP_EQ:
- {
- if( constraint.strength() < strength::required )
- {
- Symbol errplus( Symbol::Error, m_id_tick++ );
- Symbol errminus( Symbol::Error, m_id_tick++ );
- tag.marker = errplus;
- tag.other = errminus;
- row->insert( errplus, -1.0 ); // v = eplus - eminus
- row->insert( errminus, 1.0 ); // v - eplus + eminus = 0
- m_objective->insert( errplus, constraint.strength() );
- m_objective->insert( errminus, constraint.strength() );
- }
- else
- {
- Symbol dummy( Symbol::Dummy, m_id_tick++ );
- tag.marker = dummy;
- row->insert( dummy );
- }
- break;
- }
- }
- // Ensure the row as a positive constant.
- if( row->constant() < 0.0 )
- row->reverseSign();
- return row;
- }
- /* Choose the subject for solving for the row.
- This method will choose the best subject for using as the solve
- target for the row. An invalid symbol will be returned if there
- is no valid target.
- The symbols are chosen according to the following precedence:
- 1) The first symbol representing an external variable.
- 2) A negative slack or error tag variable.
- If a subject cannot be found, an invalid symbol will be returned.
- */
- Symbol chooseSubject( const Row& row, const Tag& tag )
- {
- typedef Row::CellMap::const_iterator iter_t;
- iter_t end = row.cells().end();
- for( iter_t it = row.cells().begin(); it != end; ++it )
- {
- if( it->first.type() == Symbol::External )
- return it->first;
- }
- if( tag.marker.type() == Symbol::Slack || tag.marker.type() == Symbol::Error )
- {
- if( row.coefficientFor( tag.marker ) < 0.0 )
- return tag.marker;
- }
- if( tag.other.type() == Symbol::Slack || tag.other.type() == Symbol::Error )
- {
- if( row.coefficientFor( tag.other ) < 0.0 )
- return tag.other;
- }
- return Symbol();
- }
- /* Add the row to the tableau using an artificial variable.
- This will return false if the constraint cannot be satisfied.
- */
- bool addWithArtificialVariable( const Row& row )
- {
- // Create and add the artificial variable to the tableau
- Symbol art( Symbol::Slack, m_id_tick++ );
- m_rows[ art ] = new Row( row );
- m_artificial.reset( new Row( row ) );
- // Optimize the artificial objective. This is successful
- // only if the artificial objective is optimized to zero.
- optimize( *m_artificial );
- bool success = nearZero( m_artificial->constant() );
- m_artificial.reset();
- // If the artificial variable is not basic, pivot the row so that
- // it becomes basic. If the row is constant, exit early.
- RowMap::iterator it = m_rows.find( art );
- if( it != m_rows.end() )
- {
- std::auto_ptr<Row> rowptr( it->second );
- m_rows.erase( it );
- if( rowptr->cells().empty() )
- return success;
- Symbol entering( anyPivotableSymbol( *rowptr ) );
- if( entering.type() == Symbol::Invalid )
- return false; // unsatisfiable (will this ever happen?)
- rowptr->solveFor( art, entering );
- substitute( entering, *rowptr );
- m_rows[ entering ] = rowptr.release();
- }
- // Remove the artificial variable from the tableau.
- RowMap::iterator end = m_rows.end();
- for( it = m_rows.begin(); it != end; ++it )
- it->second->remove( art );
- m_objective->remove( art );
- return success;
- }
- /* Substitute the parametric symbol with the given row.
- This method will substitute all instances of the parametric symbol
- in the tableau and the objective function with the given row.
- */
- void substitute( const Symbol& symbol, const Row& row )
- {
- typedef RowMap::iterator iter_t;
- iter_t end = m_rows.end();
- for( iter_t it = m_rows.begin(); it != end; ++it )
- {
- it->second->substitute( symbol, row );
- if( it->first.type() != Symbol::External &&
- it->second->constant() < 0.0 )
- m_infeasible_rows.push_back( it->first );
- }
- m_objective->substitute( symbol, row );
- if( m_artificial.get() )
- m_artificial->substitute( symbol, row );
- }
- /* Optimize the system for the given objective function.
- This method performs iterations of Phase 2 of the simplex method
- until the objective function reaches a minimum.
- Throws
- ------
- InternalSolverError
- The value of the objective function is unbounded.
- */
- void optimize( const Row& objective )
- {
- while( true )
- {
- Symbol entering( getEnteringSymbol( objective ) );
- if( entering.type() == Symbol::Invalid )
- return;
- RowMap::iterator it = getLeavingRow( entering );
- if( it == m_rows.end() )
- throw InternalSolverError( "The objective is unbounded." );
- // pivot the entering symbol into the basis
- Symbol leaving( it->first );
- Row* row = it->second;
- m_rows.erase( it );
- row->solveFor( leaving, entering );
- substitute( entering, *row );
- m_rows[ entering ] = row;
- }
- }
- /* Optimize the system using the dual of the simplex method.
- The current state of the system should be such that the objective
- function is optimal, but not feasible. This method will perform
- an iteration of the dual simplex method to make the solution both
- optimal and feasible.
- Throws
- ------
- InternalSolverError
- The system cannot be dual optimized.
- */
- void dualOptimize()
- {
- while( !m_infeasible_rows.empty() )
- {
- Symbol leaving( m_infeasible_rows.back() );
- m_infeasible_rows.pop_back();
- RowMap::iterator it = m_rows.find( leaving );
- if( it != m_rows.end() && !nearZero( it->second->constant() ) &&
- it->second->constant() < 0.0 )
- {
- Symbol entering( getDualEnteringSymbol( *it->second ) );
- if( entering.type() == Symbol::Invalid )
- throw InternalSolverError( "Dual optimize failed." );
- // pivot the entering symbol into the basis
- Row* row = it->second;
- m_rows.erase( it );
- row->solveFor( leaving, entering );
- substitute( entering, *row );
- m_rows[ entering ] = row;
- }
- }
- }
- /* Compute the entering variable for a pivot operation.
- This method will return first symbol in the objective function which
- is non-dummy and has a coefficient less than zero. If no symbol meets
- the criteria, it means the objective function is at a minimum, and an
- invalid symbol is returned.
- */
- Symbol getEnteringSymbol( const Row& objective )
- {
- typedef Row::CellMap::const_iterator iter_t;
- iter_t end = objective.cells().end();
- for( iter_t it = objective.cells().begin(); it != end; ++it )
- {
- if( it->first.type() != Symbol::Dummy && it->second < 0.0 )
- return it->first;
- }
- return Symbol();
- }
- /* Compute the entering symbol for the dual optimize operation.
- This method will return the symbol in the row which has a positive
- coefficient and yields the minimum ratio for its respective symbol
- in the objective function. The provided row *must* be infeasible.
- If no symbol is found which meats the criteria, an invalid symbol
- is returned.
- */
- Symbol getDualEnteringSymbol( const Row& row )
- {
- typedef Row::CellMap::const_iterator iter_t;
- Symbol entering;
- double ratio = std::numeric_limits<double>::max();
- iter_t end = row.cells().end();
- for( iter_t it = row.cells().begin(); it != end; ++it )
- {
- if( it->second > 0.0 && it->first.type() != Symbol::Dummy )
- {
- double coeff = m_objective->coefficientFor( it->first );
- double r = coeff / it->second;
- if( r < ratio )
- {
- ratio = r;
- entering = it->first;
- }
- }
- }
- return entering;
- }
- /* Get the first Slack or Error symbol in the row.
- If no such symbol is present, and Invalid symbol will be returned.
- */
- Symbol anyPivotableSymbol( const Row& row )
- {
- typedef Row::CellMap::const_iterator iter_t;
- iter_t end = row.cells().end();
- for( iter_t it = row.cells().begin(); it != end; ++it )
- {
- const Symbol& sym( it->first );
- if( sym.type() == Symbol::Slack || sym.type() == Symbol::Error )
- return sym;
- }
- return Symbol();
- }
- /* Compute the row which holds the exit symbol for a pivot.
- This method will return an iterator to the row in the row map
- which holds the exit symbol. If no appropriate exit symbol is
- found, the end() iterator will be returned. This indicates that
- the objective function is unbounded.
- */
- RowMap::iterator getLeavingRow( const Symbol& entering )
- {
- typedef RowMap::iterator iter_t;
- double ratio = std::numeric_limits<double>::max();
- iter_t end = m_rows.end();
- iter_t found = m_rows.end();
- for( iter_t it = m_rows.begin(); it != end; ++it )
- {
- if( it->first.type() != Symbol::External )
- {
- double temp = it->second->coefficientFor( entering );
- if( temp < 0.0 )
- {
- double temp_ratio = -it->second->constant() / temp;
- if( temp_ratio < ratio )
- {
- ratio = temp_ratio;
- found = it;
- }
- }
- }
- }
- return found;
- }
- /* Compute the leaving row for a marker variable.
- This method will return an iterator to the row in the row map
- which holds the given marker variable. The row will be chosen
- according to the following precedence:
- 1) The row with a restricted basic varible and a negative coefficient
- for the marker with the smallest ratio of -constant / coefficient.
- 2) The row with a restricted basic variable and the smallest ratio
- of constant / coefficient.
- 3) The last unrestricted row which contains the marker.
- If the marker does not exist in any row, the row map end() iterator
- will be returned. This indicates an internal solver error since
- the marker *should* exist somewhere in the tableau.
- */
- RowMap::iterator getMarkerLeavingRow( const Symbol& marker )
- {
- const double dmax = std::numeric_limits<double>::max();
- typedef RowMap::iterator iter_t;
- double r1 = dmax;
- double r2 = dmax;
- iter_t end = m_rows.end();
- iter_t first = end;
- iter_t second = end;
- iter_t third = end;
- for( iter_t it = m_rows.begin(); it != end; ++it )
- {
- double c = it->second->coefficientFor( marker );
- if( c == 0.0 )
- continue;
- if( it->first.type() == Symbol::External )
- {
- third = it;
- }
- else if( c < 0.0 )
- {
- double r = -it->second->constant() / c;
- if( r < r1 )
- {
- r1 = r;
- first = it;
- }
- }
- else
- {
- double r = it->second->constant() / c;
- if( r < r2 )
- {
- r2 = r;
- second = it;
- }
- }
- }
- if( first != end )
- return first;
- if( second != end )
- return second;
- return third;
- }
- /* Remove the effects of a constraint on the objective function.
- */
- void removeConstraintEffects( const Constraint& cn, const Tag& tag )
- {
- if( tag.marker.type() == Symbol::Error )
- removeMarkerEffects( tag.marker, cn.strength() );
- if( tag.other.type() == Symbol::Error )
- removeMarkerEffects( tag.other, cn.strength() );
- }
- /* Remove the effects of an error marker on the objective function.
- */
- void removeMarkerEffects( const Symbol& marker, double strength )
- {
- RowMap::iterator row_it = m_rows.find( marker );
- if( row_it != m_rows.end() )
- m_objective->insert( *row_it->second, -strength );
- else
- m_objective->insert( marker, -strength );
- }
- /* Test whether a row is composed of all dummy variables.
- */
- bool allDummies( const Row& row )
- {
- typedef Row::CellMap::const_iterator iter_t;
- iter_t end = row.cells().end();
- for( iter_t it = row.cells().begin(); it != end; ++it )
- {
- if( it->first.type() != Symbol::Dummy )
- return false;
- }
- return true;
- }
- CnMap m_cns;
- RowMap m_rows;
- VarMap m_vars;
- EditMap m_edits;
- std::vector<Symbol> m_infeasible_rows;
- std::auto_ptr<Row> m_objective;
- std::auto_ptr<Row> m_artificial;
- Symbol::Id m_id_tick;
- };
- } // namespace impl
- } // namespace kiwi
|