Object Documentation

This document explains the function of the classes defined in PyGLPK. In addition to descriptions, for the benefit of those familiar with the C API, it proceeds by example and analogy to the C API: there is often some C code in one color followed by some Python code in a different color which tries to do the same thing, in this spirit:

printf("Hello world!");
total = 0;
for (i=0; i<10; ++i) total += i*i;
print "Hello world!"
total = sum(i*i for i in range(10))

Hopefully, in the case of multiline examples, it should be obvious which lines correspond to each other.

Reading this document through is not necessarily the best way to gain an understanding of PyGLPK if you are completely new to it. This is organized in order of object above any other consideration: advanced obscure subjects relating to LPX objects occur before simple functionality of row and column objects. Rather, this is intended to be a reference.

Linear Program

The most basic object in PyGLPK is the LPX class, just as in the GLPK C API it is the LPX structure. As a rule, it holds data and methods that are relevant to the linear problem as a whole. Throughout the code in this document, the token lp refers to an LPX object.

Creating and Deleting Problems

One may create the Python instance of an LPX problem with a constructor. Note that one does not need to delete the LPX object, though it may be desirable to do so.

If you have maintained objects which point back to the LPX object (e.g., rows, columns, objective function), the LPX cannot be deleted until these references are discarded. The point is that del lp will not necessarily result in deallocation of the LPX structure.

lp = lpx_create_prob();
lpx_delete_prob(lp);
lp = glpk.LPX()
del lp

Naming Problems

One gets and sets the name for a problem by querying or assigning to the name attribute. As with the C API, names are limited to 255 characters. Unset names are None. One unsets a name by assigning None or deleting the name.

lpx_set_prob_name(lp, "some name");
lpx_set_prob_name(lp, NULL);
char *thename = lpx_get_prob_name(lp);
lp.name = "some name"
del lp.name # Alternately, lp.name = None
thename = lp.name

Constraint Matrix

One gets and sets the entries of the entire constraint matrix by querying or assigning the attribute matrix. Assignments to matrix will remove previous entries.

Retrieving matrix will yeild a list of three element (int,int,float) tuples over the non-zero entries in this matrix, each of the form (ri, ci, value), indicating that row ri and column ci (counting from 0) hold value value.

For example, consider if lp encoded the constraint matrix:

p= x0+ x1+ x2
q= 10x0+ 4x1+ 5x2
r= 2x0+ 6x2

Then print lp.matrix outputs [(0, 0, 1.0), (0, 1, 1.0), (0, 2, 1.0), (1, 0, 10.0), (1, 1, 4.0), (1, 2, 5.0), (2, 0, 2.0), (2, 2, 6.0)].

For setting rather than getting, one may set all non-zero entries of the constraint matrix by assigning an iterable with similar structure to the matrix attribute. The iterable must yield values each in one of these two forms:

Indices out of bounds will result in an IndexError and duplicate indices will result in an ValueError. Order does not matter, except of course for single value entries, as their location depends on the previous entry.

One may set all entries of a row or column in the constraint matrix to zero by assigning None to or deleting the matrix attribute.

Suppose we wanted to set rather than get the earlier matrix.

int    ia[] = {0+1, 0+1, 0+1,  1+1, 2+1, 1+1, 1+1, 2+1};
int    ja[] = {0+1, 1+1, 2+1,  0+1, 0+1, 1+1, 2+1, 2+1};
double ar[] = {1.0, 1.0, 1.0, 10.0, 2.0, 4.0, 5.0, 6.0}
lpx_load_matrix(lp, sizeof(ia), ia, ja, ar);
lp.matrix = [(0, 0, 1.0), (0, 1, 1.0), (0, 2, 1.0), (1, 0, 10.0),
             (2, 0, 2.0), (1, 1, 4.0), (1, 2, 5.0), (2, 2, 6.0)]

One could also do the following.

lp.matrix = [ 1.0, 1.0, 1.0,
             10.0, 4.0, 5.0,
              2.0, 0.0, 6.0 ]

Non-Zero Constraint Matrix Entries

One gets the number of non-zero constraint matrix entries by querying the nnz integer attribute.

int numnonzero = lpx_get_num_nz(lp);
numnonzero = lp.nnz

Basis Definition

The user may want to define the initial LP basis prior to starting simplex optimization. There are several automatic ways of constructing this basis.

lpx_std_basis(lp);
lpx_adv_basis(lp);
lpx_cpx_basis(lp);
lpx_read_basis(lp, "/path/to/file");
lp.std_basis()
lp.adv_basis()
lp.cpx_basis()
lp.read_basis("/path/to/file")

Scaling

Prior to optimization, it is often help to scale your problem, in part to avoid numerical instability. The method scale tells the linear program to transform the program into an alternate equivalent formulation with better numerical properties. Note that this transformation is transparent to the user. This is a matter of internal representation used to help the solver.

By passing on an optional argument that evaluates to false, one can cancel any previous scaling.

lpx_scale_prob(lp);
lpx_unscale_prob(lp);
lp.scale()
lp.scale(False)

Problem Kind, Continuous or Mixed Integer

One gets and sets the kind of problem (the default linear program or mixed integer) by querying or assigning the attribute kind. This will hold either float if this is a pure linear program (LP), or int if this is a mixed integer program (MIP).

lpx_set_class(lp, LPX_LP);
lpx_set_class(lp, LPX_MIP);
int thekind = lpx_get_class(lp);
lp.kind = float
lp.kind = int
thekind = lp.kind

Integer and Binary Columns

One gets the number of integer and binary (i.e., integer with 0, 1 bounds) column variables by querying the nint and nbin integer attributes, respectively. If this is not a mixed integer problem, these attributes always hold 0.

int num_int = lpx_get_num_int(lp);
int num_bin = lpx_get_num_bin(lp);
num_int = lp.nint
num_bin = lp.nbin

Solving the Problem

When it comes time to actually solving a linear program, one calls a lp.solver() method, where solver refers to one of several solver methods. There are several choices available.

Return values are either None if the solver terminated normally, or a string denoting one of several possible error messages. See help for each method to review these possible return values.

lpx_simplex(lp);
lpx_exact(lp);
lpx_interior(lp);
lpx_integer(lp);  // These two may be applied only to MIP problems
lpx_intopt(lp);
lp.simplex()
lp.exact()
lp.interior()
lp.integer()  # These two may be applied only to MIP problems
lp.intopt()

Note, the solver not returning a message simply means that it terminated without error. It does not mean that an optimal solution or indeed any solution was found! For example, a solver could terminate without error if it determines that there is no feasible solution.

Solution Status

One gets the solution status for the last solver by querying the status attribute. This takes the form of a string with several possible values.

int stat = lpx_get_status(lp); // or lpx_(ipt|mip)_status
stat = lp.status

Unlike the C API, PyGLPK remembers which solver was used last and retrieves the corresponding status value. If for whatever reason you wish to retrieve the status of a solver's solution other than what was used last, you may ask for status_s (for simplex and exact), or status_i (for interior), or status_m (for integer or intopt).

Additionally, if one has used the simplex solver, one can get the primal and dual status with the status_primal and status_dual attributes.

int pstat = lpx_get_prim_stat(lp);
int dstat = lpx_get_dual_stat(lp);
pstat = lp.status_primal
dstat = lp.status_dual

Ray

If, after running a simplex optimizer, your basic solution is unbounded, you may retrieve the row or column corresponding to the non-basic variable causing primal unboundedness within the attribute ray. The meaning of this is that corresponding variable is able to infinitely change in some unbounded direction to improve the objective function.

int the_var_index = lpx_get_ray_info(lp);
row_or_col = lp.ray

File Input

In addition to programmatically defining a linear problem, there are methods to read linear programs and MIPs from files. We have seen the empty LPX constructor employed to create an empty problem. The LPX constructor also has the ability to accept a single keyword argument: the keywords specifies a file format, and the argument specifies the filename, as in lp=glpk.LPX(format=filename). If successfully read, an LPX instance will be created with the file data.

All formats accept a single string representing the path to the file to be read. Valid formats include the following.

The format gmp (GNU MathProg), in addition to accepting a single string argument, may optionally accept a three element tuple instead, containing these elements:

For the gmp option, if you input a single string filename instead of a tuple, it is equivalent to inputing the tuple (filename, None, None).

lp = lpx_read_mps("/path/to/mps_file")
lp = lpx_read_freemps("/path/to/free_mps_file")
lp = lpx_read_cpxlp("/path/to/cplexlp_file")
lp = lpx_read_model("modelfile", NULL, NULL)
lp = lpx_read_model("modelfile", "datafile", "output.txt")
lp = lpx_read_prob("/path/to/gnulp_file")
lp = glpk.LPX(mps="/path/to/mps_file")
lp = glpk.LPX(freemps="/path/to/free_mps_file")
lp = glpk.LPX(cpxlp="/path/to/cplexlp_file")
lp = glpk.LPX(gmp="modelfile")
lp = glpk.LPX(gmp=("modelfile", "datafile", "output.txt"))
lp = glpk.LPX(glp="/path/to/gnulp_file")

File Output

One may export data about a linear program to a file in a variety of formats conveying a variety of different types of information using the method write. The method accepts a large number of keyword arguments: each keyword specifies a file format, and the argument a file name, as in lp.write(format=filename). Upon invocation, the LPX object will attempt to write the data specified by the format into the indicated file.

Valid formats include the following.

Note that you can specify multiple formats and output files in a single call to write in order to write multiple files in multiple formats in one go. For example, you might want to simultaneously write out printable problem data, solutions, and bounds sensitivity information all in one go with something like lp.write(prob="foo.prob", sol="foo.sol", sens_bnds="foo.bnds") .

lpx_write_mps(lp, filename)
lpx_write_bas(lp, filename)
lpx_write_freemps(lp, filename)
lpx_write_prob(lp, filename)
lpx_write_cpxlp(lp, filename)
lpx_print_prob(lp, filename)
lpx_print_sol(lp, filename)
lpx_print_sens_bnds(lp, filename)
lpx_print_ips(lp, filename)
lpx_print_mip(lp, filename)
lp.write(mps=filename)
lp.write(bas=filename)
lp.write(freemps=filename)
lp.write(glp=filename)
lp.write(cpxlp=filename)
lp.write(prob=filename)
lp.write(sol=filename)
lp.write(sens_bnds=filename)
lp.write(ips=filename)
lp.write(mip=filename)

Objective Function

A linear program objective function specifies what linear function the LP is attempting to either minimize or maximize. Correspondingly, the objective object allows one to set objective function coefficients and the direction of optimization, and retrieve the objection function value after optimization.

The objective function for an LPX object lp is contained within lp.obj. This objects is an instance of the Objective class. Through this object one can set the objective coefficients and retrieve the objective value.

Naming Objective Function

Similar to how one names problems, one gets and sets the name for the objective function by querying or assigning to the name attribute. As with the C API, names are limited to 255 characters. Unset names are None. One unsets a name by assigning None or deleting the name.

lpx_set_obj_name(lp, "some name");
lpx_set_obj_name(lp, NULL);
char *thename = lpx_get_obj_name(lp);
lp.obj.name = "some name"
del lp.obj.name
thename = lp.obj.name

Minimize or Maximize

One gets and sets whether this is a minimization or maximization problem by querying or assigning to the maximize boolean attribute.

lpx_set_obj_dir(lp, LPX_MIN);
lpx_set_obj_dir(lp, LPX_MAX);
int ismax = (lpx_get_obj_dir(lp) == LPX_MAX);
lp.obj.maximize = False
lp.obj.maximize = True
ismax = lp.obj.maximize

Objective Coefficients

One gets and sets the objective function coefficients by indexing into the obj object, e.g., lp.obj[index]. There are as many objective coefficients as there are columns, so valid indices include 0 through len(lp.cols)-1 as well as (for negative indexing) -1 through -len(lp.cols).

One can access and change these objective coefficients through either a single index, or access or change multiple coefficients by defining multiple indices through either a series of indices or a slice.

When assigning new objective coefficients, valid assignments include single numbers (in which case all indexed coefficients receive this same value) or an iterable object (in which case all indexed coefficients receive values specified in turn).

The objective function's constant shift term can be accessed either by using None as an index, or by accessing the shift attribute, that is, lp.obj.shift.

lpx_set_obj_coef(lp, 2+1, 3.0);
for (i=0; i<lpx_get_num_cols(lp); ++i)
	lpx_set_obj_coef(lp, i+1, 1.0)
lpx_set_obj_coef(lp, 0+1, 3.14159); lpx_set_obj_coef(lp, 2+1, -2.0);
lpx_set_obj_coef(lp, 0, 0.5);
lpx_set_obj_coef(lp, lpx_get_num_cols(lp), 25.0);
double c = lpx_get_obj_coef(lp, 3+1);
double c1 = lpx_get_obj_coef(lp, 1+1), c2 = lpx_get_obj_coef(lp, 2+1);
lp.obj[2] = 3.0
lp.obj[:] = 1.0
lp.obj[0,2] = 3.14159, -2.0
lp.obj.shift = 0.5		# Alternately, lp.obj[None] = 0.5
lp.obj[-1] = 25.0
c = lp.obj[3]
c1, c2 = lp.obj[1,2]

Objective Function Value

One gets the value for the objective function by querying the value attribute.

double oval = lpx_get_obj_val(lp); // or lpx_(ipt|mip)_obj_val
oval = lp.obj.value

Unlike the C API, PyGLPK remembers which solver was used last and retrieves the corresponding objective function value. If for whatever reason you wish to retrieve an objective function from a solver type different from what you used last, you can force the issue by asking for value_s (for simplex and exact), or value_i (for interior), or value_m (for integer or intopt).

double soval = lpx_get_obj_value(lp);
double ioval = lpx_ipt_obj_value(lp);
double moval = lpx_mip_obj_value(lp);
soval = lp.obj.value_s
ioval = lp.obj.value_i
moval = lp.obj.value_m

Rows and Columns

In a linear program, rows and columns correspond to variables. Correspondingly, individual rows and column objects contain methods and data pertaining to individual variables: bounds, values after optimization, status, relevant entries of the constraint matrix, and other such objects.

Rows and columns live all live within two objects stored within an LPX object lp as lp.rows and lp.cols. Both of these objects is an instance of the BarCollection class. Individual rows and columns, all of type Bar, can be accessed by indexing or iteration over these collections.

Adding Rows and Columns

To add rows or columns, call the add method on either the row or column subcontainer. As in the C API, the newly created rows and columns are initially empty, and the return value of the add method holds the first newly valid index.

int rnew = lpx_add_rows(lp, nrs);
int cnew = lpx_add_cols(lp, ncs);
rnew = lp.rows.add(nrs)
cnew = lp.cols.add(ncs)

Number of Rows and Columns

One gets the number of rows or columns by querying the length of the LP's row and column containers.

int nrs = lpx_get_num_rows(lp);
int ncs = lpx_get_num_cols(lp);
nrs = len(lp.rows)
ncs = len(lp.cols)

Indexing Rows and Columns

One accesses particular rows and columns by indexing into the lp.rows and lp.cols collections. For example, lp.rows[ri] returns the row at index ri. This index may also be a negative index counting backwards from the end of the collection, e.g., lp.cols[-1] to get the last column of the LP.

These structures adopt much of the familiar behavior of Python sequences. Among other implications, this means that unlike in the C API, rows and columns are indexed from 0.

As we shall see, rows and columns can be named. One may also index named rows and columns by their names.

int rownum = lpx_find_row(lp, "rowname")
row = lp.rows["rowname"]

In addition to single integer or string values, one may specify multiple values in this index to retrieve a list of all specified rows or columns.

lp.cols[2,5,"bob",6] # columns 2, 5, one named "bob", 8

Indexing by slicing is supported as well. This will result in a list of all indices specified by the slice.

lp.rows[4:9]         # rows 4 through 8
lp.cols[-3:]         # the last 3 columns
lp.rows[::2]         # every row with an even index

One may also iterate over the lp.rows and lp.cols collections. Here is a comparative example of setting each column to name xi, where i is the index of this column, so the columns will be named x0, x1, x2, etc.

char buff[10];
for (i=1; i<=lpx_get_num_cols(lp); ++i) {
	snprintf(buff, sizeof(buff), "x%d", i-1);
	lpx_set_col_name(lp, i, buff);
}
for col in lp.cols:
	col.name = "x%d" % col.index

Naming Rows and Columns

As with the problem and the objective function, one gets and sets the name for a row or column by querying or assigning the attribute name.

Note the use of an index into the rows or cols collections to retrieve a particular row or columns. As with the C API, indices are integral, though we count from 0.

lpx_set_row_name(lp, ri+1, "row name");
lpx_set_col_name(lp, ci+1, "col name");
char *rname = lpx_get_row_name(lp, ri+1);
char *cname = lpx_get_col_name(lp, ci+1);
lp.rows[ri].name = "row name"
lp.cols[ci].name = "col name"
rname = lp.rows[ri].name
cname = lp.cols[ci].name

After the user names a row or column, they may index this row or column by its name.

lp.rows[ri].name = "xi"
therow = lp.rows["xi"]

Bounding Rows and Columns

One gets and sets the bounds for a row or column by querying or assigning the attribute bounds. To set bounds, one may assign one or two values to the bounds, where values are either None or numeric.

One None (or two Nones) sets the row's auxiliary (or column's structural) variable unbounded. (One may also delete the bounds.) One numeric value (or two equal numeric values) sets an equality bound. In the case of two values, the first is interpreted as a lower bound, the second as an upper bound, with None indicating unboundedness in that direction. Setting a lower bound greater than an upper bound causes a ValueError.

In this code, we see instances of setting free (unbounded), lower, upper, double, and fixed (equality) bounds, respectively on a row and column.

lpx_set_row_bnds(lp, ri+1, LPX_FR,  0,   0);
lpx_set_row_bnds(lp, ri+1, LPX_LO,  2,   0);
lpx_set_col_bnds(lp, ci+1, LPX_UP,  0,   5);
lpx_set_col_bnds(lp, ci+1, LPX_DB, -1,   3.14159);
lpx_set_row_bnds(lp, ri+1, LPX_FX,  3.4, 3.4);
lp.rows[ri].bounds = None        # Or, lp.rows[ri].bounds = None, None
                                 # Or, del lp.rows[ri].bounds
lp.rows[ri].bounds = 2, None
lp.cols[ci].bounds = None, 5
lp.cols[ci].bounds = -1, 3.14159
lp.rows[ri].bounds = 3.4         # Or, lp.rows[ri].bounds = 3.4, 3.4

Accessing bounds always yields two values (again, either None or numeric) representing lower and upper bounds respectively, even if the bounds resulted from either a single value assignment or a deletion. Again, None represents unboundedness in that direction.

Matrix Entries of Rows and Columns

One gets and sets the entries of a row or column in the constraint matrix by querying or assigning the attribute matrix. Assignments to matrix will remove previous entries.

Retrieving matrix will yeild a list of two element tuples over the non-zero entries in this row or column, each of the form (index, value). The index is the index (counting from 0) of the entry holding value value in this row or column.

If we have an LPX object with r rows and c columns, then valid indices for rows are 0 through c−1, and valid entries for columns are 0 through r−1.

For example, suppose for an object lp the row r2 (that is, row at index 2) encodes the constraint:

p = 10x0 − 3.14159x1 + 0.5x3

Then print lp.rows[2].matrix outputs [(0, 10.0), (1, -3.14159), (3, 0.5)].

One may set all non-zero entries of a row or column by assigning an iterable with similar structure to the matrix attribute. Suppose our LPX object has numr rows and numc columns. The iterable must yield values each in one of these two forms:

For example, where one interested in defining (rather than simply retrieving) the entries of the constraint row used in the example above, if there are four columns, all of the following are equivalent:

# Define constraint p = 10*x0 - 3.14159*x1 + 0.5*x3
lp.rows[2].matrix = [(0, 10), (1, -3.14159), (3, 0.5)]
lp.rows[2].matrix = [(0, 10), (1, -3.14159), (-1, 0.5)]
lp.rows[2].matrix = [(1, -3.14159), (0, 10), (3, 0.5)]
lp.rows[2].matrix = [10, -3.14159, 0, 0.5]
lp.rows[2].matrix = [10, -3.14159, (-1, 0.5)]
lp.rows[2].matrix = [10, -3.14159, (3, 0.5)]

Indices out of bounds will result in an IndexError and duplicate indices will result in an ValueError. Order does not matter, except of course for single value entries, as their index depends on the previous entry.

One may set all entries of a row or column in the constraint matrix to zero by assigning None to or deleting the matrix attribute.

Number of Non-Zero Constraint Row and Column Entries

One gets the number of non-zero constraint elements within a row or a column by querying the nnz integer attribute.

int rnnz = lpx_get_mat_row(lp, ri+1, NULL, NULL);
int cnnz = lpx_get_mat_col(lp, ci+1, NULL, NULL);
rnnz = lp.row[ri].nnz
cnnz = lp.col[ci].nnz

Deleting Rows and Columns

To delete rows or columns, delete as one would from a typical Python list. Note the methods of indexing into the row and column collections. Accepted indices include single values, lists of values, or slices.

int indices1[] = { 2+1 };
lpx_del_cols(lp, 1, indices1-1);
int indices2[] = { 2+1,5+1,6+1 };
lpx_del_rows(lp, 3, indices2-1);
int indices3[] = { 3+1, 4+1, 5+1, 6+1 };
lpx_del_cols(lp, 4, indices3-1);
del lp.cols[2]       # Remove col indexed at 2
del lp.rows[2,5,6]   # Remove rows indexed at 2,5,6
del lp.cols[3:7]     # Remove cols indexed at 3,4,5,6

Special Attributes of Rows and Columns

As is clear from the previous examples, row and column collections (e.g., as accessed by lp.rows) and rows and columns (e.g., as accessed by lp.rows[i]) as well as the rows and columns themselves are bona fide objects. (This was a design choice: rather than having only the LPX class where one defines a hundred or so get and set methods, as the C API must, one retrieves the rows and columns and operates on them instead.)

As a point of implementation, these row and column objects do not contain the row and column data. In reality, they just contain a pointer back to the LPX and an index. We shall see consequences of this in this subsection.

Aside from attributes which have obvious analogies to functions in the C API (e.g., name with the lpx_[gs]et_(row|col)_name functions), rows and columns have other special attributes that do not have analogies in the C API which are exposed to Python users in the hope they may find them useful.

index is an integer attribute containing the index of this row or column.

valid is a boolean attribute containing whether this row or column is valid. A row or column may become invalid if its index points to somewhere beyond the current size of the LPX. This is mostly useless: one can track the size of the program, and even if you do not, using an out of date row or column safely throws exceptions.

isrow and iscol are boolean attributes indicating whether this is a row or a column. Naturally these two attributes are inverses of each other.

Example usage of these principles to elucidate these implementations is illustrated in this example. All assertions in this snippet are satisfied.

lp = glpk.LPX()
lp.rows.add(3)
lp.rows[0].name, lp.rows[1].name, lp.rows[2].name = 'p', 'q', 'r'
row1, row2 = lp.rows[1], lp.rows[2]
assert row1.name == 'q' and row2.name == 'r'
del lp.rows[1]
assert row1.name == 'r' and row1.valid and not row2.valid
assert row1.isrow

Row and Column Basis Status

One gets and sets the current basis status for a row or column by querying or assigning the attribute status. This is a two-character string with the following possible values.

if (lpx_get_row_stat(lp, ri+1) == LPX_BS)
	printf("row is basic\n");
else
	printf("row is non-basic\n");
if lp.rows[ri].status == "bs":
	print "row is basic"
else:
	print "row is non-basic"

As an example of setting the status, the user may wish to assign to this attribute in order to manually define the initial basis and not rely upon the automatic basis definition methods lp.*_basis(). To illustrate this, here is the code within the GLPK standard basis code in both C and Python versions.

int i, j, m, n, type;
double lb, ub;
// all auxiliary variables are basic
m = lpx_get_num_rows(lp);
for (i = 1; i <= m; i++)
	lpx_set_row_stat(lp, i, LPX_BS);
// all structural variables are non-basic
n = lpx_get_num_cols(lp);
for (j = 1; j <= n; j++) {
	type = lpx_get_col_type(lp, j);
	lb = lpx_get_col_lb(lp, j);
	ub = lpx_get_col_ub(lp, j);
	if (type != LPX_DB || fabs(lb) <= fabs(ub))
		lpx_set_col_stat(lp, j, LPX_NL);
	else
		lpx_set_col_stat(lp, j, LPX_NU);
}
# all auxiliary variables are basic
for row in lp.rows:
	row.status = "bs"
# all structural variables are non-basic
for col in lp.cols:
	lb, ub = col.bounds
	if lb==None or ub==None or abs(lb)<=abs(ub):
		col.status = "nl"
	else:
		col.status = "nu"

Column Variable Kind, Continuous or Integer

One gets and sets the kind of variable (the default continuous, or integer) by querying or assigning the attribute kind. This will hold either float if this is a continuous variable, or int if this is an integer variable.

lpx_set_col_kind(lp, ci+1, LPX_CV);
lpx_set_col_kind(lp, ci+1, LPX_IV);
int kind = lpx_get_col_kind(lp, ci+1);
lp.cols[ci].kind = float
lp.cols[ci].kind = int
kind = lp.cols[ci].kind

Note that setting a column variable as integer requires that the lp be a MIP problem, i.e., lp.kind==int. In the event that lp.kind==float, all variable assignments to kind int will fail, and all variables will return float when asked their kind.

Another note, rows must be continuous. As a matter of implementation, because they are the same type of object as columns, they may also be queried and assigned to in this fashion. However, their kind attribute always returns and only accepts float.

Row and Column Variable Values

One gets a row or column's variable value by querying the primal, dual, or value attribute.

double pval = lpx_get_row_prim(ri+1); // if simplex
double dval = lpx_get_col_dual(ci+1);
pval = lp.rows[ri].primal
dval = lp.cols[ci].dual

The two attributes primal and value are interchangable. The term value is used to refer to the solutions of the MIP since it only has one type of value (no dual). However, since it is the same type of value (loosely speaking), this "link" between the two was established.

Note that unlike the C API, this remembers which solver was used last and retrieves the appropriate corresponding variable value. If for whatever reason you wish to retrieve a variable function from a solver type different from what you used last (e.g., reviewing the relaxed basic solution after calling integer()), you can ask for primal_s or dual_s (for primals and duals from simplex and exact), or primal_i or dual_i (for primals and duals from interior), or value_m (for values from integer or intopt).

double prim_sim_row = lpx_get_row_prim(lp, ri+1);
double prim_sim_col = lpx_get_col_prim(lp, ci+1);
double dual_sim_row = lpx_get_row_dual(lp, ri+1);
double dual_sim_col = lpx_get_col_dual(lp, ci+1);
double prim_ipt_row = lpx_ipt_row_prim(lp, ri+1);
double prim_ipt_col = lpx_ipt_col_prim(lp, ci+1);
double dual_ipt_row = lpx_ipt_row_dual(lp, ri+1);
double dual_ipt_col = lpx_ipt_col_dual(lp, ci+1);
double valu_mip_row = lpx_mip_row_val (lp, ri+1);
double valu_mip_col = lpx_mip_col_val (lp, ci+1);
prim_sim_row, prim_sim_col = lp.rows[ri].primal_s, lp.cols[ci].primal_s
dual_sim_row, dual_sim_col = lp.rows[ri].dual_s,   lp.cols[ci].dual_s
prim_ipt_row, prim_ipt_col = lp.rows[ri].primal_i, lp.cols[ci].primal_i
dual_ipt_row, dual_ipt_col = lp.rows[ri].dual_i,   lp.cols[ci].dual_i
valu_mip_row, valu_mip_col = lp.rows[ri].value_m,  lp.cols[ci].value_m

Parameter Collection

Contained with a linear program object lp is a parameter object param. This helps control the behavior of the GLPK.

Parameter Values

One gets and sets parameter values by querying or assigning to one of the many attributes of the lp.param object, i.e., lp.param.param_name.

For a descriptive enumeration of these parameters, either query the inline help for the Python object (i.e., help(lp.params)), or refer to the original GLPK C API reference manual. Each available C parameter identifier of the form LPX_K_PARAMNAME corresponds to an attribute of the lp.param.paramname. (That is, the last component downcased.)

lpx_set_int_parm(lp, LPX_K_ITLIM, 250);
double relax = lpx_get_real_parm(lp, LPX_K_RELAX);
lp.params.itlim = 250
relax = lp.params.relax

Resetting

One can reset the parameter values back to their default values by calling the reset method.

lpx_reset_parms(lp);
lp.params.reset()

Karush-Kuhn-Tucker Conditions

The linear program object has the ability to return a KKT type objects with which the user may evaluate the fitness of either simplex or MIP solutions.

Retrieving KKT Objects

One can compute and retrieve these conditions for simplex solvers with the kkt and for integer solvers with the kktint methods. The kkt method has an optional argument that allows one to specify whether one wants to compute the conditions for the internally scaled version of the problem (by default false).

LPXKKT kkt, skkt, ikkt;
lpx_check_kkt(lp, 0,  &kkt);  // unscaled simplex KKT
lpx_check_kkt(lp, 1, &skkt);  // scaled simplex KKT
lpx_check_int(lp,    &ikkt);  // integer conditions
kkt  = lp.kkt()     # unscaled simplex KKT
skkt = lp.kkt(True) # scaled simplex KKT
ikkt = lp.kktint()  # integer conditions

These objects have KKT statistics about the absolute and relative errors and worst rows and columns in the primal and (in the case of non-integer problems) dual solutions. See the inline help for more information about these fields.

Miscellaneous Notes

Help

In addition to this documentation, like most Python objects, the objects have built in inline help, accessible from an interactive Python session. For example, to access the built in documentation for the glpk module:

help(glpk)

Version

The module defines a tuple that reflects the version of GLPK that the build process believed it was linking against. For example, if the module believed it was linking against GLPK 4.15, the tuple would be (4, 15) .

glpk.version

Thomas Finley, 2007