/// <summary> /// Minimize the value of the objective. /// </summary> /// <remarks> /// The tableau should already be feasible. /// </remarks> private void Optimize(ClObjectiveVariable zVar) /* throws ExClInternalError */ { ClLinearExpression zRow = RowExpression(zVar); if (zRow == null) { throw new CassowaryInternalException("Assertion failed: zRow != null"); } ClAbstractVariable entryVar = null; ClAbstractVariable exitVar = null; while (true) { double objectiveCoeff = 0; foreach (var kvp in zRow.Terms) { if (kvp.Key.IsPivotable && kvp.Value.Value < objectiveCoeff) { objectiveCoeff = kvp.Value.Value; entryVar = kvp.Key; } } if (objectiveCoeff >= -EPSILON || entryVar == null) { return; } double minRatio = Double.MaxValue; foreach (ClAbstractVariable v in Columns[entryVar]) { if (v.IsPivotable) { ClLinearExpression expr = RowExpression(v); double coeff = expr.CoefficientFor(entryVar); if (coeff < 0.0) { double r = -expr.Constant / coeff; if (r < minRatio) { minRatio = r; exitVar = v; } } } } // ReSharper disable CompareOfFloatsByEqualityOperator if (minRatio == Double.MaxValue) // ReSharper restore CompareOfFloatsByEqualityOperator { throw new CassowaryInternalException("Objective function is unbounded in Optimize"); } Pivot(entryVar, exitVar); } }
/// <summary> /// Re-optimize using the dual simplex algorithm. /// </summary> /// <remarks> /// We have set new values for the constants in the edit constraints. /// </remarks> protected void DualOptimize() /* throws ExClInternalError */ { ClLinearExpression zRow = RowExpression(_objective); while (InfeasibleRows.Count > 0) { ClAbstractVariable exitVar = InfeasibleRows.First(); InfeasibleRows.Remove(exitVar); ClAbstractVariable entryVar = null; ClLinearExpression expr = RowExpression(exitVar); if (expr != null) { if (expr.Constant < 0.0) { double ratio = Double.MaxValue; var terms = expr.Terms; foreach (ClAbstractVariable v in terms.Keys) { double c = (terms[v]).Value; if (c > 0.0 && v.IsPivotable) { double zc = zRow.CoefficientFor(v); double r = zc / c; if (r < ratio) { entryVar = v; ratio = r; } } } // ReSharper disable CompareOfFloatsByEqualityOperator if (ratio == Double.MaxValue) // ReSharper restore CompareOfFloatsByEqualityOperator { throw new CassowaryInternalException("ratio == nil (Double.MaxValue) in DualOptimize"); } Pivot(entryVar, exitVar); } } } }
/// <summary> /// Fix the constants in the equations representing the edit constraints. /// </summary> /// <remarks> /// Each of the non-required edits will be represented by an equation /// of the form: /// v = c + eplus - eminus /// where v is the variable with the edit, c is the previous edit value, /// and eplus and eminus are slack variables that hold the error in /// satisfying the edit constraint. We are about to change something, /// and we want to fix the constants in the equations representing /// the edit constraints. If one of eplus and eminus is basic, the other /// must occur only in the expression for that basic error variable. /// (They can't both be basic.) Fix the constant in this expression. /// Otherwise they are both non-basic. Find all of the expressions /// in which they occur, and fix the constants in those. See the /// UIST paper for details. /// (This comment was for ResetEditConstants(), but that is now /// gone since it was part of the screwey vector-based interface /// to resolveing. --02/16/99 gjb) /// </remarks> protected void DeltaEditConstant(double delta, ClAbstractVariable plusErrorVar, ClAbstractVariable minusErrorVar) { ClLinearExpression exprPlus = RowExpression(plusErrorVar); if (exprPlus != null) { exprPlus.IncrementConstant(delta); if (exprPlus.Constant < 0.0) { InfeasibleRows.Add(plusErrorVar); } return; } ClLinearExpression exprMinus = RowExpression(minusErrorVar); if (exprMinus != null) { exprMinus.IncrementConstant(-delta); if (exprMinus.Constant < 0.0) { InfeasibleRows.Add(minusErrorVar); } return; } var columnVars = Columns[minusErrorVar]; foreach (ClAbstractVariable basicVar in columnVars) { ClLinearExpression expr = RowExpression(basicVar); //Assert(expr != null, "expr != null"); double c = expr.CoefficientFor(minusErrorVar); expr.IncrementConstant(c * delta); if (basicVar.IsRestricted && expr.Constant < 0.0) { InfeasibleRows.Add(basicVar); } } }
/// <summary> /// Remove a constraint from the tableau. /// Also remove any error variable associated with it. /// </summary> public ClSimplexSolver RemoveConstraint(ClConstraint cn) /* throws ExClRequiredFailure, ExClInternalError */ { _cNeedsSolving = true; ResetStayConstants(); ClLinearExpression zRow = RowExpression(_objective); HashSet <ClAbstractVariable> eVars; if (_errorVars.TryGetValue(cn, out eVars)) { foreach (ClAbstractVariable clv in eVars) { ClLinearExpression expr = RowExpression(clv); if (expr == null) { zRow.AddVariable(clv, -cn.Weight * cn.Strength.SymbolicWeight.AsDouble(), _objective, this); } else // the error variable was in the basis { zRow.AddExpression(expr, -cn.Weight * cn.Strength.SymbolicWeight.AsDouble(), _objective, this); } } } ClAbstractVariable marker; if (!_markerVars.TryGetValue(cn, out marker)) { throw new CassowaryConstraintNotFoundException(); } _markerVars.Remove(cn); if (RowExpression(marker) == null) { // not in the basis, so need to do some more work var col = Columns[marker]; ClAbstractVariable exitVar = null; double minRatio = 0.0; foreach (ClAbstractVariable v in col) { if (v.IsRestricted) { ClLinearExpression expr = RowExpression(v); double coeff = expr.CoefficientFor(marker); if (coeff < 0.0) { double r = -expr.Constant / coeff; if (exitVar == null || r < minRatio) { minRatio = r; exitVar = v; } } } } if (exitVar == null) { foreach (ClAbstractVariable v in col) { if (v.IsRestricted) { ClLinearExpression expr = RowExpression(v); double coeff = expr.CoefficientFor(marker); double r = expr.Constant / coeff; if (exitVar == null || r < minRatio) { minRatio = r; exitVar = v; } } } } if (exitVar == null) { // exitVar is still null if (col.Count == 0) { RemoveColumn(marker); } else { // put first element in exitVar var colEnum = col.GetEnumerator(); colEnum.MoveNext(); exitVar = colEnum.Current; } } if (exitVar != null) { Pivot(marker, exitVar); } } if (RowExpression(marker) != null) { RemoveRow(marker); } if (eVars != null) { foreach (ClAbstractVariable v in eVars.Where(a => a != marker)) { RemoveColumn(v); } } if (cn.IsStayConstraint) { if (eVars != null) { for (int i = 0; i < _stayPlusErrorVars.Count; i++) { eVars.Remove(_stayPlusErrorVars[i]); eVars.Remove(_stayMinusErrorVars[i]); } } } else if (cn.IsEditConstraint) { Assert(eVars != null, "eVars != null"); ClEditConstraint cnEdit = (ClEditConstraint)cn; ClVariable clv = cnEdit.Variable; ClEditInfo cei = _editVarMap[clv]; ClSlackVariable clvEditMinus = cei.ClvEditMinus; RemoveColumn(clvEditMinus); _editVarMap.Remove(clv); } // FIXME: do the remove at top if (eVars != null) { //_errorVars.Remove(eVars); _errorVars.Remove(cn); } if (_cOptimizeAutomatically) { Optimize(_objective); SetExternalVariables(); } return(this); }