/* $Id$ */
// Copyright (C) 2002, International Business Machines
// Corporation and others. All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).
#ifndef ClpPackedMatrix_H
#define ClpPackedMatrix_H
#include "CoinPragma.hpp"
#include "ClpMatrixBase.hpp"
#include "ClpPrimalColumnSteepest.hpp"
/** This implements CoinPackedMatrix as derived from ClpMatrixBase.
It adds a few methods that know about model as well as matrix
For details see CoinPackedMatrix */
class ClpPackedMatrix2;
class ClpPackedMatrix3;
class CoinDoubleArrayWithLength;
class ClpPackedMatrix : public ClpMatrixBase {
public:
/**@name Useful methods */
//@{
/// Return a complete CoinPackedMatrix
virtual CoinPackedMatrix *getPackedMatrix() const
{
return matrix_;
}
/** Whether the packed matrix is column major ordered or not. */
virtual bool isColOrdered() const
{
return matrix_->isColOrdered();
}
/** Number of entries in the packed matrix. */
virtual CoinBigIndex getNumElements() const
{
return matrix_->getNumElements();
}
/** Number of columns. */
virtual int getNumCols() const
{
return matrix_->getNumCols();
}
/** Number of rows. */
virtual int getNumRows() const
{
return matrix_->getNumRows();
}
/** A vector containing the elements in the packed matrix. Note that there
might be gaps in this list, entries that do not belong to any
major-dimension vector. To get the actual elements one should look at
this vector together with vectorStarts and vectorLengths. */
virtual const double *getElements() const
{
return matrix_->getElements();
}
/// Mutable elements
inline double *getMutableElements() const
{
return matrix_->getMutableElements();
}
/** A vector containing the minor indices of the elements in the packed
matrix. Note that there might be gaps in this list, entries that do not
belong to any major-dimension vector. To get the actual elements one
should look at this vector together with vectorStarts and
vectorLengths. */
virtual const int *getIndices() const
{
return matrix_->getIndices();
}
virtual const CoinBigIndex *getVectorStarts() const
{
return matrix_->getVectorStarts();
}
/** The lengths of the major-dimension vectors. */
virtual const int *getVectorLengths() const
{
return matrix_->getVectorLengths();
}
/** The length of a single major-dimension vector. */
virtual int getVectorLength(int index) const
{
return matrix_->getVectorSize(index);
}
/** Delete the columns whose indices are listed in indDel
. */
virtual void deleteCols(const int numDel, const int *indDel);
/** Delete the rows whose indices are listed in indDel
. */
virtual void deleteRows(const int numDel, const int *indDel);
#ifndef CLP_NO_VECTOR
/// Append Columns
virtual void appendCols(int number, const CoinPackedVectorBase *const *columns);
/// Append Rows
virtual void appendRows(int number, const CoinPackedVectorBase *const *rows);
#endif
/** Append a set of rows/columns to the end of the matrix. Returns number of errors
i.e. if any of the new rows/columns contain an index that's larger than the
number of columns-1/rows-1 (if numberOther>0) or duplicates
If 0 then rows, 1 if columns */
virtual int appendMatrix(int number, int type,
const CoinBigIndex *starts, const int *index,
const double *element, int numberOther = -1);
/** Replace the elements of a vector. The indices remain the same.
This is only needed if scaling and a row copy is used.
At most the number specified will be replaced.
The index is between 0 and major dimension of matrix */
virtual void replaceVector(const int index,
const int numReplace, const double *newElements)
{
matrix_->replaceVector(index, numReplace, newElements);
}
/** Modify one element of packed matrix. An element may be added.
This works for either ordering If the new element is zero it will be
deleted unless keepZero true */
virtual void modifyCoefficient(int row, int column, double newElement,
bool keepZero = false)
{
matrix_->modifyCoefficient(row, column, newElement, keepZero);
}
/** Returns a new matrix in reverse order without gaps */
virtual ClpMatrixBase *reverseOrderedCopy() const;
/// Returns number of elements in column part of basis
virtual int countBasis(const int *whichColumn,
int &numberColumnBasic);
/// Fills in column part of basis
virtual void fillBasis(ClpSimplex *model,
const int *whichColumn,
int &numberColumnBasic,
int *row, int *start,
int *rowCount, int *columnCount,
CoinFactorizationDouble *element);
/** Creates scales for column copy (rowCopy in model may be modified)
returns non-zero if no scaling done */
virtual int scale(ClpModel *model, ClpSimplex *simplex = NULL) const;
/** Scales rowCopy if column copy scaled
Only called if scales already exist */
virtual void scaleRowCopy(ClpModel *model) const;
/// Creates scaled column copy if scales exist
void createScaledMatrix(ClpSimplex *model) const;
/** Realy really scales column copy
Only called if scales already exist.
Up to user ro delete */
virtual ClpMatrixBase *scaledColumnCopy(ClpModel *model) const;
/** Checks if all elements are in valid range. Can just
return true if you are not paranoid. For Clp I will
probably expect no zeros. Code can modify matrix to get rid of
small elements.
check bits (can be turned off to save time) :
1 - check if matrix has gaps
2 - check if zero elements
4 - check and compress duplicates
8 - report on large and small
*/
virtual bool allElementsInRange(ClpModel *model,
double smallest, double largest,
int check = 15);
/** Returns largest and smallest elements of both signs.
Largest refers to largest absolute value.
*/
virtual void rangeOfElements(double &smallestNegative, double &largestNegative,
double &smallestPositive, double &largestPositive);
/** Unpacks a column into an CoinIndexedvector
*/
virtual void unpack(const ClpSimplex *model, CoinIndexedVector *rowArray,
int column) const;
/** Unpacks a column into an CoinIndexedvector
** in packed foramt
Note that model is NOT const. Bounds and objective could
be modified if doing column generation (just for this variable) */
virtual void unpackPacked(ClpSimplex *model,
CoinIndexedVector *rowArray,
int column) const;
/** Adds multiple of a column into an CoinIndexedvector
You can use quickAdd to add to vector */
virtual void add(const ClpSimplex *model, CoinIndexedVector *rowArray,
int column, double multiplier) const;
/** Adds multiple of a column into an array */
virtual void add(const ClpSimplex *model, double *array,
int column, double multiplier) const;
/// Allow any parts of a created CoinPackedMatrix to be deleted
virtual void releasePackedMatrix() const {}
/** Given positive integer weights for each row fills in sum of weights
for each column (and slack).
Returns weights vector
*/
virtual CoinBigIndex *dubiousWeights(const ClpSimplex *model, int *inputWeights) const;
/// Says whether it can do partial pricing
virtual bool canDoPartialPricing() const;
/// Partial pricing
virtual void partialPricing(ClpSimplex *model, double start, double end,
int &bestSequence, int &numberWanted);
/// makes sure active columns correct
virtual int refresh(ClpSimplex *model);
// Really scale matrix
virtual void reallyScale(const double *rowScale, const double *columnScale);
/** Set the dimensions of the matrix. In effect, append new empty
columns/rows to the matrix. A negative number for either dimension
means that that dimension doesn't change. Otherwise the new dimensions
MUST be at least as large as the current ones otherwise an exception
is thrown. */
virtual void setDimensions(int numrows, int numcols);
//@}
/**@name Matrix times vector methods */
//@{
/** Return y + A * scalar *x
in y
.
@pre x
must be of size numColumns()
@pre y
must be of size numRows()
*/
virtual void times(double scalar,
const double *x, double *y) const;
/// And for scaling
virtual void times(double scalar,
const double *x, double *y,
const double *rowScale,
const double *columnScale) const;
/** Return y + x * scalar * A
in y
.
@pre x
must be of size numRows()
@pre y
must be of size numColumns()
*/
virtual void transposeTimes(double scalar,
const double *x, double *y) const;
/// And for scaling
virtual void transposeTimes(double scalar,
const double *x, double *y,
const double *rowScale,
const double *columnScale,
double *spare = NULL) const;
/** Return y - pi * A
in y
.
@pre pi
must be of size numRows()
@pre y
must be of size numColumns()
This just does subset (but puts in correct place in y) */
void transposeTimesSubset(int number,
const int *which,
const double *pi, double *y,
const double *rowScale,
const double *columnScale,
double *spare = NULL) const;
/** Return x * scalar * A in z
.
Can use y as temporary array (will be empty at end)
Note - If x packed mode - then z packed mode
Squashes small elements and knows about ClpSimplex */
virtual void transposeTimes(const ClpSimplex *model, double scalar,
const CoinIndexedVector *x,
CoinIndexedVector *y,
CoinIndexedVector *z) const;
/** Return x * scalar * A in z
.
Note - If x packed mode - then z packed mode
This does by column and knows no gaps
Squashes small elements and knows about ClpSimplex */
void transposeTimesByColumn(const ClpSimplex *model, double scalar,
const CoinIndexedVector *x,
CoinIndexedVector *y,
CoinIndexedVector *z) const;
/** Return x * scalar * A in z
.
Can use y as temporary array (will be empty at end)
Note - If x packed mode - then z packed mode
Squashes small elements and knows about ClpSimplex.
This version uses row copy*/
virtual void transposeTimesByRow(const ClpSimplex *model, double scalar,
const CoinIndexedVector *x,
CoinIndexedVector *y,
CoinIndexedVector *z) const;
/** Return x *A
in z
but
just for indices in y.
Note - z always packed mode */
virtual void subsetTransposeTimes(const ClpSimplex *model,
const CoinIndexedVector *x,
const CoinIndexedVector *y,
CoinIndexedVector *z) const;
/** Returns true if can combine transposeTimes and subsetTransposeTimes
and if it would be faster */
virtual bool canCombine(const ClpSimplex *model,
const CoinIndexedVector *pi) const;
/** Updates two arrays for steepest and does devex weights
Returns nonzero if updates reduced cost and infeas -
new infeas in dj1 */
virtual int transposeTimes2(const ClpSimplex *model,
const CoinIndexedVector *pi1, CoinIndexedVector *dj1,
const CoinIndexedVector *pi2,
CoinIndexedVector *spare,
double *infeas, double *reducedCost,
double referenceIn, double devex,
// Array for exact devex to say what is in reference framework
unsigned int *reference,
double *weights, double scaleFactor);
/// Updates second array for steepest and does devex weights
virtual void subsetTimes2(const ClpSimplex *model,
CoinIndexedVector *dj1,
const CoinIndexedVector *pi2, CoinIndexedVector *dj2,
double referenceIn, double devex,
// Array for exact devex to say what is in reference framework
unsigned int *reference,
double *weights, double scaleFactor);
/// Sets up an effective RHS
void useEffectiveRhs(ClpSimplex *model);
#if COIN_LONG_WORK
// For long double versions
virtual void times(CoinWorkDouble scalar,
const CoinWorkDouble *x, CoinWorkDouble *y) const;
virtual void transposeTimes(CoinWorkDouble scalar,
const CoinWorkDouble *x, CoinWorkDouble *y) const;
#endif
//@}
/**@name Other */
//@{
/// Returns CoinPackedMatrix (non const)
inline CoinPackedMatrix *matrix() const
{
return matrix_;
}
/** Just sets matrix_ to NULL so it can be used elsewhere.
used in GUB
*/
inline void setMatrixNull()
{
matrix_ = NULL;
}
/// Say we want special column copy
inline void makeSpecialColumnCopy()
{
flags_ |= 16;
}
/// Say we don't want special column copy
void releaseSpecialColumnCopy();
/// Are there zeros?
inline bool zeros() const
{
return ((flags_ & 1) != 0);
}
/// Do we want special column copy
inline bool wantsSpecialColumnCopy() const
{
return ((flags_ & 16) != 0);
}
/// Flags
inline int flags() const
{
return flags_;
}
/// Sets flags_ correctly
inline void checkGaps()
{
flags_ = (matrix_->hasGaps()) ? (flags_ | 2) : (flags_ & (~2));
}
/// number of active columns (normally same as number of columns)
inline int numberActiveColumns() const
{
return numberActiveColumns_;
}
/// Set number of active columns (normally same as number of columns)
inline void setNumberActiveColumns(int value)
{
numberActiveColumns_ = value;
}
//@}
/**@name Constructors, destructor */
//@{
/** Default constructor. */
ClpPackedMatrix();
/** Destructor */
virtual ~ClpPackedMatrix();
//@}
/**@name Copy method */
//@{
/** The copy constructor. */
ClpPackedMatrix(const ClpPackedMatrix &);
/** The copy constructor from an CoinPackedMatrix. */
ClpPackedMatrix(const CoinPackedMatrix &);
/** Subset constructor (without gaps). Duplicates are allowed
and order is as given */
ClpPackedMatrix(const ClpPackedMatrix &wholeModel,
int numberRows, const int *whichRows,
int numberColumns, const int *whichColumns);
ClpPackedMatrix(const CoinPackedMatrix &wholeModel,
int numberRows, const int *whichRows,
int numberColumns, const int *whichColumns);
/** This takes over ownership (for space reasons) */
ClpPackedMatrix(CoinPackedMatrix *matrix);
ClpPackedMatrix &operator=(const ClpPackedMatrix &);
/// Clone
virtual ClpMatrixBase *clone() const;
/// Copy contents - resizing if necessary - otherwise re-use memory
virtual void copy(const ClpPackedMatrix *from);
/** Subset clone (without gaps). Duplicates are allowed
and order is as given */
virtual ClpMatrixBase *subsetClone(
int numberRows, const int *whichRows,
int numberColumns, const int *whichColumns) const;
/// make special row copy
void specialRowCopy(ClpSimplex *model, const ClpMatrixBase *rowCopy);
/// make special column copy
void specialColumnCopy(ClpSimplex *model);
/// Correct sequence in and out to give true value
virtual void correctSequence(const ClpSimplex *model, int &sequenceIn, int &sequenceOut);
//@}
private:
/// Meat of transposeTimes by column when not scaled
int gutsOfTransposeTimesUnscaled(const double *COIN_RESTRICT pi,
int *COIN_RESTRICT index,
double *COIN_RESTRICT array,
const double tolerance) const;
/// Meat of transposeTimes by column when scaled
int gutsOfTransposeTimesScaled(const double *COIN_RESTRICT pi,
const double *COIN_RESTRICT columnScale,
int *COIN_RESTRICT index,
double *COIN_RESTRICT array,
const double tolerance) const;
/// Meat of transposeTimes by column when not scaled and skipping
int gutsOfTransposeTimesUnscaled(const double *COIN_RESTRICT pi,
int *COIN_RESTRICT index,
double *COIN_RESTRICT array,
const unsigned char *status,
const double tolerance) const;
/** Meat of transposeTimes by column when not scaled and skipping
and doing part of dualColumn */
int gutsOfTransposeTimesUnscaled(const double *COIN_RESTRICT pi,
int *COIN_RESTRICT index,
double *COIN_RESTRICT array,
const unsigned char *status,
int *COIN_RESTRICT spareIndex,
double *COIN_RESTRICT spareArray,
const double *COIN_RESTRICT reducedCost,
double &upperTheta,
double acceptablePivot,
double dualTolerance,
int &numberRemaining,
const double zeroTolerance) const;
/// Meat of transposeTimes by column when scaled and skipping
int gutsOfTransposeTimesScaled(const double *COIN_RESTRICT pi,
const double *COIN_RESTRICT columnScale,
int *COIN_RESTRICT index,
double *COIN_RESTRICT array,
const unsigned char *status,
const double tolerance) const;
/// Meat of transposeTimes by row n > K if packed - returns number nonzero
int gutsOfTransposeTimesByRowGEK(const CoinIndexedVector *COIN_RESTRICT piVector,
int *COIN_RESTRICT index,
double *COIN_RESTRICT output,
int numberColumns,
const double tolerance,
const double scalar) const;
/// Meat of transposeTimes by row n > 2 if packed - returns number nonzero
int gutsOfTransposeTimesByRowGE3(const CoinIndexedVector *COIN_RESTRICT piVector,
int *COIN_RESTRICT index,
double *COIN_RESTRICT output,
double *COIN_RESTRICT array2,
const double tolerance,
const double scalar) const;
/// Meat of transposeTimes by row n > 2 if packed - returns number nonzero
int gutsOfTransposeTimesByRowGE3a(const CoinIndexedVector *COIN_RESTRICT piVector,
int *COIN_RESTRICT index,
double *COIN_RESTRICT output,
int *COIN_RESTRICT lookup,
char *COIN_RESTRICT marked,
const double tolerance,
const double scalar) const;
/// Meat of transposeTimes by row n == 2 if packed
void gutsOfTransposeTimesByRowEQ2(const CoinIndexedVector *piVector, CoinIndexedVector *output,
CoinIndexedVector *spareVector, const double tolerance, const double scalar) const;
/// Meat of transposeTimes by row n == 1 if packed
void gutsOfTransposeTimesByRowEQ1(const CoinIndexedVector *piVector, CoinIndexedVector *output,
const double tolerance, const double scalar) const;
/// Gets rid of special copies
void clearCopies();
protected:
/// Check validity
void checkFlags(int type) const;
/**@name Data members
The data members are protected to allow access for derived classes. */
//@{
/// Data
CoinPackedMatrix *matrix_;
/// number of active columns (normally same as number of columns)
int numberActiveColumns_;
/** Flags -
1 - has zero elements
2 - has gaps
4 - has special row copy
8 - has special column copy
16 - wants special column copy
*/
mutable int flags_;
/// Special row copy
ClpPackedMatrix2 *rowCopy_;
/// Special column copy
ClpPackedMatrix3 *columnCopy_;
//@}
};
#ifdef THREAD
#include
typedef struct {
double acceptablePivot;
const ClpSimplex *model;
double *spare;
int *spareIndex;
double *arrayTemp;
int *indexTemp;
int *numberInPtr;
//double * bestPossiblePtr;
double *upperThetaPtr;
int *posFreePtr;
double *freePivotPtr;
int *numberOutPtr;
const unsigned short *count;
const double *pi;
const CoinBigIndex *rowStart;
const double *element;
const unsigned short *column;
int offset;
int numberInRowArray;
int numberLook;
} dualColumn0Struct;
#endif
class ClpPackedMatrix2 {
public:
/**@name Useful methods */
//@{
/** Return x * -1 * A in z
.
Note - x packed and z will be packed mode
Squashes small elements and knows about ClpSimplex */
void transposeTimes(const ClpSimplex *model,
const CoinPackedMatrix *rowCopy,
const CoinIndexedVector *x,
CoinIndexedVector *spareArray,
CoinIndexedVector *z) const;
/// Returns true if copy has useful information
inline bool usefulInfo() const
{
return rowStart_ != NULL;
}
//@}
/**@name Constructors, destructor */
//@{
/** Default constructor. */
ClpPackedMatrix2();
/** Constructor from copy. */
ClpPackedMatrix2(ClpSimplex *model, const CoinPackedMatrix *rowCopy);
/** Destructor */
virtual ~ClpPackedMatrix2();
//@}
/**@name Copy method */
//@{
/** The copy constructor. */
ClpPackedMatrix2(const ClpPackedMatrix2 &);
ClpPackedMatrix2 &operator=(const ClpPackedMatrix2 &);
//@}
protected:
/**@name Data members
The data members are protected to allow access for derived classes. */
//@{
/// Number of blocks
int numberBlocks_;
/// Number of rows
int numberRows_;
/// Column offset for each block (plus one at end)
int *offset_;
/// Counts of elements in each part of row
mutable unsigned short *count_;
/// Row starts
mutable CoinBigIndex *rowStart_;
/// columns within block
unsigned short *column_;
/// work arrays
double *work_;
#ifdef THREAD
pthread_t *threadId_;
dualColumn0Struct *info_;
#endif
//@}
};
typedef struct {
CoinBigIndex startElements_; // point to data
CoinBigIndex startRows_; // point to data later
int startIndices_; // point to column_
int numberInBlock_;
int numberScan_; // i.e. miss out basic and fixed
/* order is -
free or superbasic
at lower
at upper
fixed or basic */
int firstAtLower_;
int firstAtUpper_;
int firstBasic_; // or fixed
int numberElements_; // number elements per column
int numberOnes_; // later
} blockStruct;
class ClpPackedMatrix3 {
public:
/**@name Useful methods */
//@{
/** Return x * -1 * A in z
.
Note - x packed and z will be packed mode
Squashes small elements and knows about ClpSimplex */
void transposeTimes(const ClpSimplex *model,
const double *pi,
CoinIndexedVector *output) const;
/// This version does dualColumn0
/// Updates two arrays for steepest
void transposeTimes(const ClpSimplex *model,
const double *pi,
CoinIndexedVector *output,
CoinIndexedVector *candidate,
const CoinIndexedVector *rowArray) const;
void transposeTimes2(const ClpSimplex *model,
const double *pi, CoinIndexedVector *dj1,
const double *piWeight,
double *COIN_RESTRICT infeas,
double *COIN_RESTRICT reducedCost,
double referenceIn, double devex,
// Array for exact devex to say what is in reference framework
unsigned int *reference,
double *weights, double scaleFactor);
//@}
/**@name Constructors, destructor */
//@{
/** Default constructor. */
ClpPackedMatrix3();
/** Constructor from copy. */
ClpPackedMatrix3(ClpSimplex *model, const CoinPackedMatrix *columnCopy);
/** Destructor */
virtual ~ClpPackedMatrix3();
//@}
/**@name Copy method */
//@{
/** The copy constructor. */
ClpPackedMatrix3(const ClpPackedMatrix3 &);
ClpPackedMatrix3 &operator=(const ClpPackedMatrix3 &);
//@}
/**@name Sort methods */
//@{
/** Sort blocks */
void sortBlocks(const ClpSimplex *model);
/// Swap one variable
void swapOne(const ClpSimplex *model, const ClpPackedMatrix *matrix,
int iColumn);
/// Part of above
void swapOne(int iBlock, int kA, int kB);
/** Debug - check blocks */
void checkBlocks(const ClpSimplex *model);
/**
type - 1 redo infeasible, 2 choose sequenceIn, 3 both
returns sequenceIn (or -1) for type 2
*/
int redoInfeasibilities(const ClpSimplex *model,
ClpPrimalColumnSteepest *pivotChoose,
int type);
/// Get temporary array (aligned)
//@}
protected:
/**@name Data members
The data members are protected to allow access for derived classes. */
//@{
/// Number of blocks
int numberBlocks_;
/// Number of columns
int numberColumns_;
/// Number of columns including gaps
int numberColumnsWithGaps_;
#if ABOCA_LITE
/// Number of chunks
int numberChunks_;
#endif
/// Number of elements (including gaps)
CoinBigIndex numberElements_;
/// Maximum size of any block
int maxBlockSize_;
/// Column indices and reverse lookup (within block)
int *column_;
/// Starts for odd/long vectors??
CoinBigIndex *start_;
/// Rows
int *row_;
/// Elements
double *element_;
/// Temporary work area (aligned)
CoinDoubleArrayWithLength *temporary_;
#if ABOCA_LITE
/// Chunk ends (could have more than cpus)
int endChunk_[2 * ABOCA_LITE + 1];
#endif
/// Blocks (ordinary start at 0 and go to first block)
blockStruct *block_;
/// If active
int ifActive_;
//@}
};
#elif INCLUDE_MATRIX3_PRICING
int iColumn = *column;
column++;
if (fabs(value) > zeroTolerance) {
double thisWeight = weights[iColumn];
double pivot = value * scaleFactor;
double pivotSquared = pivot * pivot;
thisWeight += pivotSquared * devex + pivot * modification;
if (thisWeight < DEVEX_TRY_NORM) {
if (referenceIn < 0.0) {
// steepest
thisWeight = CoinMax(DEVEX_TRY_NORM, DEVEX_ADD_ONE + pivotSquared);
} else {
// exact
thisWeight = referenceIn * pivotSquared;
if (reference(iColumn))
thisWeight += 1.0;
thisWeight = CoinMax(thisWeight, DEVEX_TRY_NORM);
}
}
// out basic or fixed
weights[iColumn] = thisWeight;
value = reducedCost[iColumn] - value;
reducedCost[iColumn] = value;
unsigned char thisStatus = status[iColumn] & 7;
assert(thisStatus != 0 && thisStatus != 4);
if (thisStatus == 3) {
//} else if ((thisStatus&1)!=0) {
// basic or fixed
//value=0.0;
} else {
assert(thisStatus == 2);
value = -value;
}
if (value < dualTolerance) {
value *= value;
if (value > bestRatio * weights[iColumn]) {
bestSequence = iColumn;
bestRatio = value / weights[iColumn];
#if NO_CHANGE_MULTIPLIER != 1
bestRatio2 = bestRatio * NO_CHANGE_MULTIPLIER;
#endif
}
}
} else {
// interesting - was faster without this?!
value = reducedCost[iColumn];
unsigned char thisStatus = status[iColumn] & 7;
assert(thisStatus != 0 && thisStatus != 4);
if (thisStatus == 3) {
} else if ((thisStatus & 1) != 0) {
// basic or fixed
value = 0.0;
} else {
value = -value;
}
if (value < dualTolerance) {
value *= value;
if (value > bestRatio2 * weights[iColumn]) {
bestSequence = iColumn;
bestRatio2 = value / weights[iColumn];
#if NO_CHANGE_MULTIPLIER != 1
bestRatio = bestRatio2 * INVERSE_MULTIPLIER;
#endif
}
}
}
#endif
/* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
*/