Linear Functions and Constraints

This module implements linear functions (see LinearFunction) in formal variables and chained (in)equalities between them (see LinearConstraint). By convention, these are always written as either equalities or less-or-equal. For example:

sage: p = MixedIntegerLinearProgram()
sage: x = p.new_variable()
sage: f = 1 + x[1] + 2*x[2];  f     #  a linear function
1 + x_0 + 2*x_1
sage: type(f)
<class 'sage.numerical.linear_functions.LinearFunction'>

sage: c = (0 <= f);  c    # a constraint
0 <= 1 + x_0 + 2*x_1
sage: type(c)
<class 'sage.numerical.linear_functions.LinearConstraint'>
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> x = p.new_variable()
>>> f = Integer(1) + x[Integer(1)] + Integer(2)*x[Integer(2)];  f     #  a linear function
1 + x_0 + 2*x_1
>>> type(f)
<class 'sage.numerical.linear_functions.LinearFunction'>

>>> c = (Integer(0) <= f);  c    # a constraint
0 <= 1 + x_0 + 2*x_1
>>> type(c)
<class 'sage.numerical.linear_functions.LinearConstraint'>

Note that you can use this module without any reference to linear programming, it only implements linear functions over a base ring and constraints. However, for ease of demonstration we will always construct them out of linear programs (see mip).

Constraints can be equations or (non-strict) inequalities. They can be chained:

sage: p = MixedIntegerLinearProgram()
sage: x = p.new_variable()
sage: x[0] == x[1] == x[2] == x[3]
x_0 == x_1 == x_2 == x_3

sage: ieq_01234 = x[0] <= x[1] <= x[2] <= x[3] <= x[4]
sage: ieq_01234
x_0 <= x_1 <= x_2 <= x_3 <= x_4
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> x = p.new_variable()
>>> x[Integer(0)] == x[Integer(1)] == x[Integer(2)] == x[Integer(3)]
x_0 == x_1 == x_2 == x_3

>>> ieq_01234 = x[Integer(0)] <= x[Integer(1)] <= x[Integer(2)] <= x[Integer(3)] <= x[Integer(4)]
>>> ieq_01234
x_0 <= x_1 <= x_2 <= x_3 <= x_4

If necessary, the direction of inequality is flipped to always write inequalities as less or equal:

sage: x[5] >= ieq_01234
x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5

sage: (x[5] <= x[6]) >= ieq_01234
x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5 <= x_6
sage: (x[5] <= x[6]) <= ieq_01234
x_5 <= x_6 <= x_0 <= x_1 <= x_2 <= x_3 <= x_4
>>> from sage.all import *
>>> x[Integer(5)] >= ieq_01234
x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5

>>> (x[Integer(5)] <= x[Integer(6)]) >= ieq_01234
x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5 <= x_6
>>> (x[Integer(5)] <= x[Integer(6)]) <= ieq_01234
x_5 <= x_6 <= x_0 <= x_1 <= x_2 <= x_3 <= x_4

Warning

The implementation of chained inequalities uses a Python hack to make it work, so it is not completely robust. In particular, while constants are allowed, no two constants can appear next to each other. The following does not work for example:

sage: x[0] <= 3 <= 4
True
>>> from sage.all import *
>>> x[Integer(0)] <= Integer(3) <= Integer(4)
True

If you really need this for some reason, you can explicitly convert the constants to a LinearFunction:

sage: from sage.numerical.linear_functions import LinearFunctionsParent
sage: LF = LinearFunctionsParent(QQ)
sage: x[1] <= LF(3) <= LF(4)
x_1 <= 3 <= 4
>>> from sage.all import *
>>> from sage.numerical.linear_functions import LinearFunctionsParent
>>> LF = LinearFunctionsParent(QQ)
>>> x[Integer(1)] <= LF(Integer(3)) <= LF(Integer(4))
x_1 <= 3 <= 4
class sage.numerical.linear_functions.LinearConstraint[source]

Bases: LinearFunctionOrConstraint

A class to represent formal Linear Constraints.

A Linear Constraint being an inequality between two linear functions, this class lets the user write LinearFunction1 <= LinearFunction2 to define the corresponding constraint, which can potentially involve several layers of such inequalities (A <= B <= C), or even equalities like A == B == C.

Trivial constraints (meaning that they have only one term and no relation) are also allowed. They are required for the coercion system to work.

Warning

This class has no reason to be instantiated by the user, and is meant to be used by instances of MixedIntegerLinearProgram.

INPUT:

  • parent – the parent; a LinearConstraintsParent_class

  • terms – list/tuple/iterable of two or more linear functions (or things that can be converted into linear functions)

  • equality – boolean (default: False); whether the terms are the entries of a chained less-or-equal (<=) inequality or a chained equality

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: b = p.new_variable()
sage: b[2]+2*b[3] <= b[8]-5
x_0 + 2*x_1 <= -5 + x_2
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> b = p.new_variable()
>>> b[Integer(2)]+Integer(2)*b[Integer(3)] <= b[Integer(8)]-Integer(5)
x_0 + 2*x_1 <= -5 + x_2
equals(left, right)[source]

Compare left and right.

OUTPUT:

boolean; whether all terms of left and right are equal. Note that this is stronger than mathematical equivalence of the relations.

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: x = p.new_variable()
sage: (x[1] + 1 >= 2).equals(3/3 + 1*x[1] + 0*x[2] >= 8/4)
True
sage: (x[1] + 1 >= 2).equals(x[1] + 1-1 >= 1-1)
False
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> x = p.new_variable()
>>> (x[Integer(1)] + Integer(1) >= Integer(2)).equals(Integer(3)/Integer(3) + Integer(1)*x[Integer(1)] + Integer(0)*x[Integer(2)] >= Integer(8)/Integer(4))
True
>>> (x[Integer(1)] + Integer(1) >= Integer(2)).equals(x[Integer(1)] + Integer(1)-Integer(1) >= Integer(1)-Integer(1))
False
equations()[source]

Iterate over the unchained(!) equations.

OUTPUT:

An iterator over pairs (lhs, rhs) such that the individual equations are lhs == rhs.

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: b = p.new_variable()
sage: eqns = 1 == b[0] == b[2] == 3 == b[3];  eqns
1 == x_0 == x_1 == 3 == x_2
sage: for lhs, rhs in eqns.equations():
....:     print(str(lhs) + ' == ' + str(rhs))
1 == x_0
x_0 == x_1
x_1 == 3
3 == x_2
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> b = p.new_variable()
>>> eqns = Integer(1) == b[Integer(0)] == b[Integer(2)] == Integer(3) == b[Integer(3)];  eqns
1 == x_0 == x_1 == 3 == x_2
>>> for lhs, rhs in eqns.equations():
...     print(str(lhs) + ' == ' + str(rhs))
1 == x_0
x_0 == x_1
x_1 == 3
3 == x_2
inequalities()[source]

Iterate over the unchained(!) inequalities.

OUTPUT:

An iterator over pairs (lhs, rhs) such that the individual equations are lhs <= rhs.

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: b = p.new_variable()
sage: ieq = 1 <= b[0] <= b[2] <= 3 <= b[3]; ieq
1 <= x_0 <= x_1 <= 3 <= x_2

sage: for lhs, rhs in ieq.inequalities():
....:     print(str(lhs) + ' <= ' + str(rhs))
1 <= x_0
x_0 <= x_1
x_1 <= 3
3 <= x_2
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> b = p.new_variable()
>>> ieq = Integer(1) <= b[Integer(0)] <= b[Integer(2)] <= Integer(3) <= b[Integer(3)]; ieq
1 <= x_0 <= x_1 <= 3 <= x_2

>>> for lhs, rhs in ieq.inequalities():
...     print(str(lhs) + ' <= ' + str(rhs))
1 <= x_0
x_0 <= x_1
x_1 <= 3
3 <= x_2
is_equation()[source]

Whether the constraint is a chained equation.

OUTPUT: boolean

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: b = p.new_variable()
sage: (b[0] == b[1]).is_equation()
True
sage: (b[0] <= b[1]).is_equation()
False
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> b = p.new_variable()
>>> (b[Integer(0)] == b[Integer(1)]).is_equation()
True
>>> (b[Integer(0)] <= b[Integer(1)]).is_equation()
False
is_less_or_equal()[source]

Whether the constraint is a chained less-or_equal inequality.

OUTPUT: boolean

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: b = p.new_variable()
sage: (b[0] == b[1]).is_less_or_equal()
False
sage: (b[0] <= b[1]).is_less_or_equal()
True
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> b = p.new_variable()
>>> (b[Integer(0)] == b[Integer(1)]).is_less_or_equal()
False
>>> (b[Integer(0)] <= b[Integer(1)]).is_less_or_equal()
True
is_trivial()[source]

Test whether the constraint is trivial.

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: LC = p.linear_constraints_parent()
sage: ieq = LC(1,2);  ieq
1 <= 2
sage: ieq.is_trivial()
False

sage: ieq = LC(1);  ieq
trivial constraint starting with 1
sage: ieq.is_trivial()
True
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> LC = p.linear_constraints_parent()
>>> ieq = LC(Integer(1),Integer(2));  ieq
1 <= 2
>>> ieq.is_trivial()
False

>>> ieq = LC(Integer(1));  ieq
trivial constraint starting with 1
>>> ieq.is_trivial()
True
sage.numerical.linear_functions.LinearConstraintsParent()[source]

Return the parent for linear functions over base_ring.

The output is cached, so only a single parent is ever constructed for a given base ring.

INPUT:

OUTPUT: the parent of the linear constraints with the given linear functions

EXAMPLES:

sage: from sage.numerical.linear_functions import (
....:     LinearFunctionsParent, LinearConstraintsParent)
sage: LF = LinearFunctionsParent(QQ)
sage: LinearConstraintsParent(LF)
Linear constraints over Rational Field
>>> from sage.all import *
>>> from sage.numerical.linear_functions import (
...     LinearFunctionsParent, LinearConstraintsParent)
>>> LF = LinearFunctionsParent(QQ)
>>> LinearConstraintsParent(LF)
Linear constraints over Rational Field
class sage.numerical.linear_functions.LinearConstraintsParent_class[source]

Bases: Parent

Parent for LinearConstraint.

Warning

This class has no reason to be instantiated by the user, and is meant to be used by instances of MixedIntegerLinearProgram. Also, use the LinearConstraintsParent() factory function.

INPUT/OUTPUT: see LinearFunctionsParent()

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: LC = p.linear_constraints_parent();  LC
Linear constraints over Real Double Field
sage: from sage.numerical.linear_functions import LinearConstraintsParent
sage: LinearConstraintsParent(p.linear_functions_parent()) is LC
True
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> LC = p.linear_constraints_parent();  LC
Linear constraints over Real Double Field
>>> from sage.numerical.linear_functions import LinearConstraintsParent
>>> LinearConstraintsParent(p.linear_functions_parent()) is LC
True
linear_functions_parent()[source]

Return the parent for the linear functions.

EXAMPLES:

sage: LC = MixedIntegerLinearProgram().linear_constraints_parent()
sage: LC.linear_functions_parent()
Linear functions over Real Double Field
>>> from sage.all import *
>>> LC = MixedIntegerLinearProgram().linear_constraints_parent()
>>> LC.linear_functions_parent()
Linear functions over Real Double Field
class sage.numerical.linear_functions.LinearFunction[source]

Bases: LinearFunctionOrConstraint

An elementary algebra to represent symbolic linear functions.

Warning

You should never instantiate LinearFunction manually. Use the element constructor in the parent instead.

EXAMPLES:

For example, do this:

sage: p = MixedIntegerLinearProgram()
sage: parent = p.linear_functions_parent()
sage: parent({0 : 1, 3 : -8})
x_0 - 8*x_3
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> parent = p.linear_functions_parent()
>>> parent({Integer(0) : Integer(1), Integer(3) : -Integer(8)})
x_0 - 8*x_3

instead of this:

sage: from sage.numerical.linear_functions import LinearFunction
sage: LinearFunction(p.linear_functions_parent(), {0 : 1, 3 : -8})
x_0 - 8*x_3
>>> from sage.all import *
>>> from sage.numerical.linear_functions import LinearFunction
>>> LinearFunction(p.linear_functions_parent(), {Integer(0) : Integer(1), Integer(3) : -Integer(8)})
x_0 - 8*x_3
coefficient(x)[source]

Return one of the coefficients.

INPUT:

  • x – a linear variable or an integer; if an integer \(i\) is passed, then \(x_i\) is used as linear variable

OUTPUT:

A base ring element. The coefficient of x in the linear function. Pass -1 for the constant term.

EXAMPLES:

sage: mip.<b> = MixedIntegerLinearProgram()
sage: lf = -8 * b[3] + b[0] - 5;  lf
-5 - 8*x_0 + x_1
sage: lf.coefficient(b[3])
-8.0
sage: lf.coefficient(0)      # x_0 is b[3]
-8.0
sage: lf.coefficient(4)
0.0
sage: lf.coefficient(-1)
-5.0
>>> from sage.all import *
>>> mip = MixedIntegerLinearProgram(names=('b',)); (b,) = mip._first_ngens(1)
>>> lf = -Integer(8) * b[Integer(3)] + b[Integer(0)] - Integer(5);  lf
-5 - 8*x_0 + x_1
>>> lf.coefficient(b[Integer(3)])
-8.0
>>> lf.coefficient(Integer(0))      # x_0 is b[3]
-8.0
>>> lf.coefficient(Integer(4))
0.0
>>> lf.coefficient(-Integer(1))
-5.0
dict()[source]

Return the dictionary corresponding to the Linear Function.

OUTPUT:

The linear function is represented as a dictionary. The value are the coefficient of the variable represented by the keys ( which are integers ). The key -1 corresponds to the constant term.

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: LF = p.linear_functions_parent()
sage: lf = LF({0 : 1, 3 : -8})
sage: lf.dict()
{0: 1.0, 3: -8.0}
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> LF = p.linear_functions_parent()
>>> lf = LF({Integer(0) : Integer(1), Integer(3) : -Integer(8)})
>>> lf.dict()
{0: 1.0, 3: -8.0}
equals(left, right)[source]

Logically compare left and right.

OUTPUT: boolean

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: x = p.new_variable()
sage: (x[1] + 1).equals(3/3 + 1*x[1] + 0*x[2])
True
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> x = p.new_variable()
>>> (x[Integer(1)] + Integer(1)).equals(Integer(3)/Integer(3) + Integer(1)*x[Integer(1)] + Integer(0)*x[Integer(2)])
True
is_zero()[source]

Test whether self is zero.

OUTPUT: boolean

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: x = p.new_variable()
sage: (x[1] - x[1] + 0*x[2]).is_zero()
True
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> x = p.new_variable()
>>> (x[Integer(1)] - x[Integer(1)] + Integer(0)*x[Integer(2)]).is_zero()
True
iteritems()[source]

Iterate over the index, coefficient pairs.

OUTPUT:

An iterator over the (key, coefficient) pairs. The keys are integers indexing the variables. The key -1 corresponds to the constant term.

EXAMPLES:

sage: p = MixedIntegerLinearProgram(solver = 'ppl')
sage: x = p.new_variable()
sage: f = 0.5 + 3/2*x[1] + 0.6*x[3]
sage: for id, coeff in sorted(f.iteritems()):
....:     print('id = {}   coeff = {}'.format(id, coeff))
id = -1   coeff = 1/2
id = 0   coeff = 3/2
id = 1   coeff = 3/5
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram(solver = 'ppl')
>>> x = p.new_variable()
>>> f = RealNumber('0.5') + Integer(3)/Integer(2)*x[Integer(1)] + RealNumber('0.6')*x[Integer(3)]
>>> for id, coeff in sorted(f.iteritems()):
...     print('id = {}   coeff = {}'.format(id, coeff))
id = -1   coeff = 1/2
id = 0   coeff = 3/2
id = 1   coeff = 3/5
class sage.numerical.linear_functions.LinearFunctionOrConstraint[source]

Bases: ModuleElement

Base class for LinearFunction and LinearConstraint.

This class exists solely to implement chaining of inequalities in constraints.

sage.numerical.linear_functions.LinearFunctionsParent()[source]

Return the parent for linear functions over base_ring.

The output is cached, so only a single parent is ever constructed for a given base ring.

INPUT:

  • base_ring – a ring; the coefficient ring for the linear functions

OUTPUT: the parent of the linear functions over base_ring

EXAMPLES:

sage: from sage.numerical.linear_functions import LinearFunctionsParent
sage: LinearFunctionsParent(QQ)
Linear functions over Rational Field
>>> from sage.all import *
>>> from sage.numerical.linear_functions import LinearFunctionsParent
>>> LinearFunctionsParent(QQ)
Linear functions over Rational Field
class sage.numerical.linear_functions.LinearFunctionsParent_class[source]

Bases: Parent

The parent for all linear functions over a fixed base ring.

Warning

You should use LinearFunctionsParent() to construct instances of this class.

INPUT/OUTPUT: see LinearFunctionsParent()

EXAMPLES:

sage: from sage.numerical.linear_functions import LinearFunctionsParent_class
sage: LinearFunctionsParent_class
<class 'sage.numerical.linear_functions.LinearFunctionsParent_class'>
>>> from sage.all import *
>>> from sage.numerical.linear_functions import LinearFunctionsParent_class
>>> LinearFunctionsParent_class
<class 'sage.numerical.linear_functions.LinearFunctionsParent_class'>
gen(i)[source]

Return the linear variable \(x_i\).

INPUT:

  • i – nonnegative integer

OUTPUT: the linear function \(x_i\)

EXAMPLES:

sage: LF = MixedIntegerLinearProgram().linear_functions_parent()
sage: LF.gen(23)
x_23
>>> from sage.all import *
>>> LF = MixedIntegerLinearProgram().linear_functions_parent()
>>> LF.gen(Integer(23))
x_23
set_multiplication_symbol(symbol='*')[source]

Set the multiplication symbol when pretty-printing linear functions.

INPUT:

  • symbol – string (default: '*'); the multiplication symbol to be used

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: x = p.new_variable()
sage: f = -1-2*x[0]-3*x[1]
sage: LF = f.parent()
sage: LF._get_multiplication_symbol()
'*'
sage: f
-1 - 2*x_0 - 3*x_1
sage: LF.set_multiplication_symbol(' ')
sage: f
-1 - 2 x_0 - 3 x_1
sage: LF.set_multiplication_symbol()
sage: f
-1 - 2*x_0 - 3*x_1
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> x = p.new_variable()
>>> f = -Integer(1)-Integer(2)*x[Integer(0)]-Integer(3)*x[Integer(1)]
>>> LF = f.parent()
>>> LF._get_multiplication_symbol()
'*'
>>> f
-1 - 2*x_0 - 3*x_1
>>> LF.set_multiplication_symbol(' ')
>>> f
-1 - 2 x_0 - 3 x_1
>>> LF.set_multiplication_symbol()
>>> f
-1 - 2*x_0 - 3*x_1
tensor(free_module)[source]

Return the tensor product with free_module.

INPUT:

  • free_module – vector space or matrix space over the same base ring

OUTPUT:

Instance of sage.numerical.linear_tensor.LinearTensorParent_class.

EXAMPLES:

sage: LF = MixedIntegerLinearProgram().linear_functions_parent()
sage: LF.tensor(RDF^3)
Tensor product of Vector space of dimension 3 over Real Double Field
and Linear functions over Real Double Field
sage: LF.tensor(QQ^2)
Traceback (most recent call last):
...
ValueError: base rings must match
>>> from sage.all import *
>>> LF = MixedIntegerLinearProgram().linear_functions_parent()
>>> LF.tensor(RDF**Integer(3))
Tensor product of Vector space of dimension 3 over Real Double Field
and Linear functions over Real Double Field
>>> LF.tensor(QQ**Integer(2))
Traceback (most recent call last):
...
ValueError: base rings must match
sage.numerical.linear_functions.is_LinearConstraint(x)[source]

Test whether x is a linear constraint.

INPUT:

  • x – anything

OUTPUT: boolean

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: x = p.new_variable()
sage: ieq = (x[0] <= x[1])
sage: from sage.numerical.linear_functions import is_LinearConstraint
sage: is_LinearConstraint(ieq)
doctest:warning...
DeprecationWarning: The function is_LinearConstraint is deprecated;
use 'isinstance(..., LinearConstraint)' instead.
See https://github.com/sagemath/sage/issues/38184 for details.
True
sage: is_LinearConstraint('a string')
False
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> x = p.new_variable()
>>> ieq = (x[Integer(0)] <= x[Integer(1)])
>>> from sage.numerical.linear_functions import is_LinearConstraint
>>> is_LinearConstraint(ieq)
doctest:warning...
DeprecationWarning: The function is_LinearConstraint is deprecated;
use 'isinstance(..., LinearConstraint)' instead.
See https://github.com/sagemath/sage/issues/38184 for details.
True
>>> is_LinearConstraint('a string')
False
sage.numerical.linear_functions.is_LinearFunction(x)[source]

Test whether x is a linear function.

INPUT:

  • x – anything

OUTPUT: boolean

EXAMPLES:

sage: p = MixedIntegerLinearProgram()
sage: x = p.new_variable()
sage: from sage.numerical.linear_functions import is_LinearFunction
sage: is_LinearFunction(x[0] - 2*x[2])
doctest:warning...
DeprecationWarning: The function is_LinearFunction is deprecated;
use 'isinstance(..., LinearFunction)' instead.
See https://github.com/sagemath/sage/issues/38184 for details.
True
sage: is_LinearFunction('a string')
False
>>> from sage.all import *
>>> p = MixedIntegerLinearProgram()
>>> x = p.new_variable()
>>> from sage.numerical.linear_functions import is_LinearFunction
>>> is_LinearFunction(x[Integer(0)] - Integer(2)*x[Integer(2)])
doctest:warning...
DeprecationWarning: The function is_LinearFunction is deprecated;
use 'isinstance(..., LinearFunction)' instead.
See https://github.com/sagemath/sage/issues/38184 for details.
True
>>> is_LinearFunction('a string')
False