Logging Backend#

It records, for debugging and unit testing purposes, all calls to backend methods in one of three ways.

See LoggingBackendFactory for more information.

class sage.numerical.backends.logging_backend.LoggingBackend(backend, printing=True, doctest=None, test_method=None, base_ring=None)#

Bases: GenericBackend

See LoggingBackendFactory for documentation.

EXAMPLES:

sage: import sage.numerical.backends.logging_backend
sage: from sage.numerical.backends.logging_backend import LoggingBackend
sage: from sage.numerical.backends.generic_backend import get_solver
sage: b = get_solver(solver = "GLPK")
sage: lb = LoggingBackend(backend=b)
sage: lb.add_variable(obj=42, name='Helloooooo')
# p.add_variable(obj=42, name='Helloooooo')
# result: 0
0
sage: lb.add_variable(obj=1789)
# p.add_variable(obj=1789)
# result: 1
1
add_col(indices, coeffs)#

Add a column.

INPUT:

  • indices (list of integers) – this list contains the indices of the constraints in which the variable’s coefficient is nonzero

  • coeffs (list of real values) – associates a coefficient to the variable in each of the constraints in which it appears. Namely, the i-th entry of coeffs corresponds to the coefficient of the variable in the constraint represented by the i-th entry in indices.

Note

indices and coeffs are expected to be of the same length.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.ncols()
0
sage: p.nrows()
0
sage: p.add_linear_constraints(5, 0, None)
sage: p.add_col(list(range(5)), list(range(5)))
sage: p.nrows()
5
add_linear_constraint(coefficients, lower_bound, upper_bound, name=None)#

Add a linear constraint.

INPUT:

  • coefficients – an iterable of pairs (i, v). In each pair, i is a variable index (integer) and v is a value (element of base_ring()).

  • lower_bound – element of base_ring() or None. The lower bound.

  • upper_bound – element of base_ring() or None. The upper bound.

  • name – string or None. Optional name for this row.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(5)
4
sage: p.add_linear_constraint( zip(range(5), range(5)), 2.0, 2.0)
sage: p.row(0)
([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])
sage: p.row_bounds(0)
(2.0, 2.0)
sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo')
sage: p.row_name(1)
'foo'
add_linear_constraint_vector(degree, coefficients, lower_bound, upper_bound, name=None)#

Add a vector-valued linear constraint.

Note

This is the generic implementation, which will split the vector-valued constraint into components and add these individually. Backends are encouraged to replace it with their own optimized implementation.

INPUT:

  • degree – integer. The vector degree, that is, the number of new scalar constraints.

  • coefficients – an iterable of pairs (i, v). In each pair, i is a variable index (integer) and v is a vector (real and of length degree).

  • lower_bound – either a vector or None. The component-wise lower bound.

  • upper_bound – either a vector or None. The component-wise upper bound.

  • name – string or None. An optional name for all new rows.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: coeffs = ([0, vector([1, 2])], [1, vector([2, 3])])
sage: upper = vector([5, 5])
sage: lower = vector([0, 0])
sage: p.add_variables(2)
1
sage: p.add_linear_constraint_vector(2, coeffs, lower, upper, 'foo')
add_linear_constraints(number, lower_bound, upper_bound, names=None)#

Add 'number linear constraints.

INPUT:

  • number (integer) – the number of constraints to add.

  • lower_bound - a lower bound, either a real value or None

  • upper_bound - an upper bound, either a real value or None

  • names - an optional list of names (default: None)

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(5)
5
sage: p.add_linear_constraints(5, None, 2)
sage: p.row(4)
([], [])
sage: p.row_bounds(4)
(None, 2.0)
add_variable(*args, **kwdargs)#

Add a variable.

This amounts to adding a new column to the matrix. By default, the variable is both positive and real.

INPUT:

  • lower_bound - the lower bound of the variable (default: 0)

  • upper_bound - the upper bound of the variable (default: None)

  • binary - True if the variable is binary (default: False).

  • continuous - True if the variable is continuous (default: True).

  • integer - True if the variable is integral (default: False).

  • obj - (optional) coefficient of this variable in the objective function (default: 0.0)

  • name - an optional name for the newly added variable (default: None).

OUTPUT: The index of the newly created variable

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.ncols()
0
sage: p.add_variable()
0
sage: p.ncols()
1
sage: p.add_variable(binary=True)
1
sage: p.add_variable(lower_bound=-2.0, integer=True)
2
sage: p.add_variable(continuous=True, integer=True)
Traceback (most recent call last):
...
ValueError: ...
sage: p.add_variable(name='x', obj=1.0)
3
sage: p.col_name(3)
'x'
sage: p.objective_coefficient(3)
1.0
add_variables(*args, **kwdargs)#

Add n variables.

This amounts to adding new columns to the matrix. By default, the variables are both nonnegative and real.

INPUT:

  • n - the number of new variables (must be > 0)

  • lower_bound - the lower bound of the variable (default: 0)

  • upper_bound - the upper bound of the variable (default: None)

  • binary - True if the variable is binary (default: False).

  • continuous - True if the variable is binary (default: True).

  • integer - True if the variable is binary (default: False).

  • obj - (optional) coefficient of all variables in the objective function (default: 0.0)

  • names - optional list of names (default: None)

OUTPUT: The index of the variable created last.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.ncols()
0
sage: p.add_variables(5)
4
sage: p.ncols()
5
sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b'])
6
base_ring()#

Return the base ring.

The backend’s base ring can be overridden. It is best to run the tests with GLPK and override the base ring to QQ. Then default input to backend methods, prepared by MixedIntegerLinearProgram, depends on the base ring. This way input will be rational and so suitable for both exact and inexact methods; whereas output will be float and will thus trigger assertAlmostEqual() tests.

EXAMPLES:

sage: import sage.numerical.backends.logging_backend
sage: from sage.numerical.backends.logging_backend import LoggingBackend
sage: from sage.numerical.backends.generic_backend import get_solver
sage: b = get_solver(solver = "GLPK")
sage: lb = LoggingBackend(backend=b)
sage: lb.base_ring()
Real Double Field
sage: from sage.rings.rational_field import QQ
sage: lb = LoggingBackend(backend=b, base_ring=QQ)
sage: lb.base_ring()
Rational Field
best_known_objective_bound()#

Return the value of the currently best known bound.

This method returns the current best upper (resp. lower) bound on the optimal value of the objective function in a maximization (resp. minimization) problem. It is equal to the output of get_objective_value() if the MILP found an optimal solution, but it can differ if it was interrupted manually or after a time limit (cf solver_parameter()).

Note

Has no meaning unless solve has been called before.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: p = MixedIntegerLinearProgram(solver="Nonexistent_LP_solver")
sage: b = p.new_variable(binary=True)
sage: for u,v in graphs.CycleGraph(5).edges(labels=False):
....:     p.add_constraint(b[u]+b[v]<=1)
sage: p.set_objective(p.sum(b[x] for x in range(5)))
sage: p.solve()
2.0
sage: pb = p.get_backend()
sage: pb.get_objective_value()
2.0
sage: pb.best_known_objective_bound()
2.0
category()#
col_bounds(index)#

Return the bounds of a specific variable.

INPUT:

  • index (integer) – the variable’s id.

OUTPUT:

A pair (lower_bound, upper_bound). Each of them can be set to None if the variable is not bounded in the corresponding direction, and is a real value otherwise.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variable()
0
sage: p.col_bounds(0)
(0.0, None)
sage: p.variable_upper_bound(0, 5)
sage: p.col_bounds(0)
(0.0, 5.0)
col_name(index)#

Return the index-th column name

INPUT:

  • index (integer) – the column id

  • name (char *) – its name. When set to NULL (default), the method returns the current name.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variable(name="I am a variable")
1
sage: p.col_name(0)
'I am a variable'
copy()#

Returns a copy of self.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = MixedIntegerLinearProgram(solver="Nonexistent_LP_solver")
sage: b = p.new_variable()
sage: p.add_constraint(b[1] + b[2] <= 6)
sage: p.set_objective(b[1] + b[2])
sage: copy(p).solve()
6.0
dump(filename, compress=True)#

Same as self.save(filename, compress)

dumps(compress=True)#

Dump self to a string s, which can later be reconstituted as self using loads(s).

There is an optional boolean argument compress which defaults to True.

EXAMPLES:

sage: from sage.misc.persist import comp
sage: O = SageObject()
sage: p_comp = O.dumps()
sage: p_uncomp = O.dumps(compress=False)
sage: comp.decompress(p_comp) == p_uncomp
True
sage: import pickletools
sage: pickletools.dis(p_uncomp)
    0: \x80 PROTO      2
    2: c    GLOBAL     'sage.structure.sage_object SageObject'
   41: q    BINPUT     ...
   43: )    EMPTY_TUPLE
   44: \x81 NEWOBJ
   45: q    BINPUT     ...
   47: .    STOP
highest protocol among opcodes = 2
get_custom_name()#

Return the custom name of this object, or None if it is not renamed.

EXAMPLES:

sage: P.<x> = QQ[]
sage: P.get_custom_name() is None
True
sage: P.rename('A polynomial ring')
sage: P.get_custom_name()
'A polynomial ring'
sage: P.reset_name()
sage: P.get_custom_name() is None
True
get_objective_value()#

Return the value of the objective function.

Note

Behavior is undefined unless solve has been called before.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(2)
1
sage: p.add_linear_constraint([(0,1), (1,2)], None, 3)
sage: p.set_objective([2, 5])
sage: p.solve()
0
sage: p.get_objective_value()
7.5
sage: p.get_variable_value(0)
0.0
sage: p.get_variable_value(1)
1.5
get_relative_objective_gap()#

Return the relative objective gap of the best known solution.

For a minimization problem, this value is computed by \((\texttt{bestinteger} - \texttt{bestobjective}) / (1e-10 + |\texttt{bestobjective}|)\), where bestinteger is the value returned by get_objective_value() and bestobjective is the value returned by best_known_objective_bound(). For a maximization problem, the value is computed by \((\texttt{bestobjective} - \texttt{bestinteger}) / (1e-10 + |\texttt{bestobjective}|)\).

Note

Has no meaning unless solve has been called before.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: p = MixedIntegerLinearProgram(solver="Nonexistent_LP_solver")
sage: b = p.new_variable(binary=True)
sage: for u,v in graphs.CycleGraph(5).edges(labels=False):
....:     p.add_constraint(b[u]+b[v]<=1)
sage: p.set_objective(p.sum(b[x] for x in range(5)))
sage: p.solve()
2.0
sage: pb = p.get_backend()
sage: pb.get_objective_value()
2.0
sage: pb.get_relative_objective_gap()
0.0
get_variable_value(variable)#

Return the value of a variable given by the solver.

Note

Behavior is undefined unless solve has been called before.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(2)
1
sage: p.add_linear_constraint([(0,1), (1, 2)], None, 3)
sage: p.set_objective([2, 5])
sage: p.solve()
0
sage: p.get_objective_value()
7.5
sage: p.get_variable_value(0)
0.0
sage: p.get_variable_value(1)
1.5
is_maximization(*args, **kwdargs)#

Test whether the problem is a maximization

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.is_maximization()
True
sage: p.set_sense(-1)
sage: p.is_maximization()
False
is_slack_variable_basic(*args, **kwdargs)#

Test whether the slack variable of the given row is basic.

This assumes that the problem has been solved with the simplex method and a basis is available. Otherwise an exception will be raised.

INPUT:

  • index (integer) – the variable’s id

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: p = MixedIntegerLinearProgram(maximization=True,
....:                               solver="Nonexistent_LP_solver")
sage: x = p.new_variable(nonnegative=True)
sage: p.add_constraint(-x[0] + x[1] <= 2)
sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17)
sage: p.set_objective(5.5 * x[0] - 3 * x[1])
sage: b = p.get_backend()
sage: # Backend-specific commands to instruct solver to use simplex method here
sage: b.solve()
0
sage: b.is_slack_variable_basic(0)
True
sage: b.is_slack_variable_basic(1)
False
is_slack_variable_nonbasic_at_lower_bound(*args, **kwdargs)#

Test whether the given variable is nonbasic at lower bound.

This assumes that the problem has been solved with the simplex method and a basis is available. Otherwise an exception will be raised.

INPUT:

  • index (integer) – the variable’s id

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: p = MixedIntegerLinearProgram(maximization=True,
....:                               solver="Nonexistent_LP_solver")
sage: x = p.new_variable(nonnegative=True)
sage: p.add_constraint(-x[0] + x[1] <= 2)
sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17)
sage: p.set_objective(5.5 * x[0] - 3 * x[1])
sage: b = p.get_backend()
sage: # Backend-specific commands to instruct solver to use simplex method here
sage: b.solve()
0
sage: b.is_slack_variable_nonbasic_at_lower_bound(0)
False
sage: b.is_slack_variable_nonbasic_at_lower_bound(1)
True
is_variable_basic(*args, **kwdargs)#

Test whether the given variable is basic.

This assumes that the problem has been solved with the simplex method and a basis is available. Otherwise an exception will be raised.

INPUT:

  • index (integer) – the variable’s id

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: p = MixedIntegerLinearProgram(maximization=True,
....:                               solver="Nonexistent_LP_solver")
sage: x = p.new_variable(nonnegative=True)
sage: p.add_constraint(-x[0] + x[1] <= 2)
sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17)
sage: p.set_objective(5.5 * x[0] - 3 * x[1])
sage: b = p.get_backend()
sage: # Backend-specific commands to instruct solver to use simplex method here
sage: b.solve()
0
sage: b.is_variable_basic(0)
True
sage: b.is_variable_basic(1)
False
is_variable_binary(*args, **kwdargs)#

Test whether the given variable is of binary type.

INPUT:

  • index (integer) – the variable’s id

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.ncols()
0
sage: p.add_variable()
0
sage: p.set_variable_type(0,0)
sage: p.is_variable_binary(0)
True
is_variable_continuous(*args, **kwdargs)#

Test whether the given variable is of continuous/real type.

INPUT:

  • index (integer) – the variable’s id

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.ncols()
0
sage: p.add_variable()
0
sage: p.is_variable_continuous(0)
True
sage: p.set_variable_type(0,1)
sage: p.is_variable_continuous(0)
False
is_variable_integer(*args, **kwdargs)#

Test whether the given variable is of integer type.

INPUT:

  • index (integer) – the variable’s id

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.ncols()
0
sage: p.add_variable()
0
sage: p.set_variable_type(0,1)
sage: p.is_variable_integer(0)
True
is_variable_nonbasic_at_lower_bound(*args, **kwdargs)#

Test whether the given variable is nonbasic at lower bound.

This assumes that the problem has been solved with the simplex method and a basis is available. Otherwise an exception will be raised.

INPUT:

  • index (integer) – the variable’s id

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: p = MixedIntegerLinearProgram(maximization=True,
....:                               solver="Nonexistent_LP_solver")
sage: x = p.new_variable(nonnegative=True)
sage: p.add_constraint(-x[0] + x[1] <= 2)
sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17)
sage: p.set_objective(5.5 * x[0] - 3 * x[1])
sage: b = p.get_backend()
sage: # Backend-specific commands to instruct solver to use simplex method here
sage: b.solve()
0
sage: b.is_variable_nonbasic_at_lower_bound(0)
False
sage: b.is_variable_nonbasic_at_lower_bound(1)
True
ncols(*args, **kwdargs)#

Return the number of columns/variables.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.ncols()
0
sage: p.add_variables(2)
1
sage: p.ncols()
2
nrows(*args, **kwdargs)#

Return the number of rows/constraints.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.nrows()
0
sage: p.add_linear_constraints(2, 2.0, None)
sage: p.nrows()
2
objective_coefficient(variable, coeff=None)#

Set or get the coefficient of a variable in the objective function

INPUT:

  • variable (integer) – the variable’s id

  • coeff (double) – its coefficient

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variable()
0
sage: p.objective_coefficient(0)
0.0
sage: p.objective_coefficient(0,2)
sage: p.objective_coefficient(0)
2.0
objective_constant_term(d=None)#

Set or get the constant term in the objective function

INPUT:

  • d (double) – its coefficient. If \(None\) (default), return the current value.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.objective_constant_term()
0.0
sage: p.objective_constant_term(42)
sage: p.objective_constant_term()
42.0
parent()#

Return the type of self to support the coercion framework.

EXAMPLES:

sage: t = log(sqrt(2) - 1) + log(sqrt(2) + 1); t                            # needs sage.symbolic
log(sqrt(2) + 1) + log(sqrt(2) - 1)
sage: u = t.maxima_methods()                                                # needs sage.symbolic
sage: u.parent()                                                            # needs sage.symbolic
<class 'sage.symbolic.maxima_wrapper.MaximaWrapper'>
problem_name(name=None)#

Return or define the problem’s name

INPUT:

  • name (str) – the problem’s name. When set to None (default), the method returns the problem’s name.

EXAMPLES:

sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")   # optional - Nonexistent_LP_solver
sage: p.problem_name("There once was a french fry") # optional - Nonexistent_LP_solver
sage: print(p.problem_name())                       # optional - Nonexistent_LP_solver
There once was a french fry
remove_constraint(i)#

Remove a constraint.

INPUT:

  • i – index of the constraint to remove.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: p = MixedIntegerLinearProgram(solver="Nonexistent_LP_solver")
sage: v = p.new_variable(nonnegative=True)
sage: x,y = v[0], v[1]
sage: p.add_constraint(2*x + 3*y, max=6)
sage: p.add_constraint(3*x + 2*y, max=6)
sage: p.set_objective(x + y + 7)
sage: p.set_integer(x); p.set_integer(y)
sage: p.solve()
9.0
sage: p.remove_constraint(0)
sage: p.solve()
10.0
sage: p.get_values([x,y])
[0.0, 3.0]
remove_constraints(constraints)#

Remove several constraints.

INPUT:

  • constraints – an iterable containing the indices of the rows to remove.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(2)
1
sage: p.add_linear_constraint([(0, 2), (1, 3)], None, 6)
sage: p.add_linear_constraint([(0, 3), (1, 2)], None, 6)
sage: p.remove_constraints([0, 1])
rename(x=None)#

Change self so it prints as x, where x is a string.

If x is None, the existing custom name is removed.

Note

This is only supported for Python classes that derive from SageObject.

EXAMPLES:

sage: x = PolynomialRing(QQ, 'x', sparse=True).gen()
sage: g = x^3 + x - 5
sage: g
x^3 + x - 5
sage: g.rename('a polynomial')
sage: g
a polynomial
sage: g + x
x^3 + 2*x - 5
sage: h = g^100
sage: str(h)[:20]
'x^300 + 100*x^298 - '
sage: h.rename('x^300 + ...')
sage: h
x^300 + ...
sage: g.rename(None)
sage: g
x^3 + x - 5

Real numbers are not Python classes, so rename is not supported:

sage: a = 3.14
sage: type(a)                                                               # needs sage.rings.real_mpfr
<... 'sage.rings.real_mpfr.RealLiteral'>
sage: a.rename('pi')                                                        # needs sage.rings.real_mpfr
Traceback (most recent call last):
...
NotImplementedError: object does not support renaming: 3.14000000000000

Note

The reason C-extension types are not supported by default is if they were then every single one would have to carry around an extra attribute, which would be slower and waste a lot of memory.

To support them for a specific class, add a cdef public _SageObject__custom_name attribute.

reset_name()#

Remove the custom name of an object.

EXAMPLES:

sage: P.<x> = QQ[]
sage: P
Univariate Polynomial Ring in x over Rational Field
sage: P.rename('A polynomial ring')
sage: P
A polynomial ring
sage: P.reset_name()
sage: P
Univariate Polynomial Ring in x over Rational Field
row(i)#

Return a row

INPUT:

  • index (integer) – the constraint’s id.

OUTPUT:

A pair (indices, coeffs) where indices lists the entries whose coefficient is nonzero, and to which coeffs associates their coefficient on the model of the add_linear_constraint method.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(5)
4
sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2)
sage: p.row(0)
([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0]) ## FIXME: Why backwards?
sage: p.row_bounds(0)
(2.0, 2.0)
row_bounds(index)#

Return the bounds of a specific constraint.

INPUT:

  • index (integer) – the constraint’s id.

OUTPUT:

A pair (lower_bound, upper_bound). Each of them can be set to None if the constraint is not bounded in the corresponding direction, and is a real value otherwise.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(5)
4
sage: p.add_linear_constraint(list(range(5)), list(range(5)), 2, 2)
sage: p.row(0)
([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0]) ## FIXME: Why backwards?
sage: p.row_bounds(0)
(2.0, 2.0)
row_name(index)#

Return the index th row name

INPUT:

  • index (integer) – the row’s id

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1'])
sage: p.row_name(0)
'Empty constraint 1'
save(filename=None, compress=True)#

Save self to the given filename.

EXAMPLES:

sage: # needs sage.symbolic
sage: x = SR.var("x")
sage: f = x^3 + 5
sage: from tempfile import NamedTemporaryFile
sage: with NamedTemporaryFile(suffix=".sobj") as t:
....:     f.save(t.name)
....:     load(t.name)
x^3 + 5
set_objective(coeff, d=0.0)#

Set the objective function.

INPUT:

  • coeff – a list of real values, whose i-th element is the coefficient of the i-th variable in the objective function.

  • d (double) – the constant term in the linear function (set to \(0\) by default)

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(5)
4
sage: p.set_objective([1, 1, 2, 1, 3])
sage: [p.objective_coefficient(x) for x in range(5)]
[1.0, 1.0, 2.0, 1.0, 3.0]

Constants in the objective function are respected:

sage: # optional - nonexistent_lp_solver
sage: p = MixedIntegerLinearProgram(solver='Nonexistent_LP_solver')
sage: x,y = p[0], p[1]
sage: p.add_constraint(2*x + 3*y, max=6)
sage: p.add_constraint(3*x + 2*y, max=6)
sage: p.set_objective(x + y + 7)
sage: p.set_integer(x); p.set_integer(y)
sage: p.solve()
9.0
set_sense(sense)#

Set the direction (maximization/minimization).

INPUT:

  • sense (integer) :

    • +1 => Maximization

    • -1 => Minimization

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.is_maximization()
True
sage: p.set_sense(-1)
sage: p.is_maximization()
False
set_variable_type(variable, vtype)#

Set the type of a variable

INPUT:

  • variable (integer) – the variable’s id

  • vtype (integer):

    • \(1\) Integer

    • \(0\) Binary

    • \(-1\) Continuous

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.ncols()
0
sage: p.add_variable()
0
sage: p.set_variable_type(0,1)
sage: p.is_variable_integer(0)
True
set_verbosity(level)#

Set the log (verbosity) level

INPUT:

  • level (integer) – From 0 (no verbosity) to 3.

EXAMPLES:

sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")  # optional - Nonexistent_LP_solver
sage: p.set_verbosity(2)                                # optional - Nonexistent_LP_solver
solve(*args, **kwdargs)#

Solve the problem.

Note

This method raises MIPSolverException exceptions when the solution cannot be computed for any reason (none exists, or the LP solver was not able to find it, etc…)

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_linear_constraints(5, 0, None)
sage: p.add_col(list(range(5)), list(range(5)))
sage: p.solve()
0
sage: p.objective_coefficient(0,1)
sage: p.solve()
Traceback (most recent call last):
...
MIPSolverException: ...
solver_parameter(name, value=None)#

Return or define a solver parameter

INPUT:

  • name (string) – the parameter

  • value – the parameter’s value if it is to be defined, or None (default) to obtain its current value.

Note

The list of available parameters is available at solver_parameter().

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.solver_parameter("timelimit")
sage: p.solver_parameter("timelimit", 60)
sage: p.solver_parameter("timelimit")
variable_lower_bound(index, value=False)#

Return or define the lower bound on a variable

INPUT:

  • index (integer) – the variable’s id

  • value – real value, or None to mean that the variable has not lower bound. When set to False (default), the method returns the current value.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variable()
0
sage: p.col_bounds(0)
(0.0, None)
sage: p.variable_lower_bound(0, 5)
sage: p.col_bounds(0)
(5.0, None)
variable_upper_bound(index, value=False)#

Return or define the upper bound on a variable

INPUT:

  • index (integer) – the variable’s id

  • value – real value, or None to mean that the variable has not upper bound. When set to False (default), the method returns the current value.

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variable()
0
sage: p.col_bounds(0)
(0.0, None)
sage: p.variable_upper_bound(0, 5)
sage: p.col_bounds(0)
(0.0, 5.0)
write_lp(name)#

Write the problem to a .lp file

INPUT:

  • filename (string)

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(2)
2
sage: p.add_linear_constraint([(0, 1], (1, 2)], None, 3)
sage: p.set_objective([2, 5])
sage: from tempfile import NamedTemporaryFile
sage: with NamedTemporaryFile(suffix=".lp") as f:
....:     p.write_lp(f.name)
write_mps(name, modern)#

Write the problem to a .mps file

INPUT:

  • filename (string)

EXAMPLES:

sage: # optional - nonexistent_lp_solver
sage: from sage.numerical.backends.generic_backend import get_solver
sage: p = get_solver(solver="Nonexistent_LP_solver")
sage: p.add_variables(2)
2
sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3)
sage: p.set_objective([2, 5])
sage: from tempfile import NamedTemporaryFile
sage: with NamedTemporaryFile(suffix=".lp") as f:
....:     p.write_lp(f.name)
sage.numerical.backends.logging_backend.LoggingBackendFactory(solver=None, printing=True, doctest_file=None, test_method_file=None, test_method=None, base_ring=Rational Field)#

Factory that constructs a LoggingBackend for debugging and testing.

An instance of it can be passed as the solver argument of sage.numerical.backends.generic_backend.get_solver() and MixedIntegerLinearProgram.

EXAMPLES:

Assume that we have the following function that does some computation using MixedIntegerLinearProgram (or MIP backend methods), and suppose we have observed that it works with the GLPK backend, but not with the COIN backend:

sage: def compute_something(solver='GLPK'):
....:     from sage.numerical.mip import MIPSolverException
....:     mip = MixedIntegerLinearProgram(solver=solver)
....:     lb = mip.get_backend()
....:     lb.add_variable(obj=42, name='Helloooooo')
....:     lb.add_variable(obj=1789)
....:     try:
....:         lb.solve()
....:     except MIPSolverException:
....:         return 4711
....:     else:
....:         return 91

We can investigate what the backend methods are doing by running a LoggingBackend in its in-terminal logging mode:

sage: import sage.numerical.backends.logging_backend
sage: from sage.numerical.backends.logging_backend import LoggingBackendFactory
sage: compute_something(solver = LoggingBackendFactory(solver='GLPK'))
# p = get_solver(solver='GLPK')
# p.add_variable(obj=42, name='Helloooooo')
# result: 0
# p.add_variable(obj=1789)
# result: 1
# p.solve()
# exception: GLPK: The LP (relaxation) problem has no dual feasible solution
4711

By replacing ‘GLPK’ by ‘COIN’ above, we can then compare the two logs and see where they differ.

Imagine that we have now fixed the bug in the COIN backend, and we want to add a doctest that documents this fact. We do not want to call compute_something in the doctest, but rather just have a sequence of calls to backend methods.

We can have the doctest autogenerated by running a LoggingBackend in its doctest-writing mode:

sage: fname = tmp_filename()
sage: compute_something(solver = LoggingBackendFactory(solver='GLPK', printing=False,
....:                                                  doctest_file=fname))
4711
sage: with open(fname) as f:
....:     for line in f.readlines(): _ = sys.stdout.write('|{}'.format(line))
|        sage: p = get_solver(solver='GLPK')
|        sage: p.add_variable(obj=42, name='Helloooooo')
|        0
|        sage: p.add_variable(obj=1789)
|        1
|        sage: p.solve()
|        Traceback (most recent call last):
|        ...
|        MIPSolverException: GLPK: The LP (relaxation) problem has no dual feasible solution

We then copy from the generated file and paste into the source code of the COIN backend.

If this test seems valuable enough that all backends should be tested against it, we should create a test method instead of a docstring.

We can have the test method autogenerated by running a LoggingBackend in its test-method-writing mode:

sage: fname = tmp_filename()
sage: compute_something(solver= LoggingBackendFactory(solver='GLPK', printing=False,
....:                                                 test_method_file=fname,
....:                                                 test_method='something'))
4711
sage: with open(fname) as f:
....:     for line in f.readlines(): _ = sys.stdout.write('|{}'.format(line))
|
|    @classmethod
|    def _test_something(cls, tester=None, **options):
|        ...
|        Run tests on ...
|
|        TESTS::
|
|            sage: from sage.numerical.backends.generic_backend import GenericBackend
|            sage: p = GenericBackend()
|            sage: p._test_something()
|            Traceback (most recent call last):
|            ...
|            NotImplementedError
|
|        ...
|        p = cls()                         # fresh instance of the backend
|        if tester is None:
|            tester = p._tester(**options)
|        tester.assertEqual(p.add_variable(obj=42, name='Helloooooo'), 0)
|        tester.assertEqual(p.add_variable(obj=1789), 1)
|        with tester.assertRaises(MIPSolverException) as cm:
|            p.solve()

We then copy from the generated file and paste into the source code of the generic backend, where all test methods are defined.

If test_method_file is not provided, a default output file name will be computed from test_method.