2021-07-24 21:11:47 -07:00

91 lines
3.7 KiB
C++

//========= Copyright c 1996-2009, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef VPHYSICS_PHYSX_SIMPLEX_H
#define VPHYSICS_PHYSX_SIMPLEX_H
#include "tier0/dbg.h"
//////////////////////////////////////////////////////////////////////////
// Direct simplex LP solver using tableau and Gauss pivoting rule;
// see http://www.teachers.ash.org.au/mikemath/mathsc/linearprogramming/simplex.PDF
// After constructing an instance of this class with the appropriate number of vars and constraints,
// fill in constraints using SetConstraintFactor and SetConstraintConst
//
// Here's the problem in its canonical form:
// Maximize objective = c'x : x[i] >= 0, A.x <= b; and make that c' positive! negative will automatically mean 0 is your best answer
// Vector x is the vector of unknowns (variables) and has dimentionality of numVariables
// Vector c is dotted with the x, so has the same dimentionality; you set it with SetObjectiveFactor()
// every component of x must be positive in feasible solution
// A is constraint matrix and has dims: m_numConstraints by m_numVariables; you set it with SetConstraintFactor();
// b has dims: m_numConstraints, you set it with SetConstraintConst()
//
// This is solved with the simplest possible simplex method (I have no good reason to implement pivot rules now)
// The simplex tableau (m_pTableau) starts like this:
// | A | b |
// | c' | 0 |
//
class CSimplex
{
public:
int m_numConstraints, m_numVariables;
float *m_pTableau;
float *m_pInitialTableau;
float *m_pSolution;
int *m_pBasis; // indices of basis variables, corresponding to each row in the tableau; >= numVars if the slack var corresponds to that row
int *m_pNonBasis; // indices of non-basis primal variables (labels on the top of the classic Tucker(?) tableau)
enum StateEnum{kInfeasible, kUnbound, kOptimal, kUnknown, kCannotPivot};
StateEnum m_state;
//CVarBitVec m_isBasis;
public:
CSimplex();
CSimplex(int numVariables, int numConstraints);
~CSimplex();
void Init(int numVariables, int numConstraints);
void InitTableau(const float *pTableau);
void SetObjectiveFactors(int numFactors, const float *pFactors);
void SetConstraintFactor(int nConstraint, int nConstant, float fFactor);
void SetConstraintConst(int nConstraint, float fConst);
void SetObjectiveFactor(int nConstant, float fFactor);
StateEnum Solve(float flThreshold = 1e-5f, int maxStallIterations = 128);
StateEnum SolvePhase1(float flThreshold = 1e-5f, int maxStallIterations = 128);
StateEnum SolvePhase2(float flThreshold = 1e-5f, int maxStallIterations = 128);
float GetSolution(int nVariable)const;
float GetSlack(int nConstraint)const;
float GetObjective()const;
void PrintTableau()const;
protected:
void Destruct();
float *operator [] (int row) {Assert(row >= 0 && row < NumRows());return m_pTableau + row * NumColumns();}
float &Tableau(int row, int col){Assert(row >= 0 && row < NumRows());return m_pTableau[row * NumColumns()+col];}
float Tableau(int row, int col)const{Assert(row >= 0 && row < NumRows());return m_pTableau[row * NumColumns()+col];}
float GetInitialTableau(int row, int col)const{return m_pInitialTableau[row * NumColumns()+col];}
bool IteratePhase1();
bool IteratePhase2();
int NumRows()const {return m_numConstraints + 1;}
int NumColumns()const{return m_numVariables + 1;}
void Validate();
void PrepareTableau();
void GatherSolution();
bool Pivot(int nPivotRow, int nPivotColumn);
void MultiplyRow(int nRow, float fFactor);
void AddRowMulFactor(int nTargetRow, int nPivotRow, float fFactor);
int FindPivotColumn();
int FindPivotRow(int nColumn);
int FindLastNegConstrRow();
int ChooseNegativeElementInRow(int nRow);
};
#endif