mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-25 09:11:03 +00:00
* Implement GSCSTableau, GSCSEditInfo, GSCSEditVariableManager * Update GSTableau based on feedback * Remove adding expression term variable to external parameteric variables
693 lines
22 KiB
Objective-C
693 lines
22 KiB
Objective-C
/* Copyright (C) 2023 Free Software Foundation, Inc.
|
|
|
|
By: Benjamin Johnson
|
|
Date: 12-6-2023
|
|
This file is part of the GNUstep Library.
|
|
|
|
This library 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.
|
|
|
|
This library 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 this library; if not, write to the Free
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110 USA.
|
|
*/
|
|
|
|
#import "GSCSTableau.h"
|
|
#import "GSCSFloatComparator.h"
|
|
#import "GSFastEnumeration.h"
|
|
|
|
@interface GSCSTableau (PrivateMethods)
|
|
|
|
- (void) recordUpdatedVariable: (GSCSVariable *)variable;
|
|
|
|
- (void) addTermVariablesForExpression: (GSCSLinearExpression *)expression
|
|
variable: (GSCSVariable *)variable;
|
|
|
|
- (void) substituteOutTermInExpression: (GSCSLinearExpression *)expression
|
|
newExpression:
|
|
(GSCSLinearExpression *)newExpression
|
|
subject: (GSCSVariable *)subject
|
|
term: (GSCSVariable *)term
|
|
multiplier: (CGFloat)multiplier;
|
|
|
|
- (CGFloat) calculateNewCoefficent: (CGFloat)coefficentInExistingExpression
|
|
coefficentInNewExpression: (CGFloat)coefficentInNewExpression
|
|
multiplier: (CGFloat)multiplier;
|
|
|
|
- (void) removeColumnVariable: (GSCSVariable *)variable
|
|
subject: (GSCSVariable *)subject;
|
|
|
|
- (void) recordUpdatedVariable: (GSCSVariable *)variable;
|
|
|
|
- (void) addNewExpression: (GSCSLinearExpression *)newExpression
|
|
toExpression: (GSCSLinearExpression *)existingExpression
|
|
n: (CGFloat)n
|
|
subject: (GSCSVariable *)subject;
|
|
|
|
- (NSString *) columnsDescription;
|
|
|
|
- (NSString *) rowsDescription;
|
|
|
|
@end
|
|
|
|
@implementation GSCSTableau
|
|
|
|
- (instancetype) init
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
ASSIGN(_rows,
|
|
[NSMapTable mapTableWithKeyOptions: NSMapTableStrongMemory
|
|
valueOptions: NSMapTableStrongMemory]);
|
|
|
|
ASSIGN(_columns,
|
|
[NSMapTable mapTableWithKeyOptions: NSMapTableStrongMemory
|
|
valueOptions: NSMapTableStrongMemory]);
|
|
|
|
ASSIGN(_externalParametricVariables, [NSMutableSet set]);
|
|
|
|
ASSIGN(_externalRows,
|
|
[NSMapTable mapTableWithKeyOptions: NSMapTableStrongMemory
|
|
valueOptions: NSMapTableStrongMemory]);
|
|
|
|
ASSIGN(_infeasibleRows, [NSMutableArray array]);
|
|
|
|
ASSIGN(_objective, [GSCSVariable objectiveVariableWithName: @"Z"]);
|
|
|
|
GSCSLinearExpression *expression = [[GSCSLinearExpression alloc] init];
|
|
[self addRowForVariable: _objective equalsExpression: expression];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (GSCSVariable *) objective
|
|
{
|
|
return _objective;
|
|
}
|
|
|
|
- (void) setObjective: (GSCSVariable *)objective
|
|
{
|
|
ASSIGN(_objective, objective);
|
|
}
|
|
|
|
- (NSMapTable *) externalRows
|
|
{
|
|
return _externalRows;
|
|
}
|
|
|
|
- (void) setExternalRows: (NSMapTable *)externalRows
|
|
{
|
|
ASSIGN(_externalRows, externalRows);
|
|
}
|
|
|
|
- (NSMutableArray *) infeasibleRows
|
|
{
|
|
return _infeasibleRows;
|
|
}
|
|
|
|
- (void) setInfeasibleRows: (NSMutableArray *)infeasibleRows
|
|
{
|
|
ASSIGN(_infeasibleRows, infeasibleRows);
|
|
}
|
|
|
|
- (BOOL) shouldPreferPivotableVariable: (GSCSVariable *)lhs
|
|
overPivotableVariable: (GSCSVariable *)rhs
|
|
{
|
|
return [lhs id] < [rhs id];
|
|
}
|
|
|
|
- (BOOL) hasColumnForVariable: (GSCSVariable *)variable
|
|
{
|
|
return [_columns objectForKey: variable] != nil;
|
|
}
|
|
|
|
- (NSSet *) columnForVariable: (GSCSVariable *)variable
|
|
{
|
|
return [_columns objectForKey: variable];
|
|
}
|
|
|
|
// Add v=expr to the tableau, update column cross indices
|
|
// v becomes a basic variable
|
|
- (void) addRowForVariable: (GSCSVariable *)variable
|
|
equalsExpression: (GSCSLinearExpression *)expression
|
|
{
|
|
[_rows setObject: expression forKey: variable];
|
|
if ([variable isExternal])
|
|
{
|
|
[_externalRows setObject: expression forKey: variable];
|
|
}
|
|
[self addTermVariablesForExpression: expression variable: variable];
|
|
}
|
|
|
|
- (void) addTermVariablesForExpression: (GSCSLinearExpression *)expression
|
|
variable: (GSCSVariable *)variable
|
|
{
|
|
NSArray *termVariables = [expression termVariables];
|
|
FOR_IN(GSCSVariable *, expressionTermVariable, termVariables)
|
|
[self addMappingFromExpressionVariable: expressionTermVariable
|
|
toRowVariable: variable];
|
|
if ([expressionTermVariable isExternal])
|
|
{
|
|
[_externalParametricVariables addObject: expressionTermVariable];
|
|
}
|
|
END_FOR_IN(termVariables);
|
|
}
|
|
|
|
- (void) addMappingFromExpressionVariable: (GSCSVariable *)columnVariable
|
|
toRowVariable: (GSCSVariable *)rowVariable
|
|
{
|
|
NSMutableSet *columnSet = [_columns objectForKey: columnVariable];
|
|
if (columnSet == nil)
|
|
{
|
|
columnSet = [NSMutableSet set];
|
|
[_columns setObject: columnSet forKey: columnVariable];
|
|
}
|
|
[columnSet addObject: rowVariable];
|
|
}
|
|
|
|
- (void) removeMappingFromExpressionVariable: (GSCSVariable *)columnVariable
|
|
toRowVariable: (GSCSVariable *)rowVariable
|
|
{
|
|
NSMutableSet *columnSet = [_columns objectForKey: columnVariable];
|
|
if (columnSet == nil)
|
|
{
|
|
return;
|
|
}
|
|
[columnSet removeObject: rowVariable];
|
|
}
|
|
|
|
- (void) removeColumn: (GSCSVariable *)variable
|
|
{
|
|
NSSet *crows = [_columns objectForKey: variable];
|
|
if (_rows != nil)
|
|
{
|
|
FOR_IN(GSCSVariable *, clv, crows)
|
|
GSCSLinearExpression *expression = [_rows objectForKey: clv];
|
|
[expression removeVariable: variable];
|
|
END_FOR_IN(crows);
|
|
[_columns removeObjectForKey: variable];
|
|
}
|
|
|
|
if ([variable isExternal])
|
|
{
|
|
[_externalRows removeObjectForKey: variable];
|
|
}
|
|
}
|
|
|
|
- (void) removeRowForVariable: (GSCSVariable *)variable
|
|
{
|
|
GSCSLinearExpression *expression = [_rows objectForKey: variable];
|
|
if (expression == nil)
|
|
{
|
|
NSException *missingExpressionException = [NSException
|
|
exceptionWithName: NSInvalidArgumentException
|
|
reason: @"No expression exists for the provided variable"
|
|
userInfo: nil];
|
|
[missingExpressionException raise];
|
|
}
|
|
[_rows removeObjectForKey: variable];
|
|
if ([variable isExternal])
|
|
{
|
|
[_externalRows removeObjectForKey: variable];
|
|
}
|
|
NSArray *expressionTermVariables = [expression termVariables];
|
|
FOR_IN(GSCSVariable *, expressionTermVariable, expressionTermVariables)
|
|
[self removeMappingFromExpressionVariable: expressionTermVariable
|
|
toRowVariable: variable];
|
|
END_FOR_IN(expressionTermVariables);
|
|
}
|
|
|
|
- (BOOL) hasRowForVariable: (GSCSVariable *)variable
|
|
{
|
|
return [_rows objectForKey: variable] != nil;
|
|
}
|
|
|
|
- (void) substituteOutVariable: (GSCSVariable *)variable
|
|
forExpression: (GSCSLinearExpression *)expression
|
|
{
|
|
NSSet *variableSet = [[_columns objectForKey: variable] copy];
|
|
FOR_IN(GSCSVariable *, columnVariable, variableSet)
|
|
GSCSLinearExpression *row = [_rows objectForKey: columnVariable];
|
|
[self substituteOutTerm: variable
|
|
withExpression: expression
|
|
inExpression: row
|
|
subject: columnVariable];
|
|
if ([columnVariable isRestricted] && [row constant] < 0.0)
|
|
{
|
|
[_infeasibleRows addObject: columnVariable];
|
|
}
|
|
END_FOR_IN(variableSet);
|
|
RELEASE(variableSet);
|
|
|
|
if ([variable isExternal])
|
|
{
|
|
[_externalRows setObject: expression forKey: variable];
|
|
[_externalParametricVariables removeObject: variable];
|
|
}
|
|
[_columns removeObjectForKey: variable];
|
|
}
|
|
|
|
- (void) substituteOutTerm: (GSCSVariable *)term
|
|
withExpression: (GSCSLinearExpression *)newExpression
|
|
inExpression: (GSCSLinearExpression *)expression
|
|
subject: (GSCSVariable *)subject
|
|
{
|
|
CGFloat coefficieint = [expression coefficientForTerm: term];
|
|
[expression removeVariable: term];
|
|
[expression setConstant: (coefficieint * [newExpression constant]) +
|
|
[expression constant]];
|
|
NSArray *termVariables = [expression termVariables];
|
|
FOR_IN(GSCSVariable *, newExpressionTerm, termVariables)
|
|
[self substituteOutTermInExpression: expression
|
|
newExpression: newExpression
|
|
subject: subject
|
|
term: newExpressionTerm
|
|
multiplier: coefficieint];
|
|
END_FOR_IN(termVariables);
|
|
}
|
|
|
|
- (void) substituteOutTermInExpression: (GSCSLinearExpression *)expression
|
|
newExpression:
|
|
(GSCSLinearExpression *)newExpression
|
|
subject: (GSCSVariable *)subject
|
|
term: (GSCSVariable *)term
|
|
multiplier: (CGFloat)multiplier
|
|
{
|
|
NSNumber *coefficentInNewExpression =
|
|
[newExpression multiplierForTerm: term];
|
|
NSNumber *coefficentInExistingExpression =
|
|
[expression multiplierForTerm: term];
|
|
|
|
if (coefficentInExistingExpression != nil)
|
|
{
|
|
CGFloat newCoefficent = [self
|
|
calculateNewCoefficent: [coefficentInExistingExpression floatValue]
|
|
coefficentInNewExpression: [coefficentInNewExpression floatValue]
|
|
multiplier: multiplier];
|
|
|
|
if ([GSCSFloatComparator isApproxiatelyZero: newCoefficent])
|
|
{
|
|
[expression removeVariable: term];
|
|
[self removeMappingFromExpressionVariable: term
|
|
toRowVariable: subject];
|
|
}
|
|
else
|
|
{
|
|
[expression addVariable: term coefficient: newCoefficent];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CGFloat updatedCoefficent
|
|
= multiplier * [coefficentInNewExpression floatValue];
|
|
[expression addVariable: term coefficient: updatedCoefficent];
|
|
[self addMappingFromExpressionVariable: term toRowVariable: subject];
|
|
}
|
|
}
|
|
|
|
- (CGFloat) calculateNewCoefficent: (CGFloat)coefficentInExistingExpression
|
|
coefficentInNewExpression: (CGFloat)coefficentInNewExpression
|
|
multiplier: (CGFloat)multiplier
|
|
{
|
|
return coefficentInExistingExpression + multiplier * coefficentInNewExpression;
|
|
}
|
|
|
|
- (BOOL) isBasicVariable: (GSCSVariable *)variable
|
|
{
|
|
return [_rows objectForKey: variable] != nil;
|
|
}
|
|
|
|
- (void) addVariable: (GSCSVariable *)variable
|
|
toExpression: (GSCSLinearExpression *)expression
|
|
{
|
|
[self addVariable: variable
|
|
toExpression: expression
|
|
withCoefficient: 1.0
|
|
subject: nil];
|
|
}
|
|
|
|
- (void) addVariable: (GSCSVariable *)variable
|
|
toExpression: (GSCSLinearExpression *)expression
|
|
withCoefficient: (CGFloat)coefficient;
|
|
{
|
|
[self addVariable: variable
|
|
toExpression: expression
|
|
withCoefficient: coefficient
|
|
subject: nil];
|
|
}
|
|
|
|
- (void) addVariable: (GSCSVariable *)variable
|
|
toExpression: (GSCSLinearExpression *)expression
|
|
withCoefficient: (CGFloat)coefficient
|
|
subject: (GSCSVariable *)subject
|
|
{
|
|
if ([expression isTermForVariable: variable])
|
|
{
|
|
CGFloat newCoefficient =
|
|
[expression coefficientForTerm: variable] + coefficient;
|
|
if (newCoefficient == 0 ||
|
|
[GSCSFloatComparator isApproxiatelyZero: newCoefficient])
|
|
{
|
|
[expression removeVariable: variable];
|
|
[self removeColumnVariable: variable subject: subject];
|
|
}
|
|
else
|
|
{
|
|
[self setVariable: variable
|
|
onExpression: expression
|
|
withCoefficient: newCoefficient];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (![GSCSFloatComparator isApproxiatelyZero: coefficient])
|
|
{
|
|
[self setVariable: variable
|
|
onExpression: expression
|
|
withCoefficient: coefficient];
|
|
if (subject)
|
|
{
|
|
[self addMappingFromExpressionVariable: variable
|
|
toRowVariable: subject];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) removeColumnVariable: (GSCSVariable *)variable
|
|
subject: (GSCSVariable *)subject
|
|
{
|
|
NSMutableSet *column = [_columns objectForKey: variable];
|
|
if (subject != nil && column != nil)
|
|
{
|
|
[column removeObject: subject];
|
|
}
|
|
}
|
|
|
|
- (void) addNewExpression: (GSCSLinearExpression *)newExpression
|
|
toExpression: (GSCSLinearExpression *)existingExpression
|
|
{
|
|
[self addNewExpression: newExpression
|
|
toExpression: existingExpression
|
|
n: 1
|
|
subject: nil];
|
|
}
|
|
|
|
- (void) addNewExpression: (GSCSLinearExpression *)newExpression
|
|
toExpression: (GSCSLinearExpression *)existingExpression
|
|
n: (CGFloat)n
|
|
subject: (GSCSVariable *)subject
|
|
{
|
|
[existingExpression setConstant: [existingExpression constant]
|
|
+ (n * [newExpression constant])];
|
|
NSArray *newExpressionTermVariables = [newExpression termVariables];
|
|
FOR_IN(GSCSVariable *, term, newExpressionTermVariables)
|
|
CGFloat newCoefficient = [newExpression coefficientForTerm: term] * n;
|
|
[self addVariable: term
|
|
toExpression: existingExpression
|
|
withCoefficient: newCoefficient
|
|
subject: subject];
|
|
|
|
[self recordUpdatedVariable: term];
|
|
END_FOR_IN(newExpressionTermVariables);
|
|
}
|
|
|
|
- (void) recordUpdatedVariable: (GSCSVariable *)variable
|
|
{
|
|
if ([variable isExternal])
|
|
{
|
|
[_externalParametricVariables addObject: variable];
|
|
}
|
|
}
|
|
|
|
- (void) setVariable: (GSCSVariable *)variable
|
|
onExpression: (GSCSLinearExpression *)expression
|
|
withCoefficient: (CGFloat)coefficient
|
|
{
|
|
[expression addVariable: variable coefficient: coefficient];
|
|
[self recordUpdatedVariable: variable];
|
|
}
|
|
|
|
- (void) changeSubjectOnExpression: (GSCSLinearExpression *)expression
|
|
existingSubject: (GSCSVariable *)existingSubject
|
|
newSubject: (GSCSVariable *)newSubject
|
|
{
|
|
[self setVariable: existingSubject
|
|
onExpression: expression
|
|
withCoefficient: [expression newSubject: newSubject]];
|
|
}
|
|
|
|
- (GSCSLinearExpression *) rowExpressionForVariable: (GSCSVariable *)variable
|
|
{
|
|
return [_rows objectForKey: variable];
|
|
}
|
|
|
|
- (void) pivotWithEntryVariable: (GSCSVariable *)entryVariable
|
|
exitVariable: (GSCSVariable *)exitVariable
|
|
{
|
|
// The expression represents the exit variable, which is about to be removed from the basis.
|
|
// This means that the old tableau should contain the equation: exitVar = expr.
|
|
GSCSLinearExpression *expression =
|
|
[self rowExpressionForVariable: exitVariable];
|
|
[self removeRowForVariable: exitVariable];
|
|
|
|
// Calculate an expression for the entry variable.
|
|
// As "expression" has been removed from the tableau,
|
|
// we can make destructive modifications to it in order to construct this expression.
|
|
[self changeSubjectOnExpression: expression
|
|
existingSubject: exitVariable
|
|
newSubject: entryVariable];
|
|
[self substituteOutVariable: entryVariable forExpression: expression];
|
|
|
|
if ([entryVariable isExternal])
|
|
{
|
|
[_externalParametricVariables removeObject: entryVariable];
|
|
}
|
|
|
|
[self addRowForVariable: entryVariable equalsExpression: expression];
|
|
}
|
|
|
|
- (NSString *) description
|
|
{
|
|
NSMutableString *description =
|
|
[NSMutableString stringWithString: @"Tableau Information\n"];
|
|
[description appendFormat: @"Rows: %ld (%ld constraints)\n", [_rows count],
|
|
[_rows count] - 1];
|
|
[description appendFormat: @"Columns: %ld\n", [_columns count]];
|
|
[description appendFormat: @"Infeasible rows: %ld\n", [_infeasibleRows count]];
|
|
[description
|
|
appendFormat: @"External basic variables: %ld\n", [_externalRows count]];
|
|
[description appendFormat: @"External parametric variables: %ld\n\n",
|
|
[_externalParametricVariables count]];
|
|
|
|
[description appendString: @"Columns: \n"];
|
|
[description appendString: [self columnsDescription]];
|
|
|
|
[description appendString: @"\nRows:\n"];
|
|
[description appendString: [self rowsDescription]];
|
|
|
|
return description;
|
|
}
|
|
|
|
- (NSString *) columnsDescription
|
|
{
|
|
NSMutableString *description = [NSMutableString string];
|
|
FOR_IN(GSCSVariable *, columnVariable, _columns)
|
|
[description appendFormat: @"%@ : %@", columnVariable,
|
|
[_columns objectForKey: columnVariable]];
|
|
END_FOR_IN(_columns);
|
|
|
|
return description;
|
|
}
|
|
|
|
- (NSString *) rowsDescription
|
|
{
|
|
NSMutableString *description = [NSMutableString string];
|
|
FOR_IN(GSCSVariable *, variable, _rows)
|
|
[description
|
|
appendFormat: @"%@ : %@\n", variable, [_rows objectForKey: variable]];
|
|
END_FOR_IN(_rows);
|
|
return description;
|
|
}
|
|
|
|
- (BOOL) hasInfeasibleRows
|
|
{
|
|
return [_infeasibleRows count] > 0;
|
|
}
|
|
|
|
- (BOOL) containsExternalParametricVariableForEveryExternalTerm
|
|
{
|
|
FOR_IN(GSCSVariable *, rowVariable, _rows)
|
|
GSCSLinearExpression *expression = [_rows objectForKey: rowVariable];
|
|
NSArray *termVariables = [expression termVariables];
|
|
FOR_IN(GSCSVariable *, variable, termVariables)
|
|
if ([variable isExternal])
|
|
{
|
|
if (![_externalParametricVariables containsObject: variable])
|
|
{
|
|
return NO;
|
|
}
|
|
}
|
|
END_FOR_IN(termVariables);
|
|
END_FOR_IN(_rows);
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL) containsExternalRowForEachExternalRowVariable
|
|
{
|
|
FOR_IN(GSCSVariable *, rowVariable, _rows)
|
|
if ([rowVariable isExternal] &&
|
|
[_externalRows objectForKey: rowVariable] == nil)
|
|
{
|
|
return NO;
|
|
}
|
|
END_FOR_IN(_rows);
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (NSArray *) substitedOutNonBasicPivotableVariables: (GSCSVariable *)objective
|
|
{
|
|
GSCSLinearExpression *objectiveRowExpression =
|
|
[self rowExpressionForVariable: objective];
|
|
|
|
NSMutableArray *substitutedOutVariables = [NSMutableArray array];
|
|
FOR_IN(GSCSVariable *, columnVariable, _columns)
|
|
if ([columnVariable isPivotable] && ![self isBasicVariable: columnVariable]
|
|
&& ![objectiveRowExpression isTermForVariable: columnVariable])
|
|
{
|
|
[substitutedOutVariables addObject: columnVariable];
|
|
}
|
|
END_FOR_IN(_columns);
|
|
|
|
return substitutedOutVariables;
|
|
}
|
|
|
|
- (GSCSVariable *) findPivotableExitVariable: (GSCSVariable *)entryVariable
|
|
{
|
|
CGFloat minRatio = DBL_MAX;
|
|
CGFloat r = 0;
|
|
GSCSVariable *exitVariable = nil;
|
|
NSSet *column = [self columnForVariable: entryVariable];
|
|
FOR_IN(GSCSVariable *, variable, column)
|
|
if ([variable isPivotable])
|
|
{
|
|
GSCSLinearExpression *expression =
|
|
[self rowExpressionForVariable: variable];
|
|
CGFloat coefficient = [expression coefficientForTerm: entryVariable];
|
|
|
|
if (coefficient < 0)
|
|
{
|
|
r = -[expression constant] / coefficient;
|
|
|
|
// Bland's anti-cycling rule:
|
|
// if multiple variables are about the same,
|
|
// always pick the lowest via some total
|
|
// ordering -- in this implementation we preferred the variable
|
|
// created first
|
|
if (r < minRatio
|
|
|| ([GSCSFloatComparator isApproxiatelyEqual: r b: minRatio] &&
|
|
[self shouldPreferPivotableVariable: variable
|
|
overPivotableVariable: exitVariable]))
|
|
{
|
|
minRatio = r;
|
|
exitVariable = variable;
|
|
}
|
|
}
|
|
}
|
|
END_FOR_IN(column);
|
|
|
|
return exitVariable;
|
|
}
|
|
|
|
- (GSCSVariable *) findPivotableExitVariableWithoutCheck:
|
|
(GSCSVariable *)entryVariable
|
|
{
|
|
CGFloat minRatio = DBL_MAX;
|
|
CGFloat r = 0;
|
|
GSCSVariable *exitVariable = nil;
|
|
NSSet *column = [self columnForVariable: entryVariable];
|
|
FOR_IN(GSCSVariable *, variable, column)
|
|
GSCSLinearExpression *expression =
|
|
[self rowExpressionForVariable: variable];
|
|
CGFloat coefficient = [expression coefficientForTerm: entryVariable];
|
|
|
|
r = -[expression constant] / coefficient;
|
|
|
|
// Bland's anti-cycling rule
|
|
if (r < minRatio
|
|
|| ([GSCSFloatComparator isApproxiatelyEqual: r b: minRatio] &&
|
|
[self shouldPreferPivotableVariable: variable
|
|
overPivotableVariable: exitVariable]))
|
|
{
|
|
minRatio = r;
|
|
exitVariable = variable;
|
|
}
|
|
END_FOR_IN(column);
|
|
|
|
return exitVariable;
|
|
}
|
|
|
|
- (GSCSVariable *)
|
|
findExitVariableForEquationWhichMinimizesRatioOfRestrictedVariables:
|
|
(GSCSVariable *)constraintMarkerVariable
|
|
{
|
|
GSCSVariable *exitVariable = nil;
|
|
CGFloat minRatio = 0;
|
|
|
|
NSSet *column = [self columnForVariable: constraintMarkerVariable];
|
|
FOR_IN(GSCSVariable *, variable, column)
|
|
if ([variable isRestricted])
|
|
{
|
|
GSCSLinearExpression *expression =
|
|
[self rowExpressionForVariable: variable];
|
|
CGFloat coefficient =
|
|
[expression coefficientForTerm: constraintMarkerVariable];
|
|
CGFloat r = [expression constant] / coefficient;
|
|
|
|
if (exitVariable == nil || r < minRatio)
|
|
{
|
|
minRatio = r;
|
|
exitVariable = variable;
|
|
}
|
|
}
|
|
END_FOR_IN(column);
|
|
|
|
return exitVariable;
|
|
}
|
|
|
|
- (GSCSVariable *) findNonBasicVariables
|
|
{
|
|
FOR_IN(GSCSVariable *, variable, _externalParametricVariables)
|
|
if (![self isBasicVariable: variable])
|
|
{
|
|
return variable;
|
|
}
|
|
END_FOR_IN(_externalParametricVariables);
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
RELEASE(_rows);
|
|
RELEASE(_columns);
|
|
RELEASE(_externalParametricVariables);
|
|
RELEASE(_externalRows);
|
|
RELEASE(_infeasibleRows);
|
|
RELEASE(_objective);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|