Quick Start Guide

Introduction

The Ill Conditioning Explainer can be used to obtain an explanation of ill conditioned basis matrices for linear programs (LP). It can be used on the initial relaxation of a mixed integer program (MIP) by invoking the Model.relax() function on the MIP to obtain the LP relaxation. Other problem types are not currently supported, although models with convex quadratic objective functions can be turned into related LPs by replacing the convex quadratic objective expression with a linear expression (including just removing the quadratic part). The currently implemented approach uses the fact that an ill conditioned square matrix has linear combination of a subset of rows or columns that is close to 0. Mathematically, for a square matrix \(B\), it calculates a nonzero linear combination vector \(y\) such that \(||B^{T}y||_{\infty} \leq \epsilon\) and \(||y|| \gg \epsilon\). The vector \(y\) can be viewed as a certificate of ill conditioning. The elements of \(y\) that are zero correspond to rows of the matrix \(B\) that do not contribute to the ill conditioning. The Ill Conditioning Explainer filters out the rows corresponding to the zero elements of \(y\). The remaining rows provide an explanation of the ill conditioning. One can similarly calculate a nonzero linear combination of the columns of \(B\) and derive subset of the columns of \(B\) to explain the ill conditioning. The Ill Conditioning Explainer can do both. For the special case of ill conditioning due to pairs of almost parallel rows or columns, a separate method is available.

Running the Explainer

This section describes the 3 most basic ways to run the explainer. Additional methods will be described later in the Advanced Usage Guide section. The kappa_explain method looks for explanations of arbitrary size. By default it will provide a row-based explanation, but a column-based explanation can be obtained by setting the expltype function argument to "COLS". Here is the simplest sequence of commands to do this.

import gurobipy as gp
import gurobi_modelanalyzer as gma

m=gp.read("myillconditionedmodel.mps")
m.optimize()
gma.kappa_explain(m)        # row-based; could also specify expltype="ROWS"

If a column-based explanation is preferred, call

gma.kappa_explain(m, expltype="COLS")

One can request both types of explanations by consecutive kappa_explain calls in a single code fragment.

The angle_explain method can be called with just the model argument, in which case it will stop and return the first pair of almost parallel rows or columns. Or it can be called with a numerical argument specifying the number of pairs of near parallel rows and columns to return.

Interpreting the Output

The kappa_explain method outputs a file of the rows or columns of the basis matrix that explain the ill conditioning using the most suitable problem file format. Row based explanations are written in LP format, while column based explanations are written in MPS format. The file name consists of the name of the model as specified in the gurobipy Model.modelName attribute, suffixed by "_kappaexplain", followed by the file format (.lp or .mps). The method also returns the model associated with the LP or MPS file that was written.

The LP format explanation consists first of a row consisting of the computed linear combination of the basis matrix rows that takes on small values. Subsequent rows correspond to the constraints that are involved in the ill conditioning. The row names in the LP file consist of the constraint name prefixed by a text string that specifies the value of the element in the y vector that comprises the certificate of infeasibility mentioned in the Introduction section. These are the row multipliers of the linear combination that results in the first row of the LP file, which has the name GRB_Combined_Row. The rows are listed in descending order by largest absolute multiplier. Here is an example of the constraints in such an LP file:

GRB_Combined_Row: = -1.75613e-08
(mult=0.5000000001354858)R09bad: - 0.999999999 X02 + X01 - X03 = 0
(mult=0.49999999963548586)R09: X02 - X01 + X03 = 0
(mult=-4.999999859945595e-10)X46: - X03 + 0.109 X22 <= 0
(mult=4.716981000007684e-10)R10: - 1.06 X01 + X04 = 0
(mult=4.716981000007684e-10)R20: - 0.43 X22 + X26 = 0
(mult=-4.716981000007684e-10)X50: X04 + X26 <= 310
(mult=2.573301814737374e-10)X27: X22 <= 500

The last 5 rows all consist of small multipliers and constraint coefficients on the order of 1 or less, so their contribution to the explanation is minimal. The first two basis rows provide the explanation, and examination of these reveals that they intersect the same 3 variables, and that the rows are almost parallel. This is thie cause of the ill conditioning. Remedies depend on the model in general, but in this case the modeler should focus on whether the coefficient of 0.999999999 in constraint R09bad has a meaningful difference fromn the coefficient in constraint R09, or if the difference involves roundoff error in the data computation for the model. In the former case, the model needs to reflect that difference in a way that it is larger; in the latter case the coefficient should be cleaned up to the true value of 1.0.

The column based explanation is analagous, with an MPS file consisting of a combined column named GRB_Combined_Column followed by the individual columns in the explanation. Here is the explanation for the same model:

COLUMNS
  GRB_Combined_Column  R09       1.1705685309948421e-09
  (mult=1.240802673407655)X04  R10       1
  (mult=1.240802673407655)X04  X50       1
  (mult=-1.240802673407655)GRBslack_X50  X50       1
  (mult=1.1705685609891108)X02  R09       1
  (mult=1.1705685609891108)X02  X21       -1
  (mult=1.1705685609891108)X02  R09bad    -0.999999999
  (mult=1.1705685598185422)X01  R09       -1
  (mult=1.1705685598185422)X01  R10       -1.06
  (mult=1.1705685598185422)X01  X05       1
  (mult=1.1705685598185422)X01  R09bad    1
  (mult=1.1705685598185422)X01  X48       0.301
  (mult=-1.1705685598185422)GRBslack_X05  X05       1
  (mult=0.8862876247488983)X16  R13       1
  (mult=0.8862876247488983)X16  X51       1
  (mult=-0.8862876247488983)GRBslack_X51  X51       1
  (mult=0.8361204007065078)X14  X21       1.4
  (mult=0.8361204007065078)X14  R12       1
  (mult=0.8361204007065078)X06  R12       -1
  (mult=0.8361204007065078)X06  R13       -1.06
  (mult=0.8361204007065078)X06  X17       1
  (mult=0.8361204007065078)X06  X49       0.301
  (mult=-0.8361204007065078)GRBslack_X17  X17       1
  (mult=-0.5033444809736455)GRBslack_X49  X49       1
  (mult=-0.35234113650538124)X23  R19       1
  (mult=-0.35234113650538124)X23  X44       -1
  (mult=0.35234113650538124)X24  R19       1
  (mult=0.35234113650538124)X24  X48       -1
  (mult=-0.2516722403609866)X36  X44       1.4
  (mult=-0.2516722403609866)X36  R23       -1
  (mult=-0.2516722403609866)X37  R23       1
  (mult=-0.2516722403609866)X37  X49       -1
RHS

For this model, the row-based explanation is easier to interpret, so we will not examine the column-based explanation in detail. However, note that the problematic coefficient of 0.999999999 in constraint R09bad does appear in the column-based output.

The angle_explain method does not output an LP or MPS file containing the basis matrix rows or columns that explain the ill conditioning, as it is capable of providing multiple (simple) explanations at once. Rather, it returns separate tuplelists of pairs of almost parallel rows and almost parallel columns, followed by the model associated with the basis matrix that generated those tuplelists. Note that one or both tuplelists may be empty if no almost parallel rows or columns were detected. Running the angle_explain method on the same model that generated the previous LP and MPS files:

>>> gma.angle_explain(m)
    ([(<gurobi.Constr R09>, <gurobi.Constr R09bad>)], [],
    <gurobi.Model Continuous instance basismodel: 28 constrs, 28 vars,
    No parameter changes>)

Thus, it detected the same two constraints as the row-based explanation, and found no almost parallel columns.

Suggested Usage Quick Start

Explainer output may be small and straightforward to interpret, or it may consists of hundreds or thousands of constraints or variables when run on large models. A model detailed discussion will appear in the Advanced Usage Guide section regarding how to interpret large explanations. However, the recommended approach when getting started is to first request a row-based explanation, but if it is too large to interpret then request the column based explanation. In many cases, one of the two explanations may be much smaller and easier to interpret than the other. If both are large, try the angle_explain routine. If none of these approach yield anything, look at the Advanced Usage Guide section for additional information on how to interpret the output.