Skip to content

Commit 8582c35

Browse files
Release Managervbraun
Release Manager
authored andcommitted
Trac #20413: InteractiveLPBackend: Use standard-form transformation, objective_constant_term, change default base_ring to QQ
Follow-up on #20296, #20311, #20301. Updating `InteractiveLPBackend` to: - use the standard-form transformation, - simplify its code slightly by making use of the new `objective_constant_term` handling in `InteractiveLPProblem` - make the example of optimizing over the dodecahedron more natural and remove use of backend methods. - change default `base_ring` to QQ -- a much saner default because it's so much faster URL: http://trac.sagemath.org/20413 Reported by: mkoeppe Ticket author(s): Matthias Koeppe Reviewer(s): Dima Pasechnik
2 parents 035a579 + c8fa4b0 commit 8582c35

File tree

4 files changed

+68
-39
lines changed

4 files changed

+68
-39
lines changed

src/sage/numerical/backends/generic_backend.pyx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,7 @@ def default_mip_solver(solver = None):
11691169
11701170
- ``InteractiveLPProblem`` (``solver="InteractiveLP"``). A didactical
11711171
implementation of the revised simplex method in Sage. It works over
1172-
any exact ordered field, the default is ``AA``.
1172+
any exact ordered field, the default is ``QQ``.
11731173
11741174
``solver`` should then be equal to one of ``"GLPK"``,
11751175
``"Coin"``, ``"CPLEX"``, ``"CVXOPT"``, ``"Gurobi"``, ``"PPL"`, or
@@ -1294,7 +1294,7 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba
12941294
12951295
- ``InteractiveLPProblem`` (``solver="InteractiveLP"``). A didactical
12961296
implementation of the revised simplex method in Sage. It works over
1297-
any exact ordered field, the default is ``AA``.
1297+
any exact ordered field, the default is ``QQ``.
12981298
12991299
``solver`` should then be equal to one of ``"GLPK"``, ``"Coin"``,
13001300
``"CPLEX"``, ``"CVXOPT"``,``"Gurobi"``, ``"PPL"``, ``"InteractiveLP"``,

src/sage/numerical/backends/interactivelp_backend.pxd

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ cdef class InteractiveLPBackend(GenericBackend):
1515
cdef object prob_name
1616

1717
cdef object lp_std_form
18+
cdef object std_form_transformation
1819
cdef object final_dictionary
1920
cdef int verbosity
2021

src/sage/numerical/backends/interactivelp_backend.pyx

+63-36
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,22 @@ cdef class InteractiveLPBackend:
5252
This backend can work with irrational algebraic numbers::
5353
5454
sage: poly = polytopes.dodecahedron(base_ring=AA)
55-
sage: lp = poly.to_linear_program(solver='InteractiveLP')
56-
sage: b = lp.get_backend()
57-
sage: for k in range(3): b.variable_lower_bound(k, 0)
58-
sage: b.set_objective([1, 1, 1])
55+
sage: lp, x = poly.to_linear_program(solver='InteractiveLP', return_variable=True)
56+
sage: lp.set_objective(x[0] + x[1] + x[2])
5957
sage: lp.solve()
6058
2.291796067500631?
61-
sage: [b.get_variable_value(k) for k in range(3)]
59+
sage: lp.get_values(x[0], x[1], x[2])
6260
[0.763932022500211?, 0.763932022500211?, 0.763932022500211?]
61+
sage: lp.set_objective(x[0] - x[1] - x[2])
62+
sage: lp.solve()
63+
2.291796067500631?
64+
sage: lp.get_values(x[0], x[1], x[2])
65+
[0.763932022500211?, -0.763932022500211?, -0.763932022500211?]
6366
"""
6467

6568
if base_ring is None:
66-
from sage.rings.all import AA
67-
base_ring = AA
69+
from sage.rings.all import QQ
70+
base_ring = QQ
6871

6972
self.lp = InteractiveLPProblem([], [], [], base_ring=base_ring)
7073
self.set_verbosity(0)
@@ -74,8 +77,6 @@ cdef class InteractiveLPBackend:
7477
else:
7578
self.set_sense(-1)
7679

77-
self.obj_constant_term = 0
78-
7980
self.row_names = []
8081

8182
cpdef base_ring(self):
@@ -91,7 +92,7 @@ cdef class InteractiveLPBackend:
9192
sage: from sage.numerical.backends.generic_backend import get_solver
9293
sage: p = get_solver(solver = "InteractiveLP")
9394
sage: p.base_ring()
94-
Algebraic Real Field
95+
Rational Field
9596
"""
9697
return self.lp.base_ring()
9798

@@ -196,7 +197,7 @@ cdef class InteractiveLPBackend:
196197
sage: p.objective_coefficient(1)
197198
1
198199
"""
199-
A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR()
200+
A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd()
200201
cdef int vtype = int(binary) + int(continuous) + int(integer)
201202
if vtype == 0:
202203
continuous = True
@@ -219,7 +220,7 @@ cdef class InteractiveLPBackend:
219220
x = tuple(x) + (name,)
220221
self.lp = InteractiveLPProblem(A, b, c, x,
221222
constraint_types, variable_types,
222-
problem_type, ring)
223+
problem_type, ring, objective_constant_term=d)
223224
return self.ncols() - 1
224225

225226
cpdef int add_variables(self, int n, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, names=None) except -1:
@@ -298,23 +299,24 @@ cdef class InteractiveLPBackend:
298299
else:
299300
raise NotImplementedError()
300301

301-
def _AbcxCVPR(self):
302+
def _AbcxCVPRd(self):
302303
"""
303304
Retrieve all problem data from the LP.
304305
305306
EXAMPLE::
306307
307308
sage: from sage.numerical.backends.generic_backend import get_solver
308309
sage: p = get_solver(solver = "InteractiveLP")
309-
sage: p._AbcxCVPR()
310-
([], (), (), (), (), (), 'max', Algebraic Real Field)
310+
sage: p._AbcxCVPRd()
311+
([], (), (), (), (), (), 'max', Rational Field, 0)
311312
"""
312313
A, b, c, x = self.lp.Abcx()
313314
constraint_types = self.lp.constraint_types()
314315
variable_types = self.lp.variable_types()
315316
problem_type = self.lp.problem_type()
316317
base_ring = self.lp.base_ring()
317-
return A, b, c, x, constraint_types, variable_types, problem_type, base_ring
318+
d = self.lp.objective_constant_term()
319+
return A, b, c, x, constraint_types, variable_types, problem_type, base_ring, d
318320

319321
cpdef set_sense(self, int sense):
320322
"""
@@ -337,14 +339,14 @@ cdef class InteractiveLPBackend:
337339
sage: p.is_maximization()
338340
False
339341
"""
340-
A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR()
342+
A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd()
341343
if sense == +1:
342344
problem_type = "max"
343345
else:
344346
problem_type = "min"
345347
self.lp = InteractiveLPProblem(A, b, c, x,
346348
constraint_types, variable_types,
347-
problem_type, ring)
349+
problem_type, ring, objective_constant_term=d)
348350

349351
cpdef objective_coefficient(self, int variable, coeff=None):
350352
"""
@@ -372,12 +374,38 @@ cdef class InteractiveLPBackend:
372374
if coeff is None:
373375
return self.lp.objective_coefficients()[variable]
374376
else:
375-
A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR()
377+
A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd()
376378
c = list(c)
377379
c[variable] = coeff
378380
self.lp = InteractiveLPProblem(A, b, c, x,
379381
constraint_types, variable_types,
380-
problem_type, ring)
382+
problem_type, ring, objective_constant_term=d)
383+
384+
cpdef objective_constant_term(self, d=None):
385+
"""
386+
Set or get the constant term in the objective function
387+
388+
INPUT:
389+
390+
- ``d`` (double) -- its coefficient. If `None` (default), return the current value.
391+
392+
EXAMPLE::
393+
394+
sage: from sage.numerical.backends.generic_backend import get_solver
395+
sage: p = get_solver(solver = "InteractiveLP")
396+
sage: p.objective_constant_term()
397+
0
398+
sage: p.objective_constant_term(42)
399+
sage: p.objective_constant_term()
400+
42
401+
"""
402+
if d is None:
403+
return self.lp.objective_constant_term()
404+
else:
405+
A, b, c, x, constraint_types, variable_types, problem_type, ring, _ = self._AbcxCVPRd()
406+
self.lp = InteractiveLPProblem(A, b, c, x,
407+
constraint_types, variable_types,
408+
problem_type, ring, objective_constant_term=d)
381409

382410
cpdef set_objective(self, list coeff, d = 0):
383411
"""
@@ -423,12 +451,11 @@ cdef class InteractiveLPBackend:
423451
-47/5
424452
425453
"""
426-
A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR()
454+
A, b, _, x, constraint_types, variable_types, problem_type, ring, _ = self._AbcxCVPRd()
427455
c = coeff
428456
self.lp = InteractiveLPProblem(A, b, c, x,
429457
constraint_types, variable_types,
430-
problem_type, ring)
431-
self.obj_constant_term = d
458+
problem_type, ring, objective_constant_term=d)
432459

433460
cpdef set_verbosity(self, int level):
434461
"""
@@ -470,13 +497,13 @@ cdef class InteractiveLPBackend:
470497
sage: p.get_values([x,y])
471498
[0, 3]
472499
"""
473-
A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR()
500+
A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd()
474501
A = A.delete_rows((i,))
475502
b = list(b); del b[i]
476503
constraint_types=list(constraint_types); del constraint_types[i]
477504
self.lp = InteractiveLPProblem(A, b, c, x,
478505
constraint_types, variable_types,
479-
problem_type, ring)
506+
problem_type, ring, objective_constant_term=d)
480507

481508
cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None):
482509
"""
@@ -511,7 +538,7 @@ cdef class InteractiveLPBackend:
511538
sage: p.row_name(1)
512539
'foo'
513540
"""
514-
A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR()
541+
A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd()
515542
if lower_bound is None:
516543
if upper_bound is None:
517544
raise ValueError("At least one of lower_bound and upper_bound must be provided")
@@ -537,7 +564,7 @@ cdef class InteractiveLPBackend:
537564

538565
self.lp = InteractiveLPProblem(A, b, c, x,
539566
constraint_types, variable_types,
540-
problem_type, ring)
567+
problem_type, ring, objective_constant_term=d)
541568

542569

543570
cpdef add_col(self, list indices, list coeffs):
@@ -633,7 +660,8 @@ cdef class InteractiveLPBackend:
633660
"""
634661
## FIXME: standard_form should allow to pass slack names (which we would take from row_names).
635662
## FIXME: Perhaps also pass the problem name as objective name
636-
lp_std_form = self.lp_std_form = self.lp.standard_form()
663+
lp_std_form, transformation = self.lp.standard_form(transformation=True)
664+
self.lp_std_form, self.std_form_transformation = lp_std_form, transformation
637665
output = lp_std_form.run_revised_simplex_method()
638666
## FIXME: Display output as a side effect if verbosity is high enough
639667
d = self.final_dictionary = lp_std_form.final_revised_dictionary()
@@ -674,7 +702,7 @@ cdef class InteractiveLPBackend:
674702
v = d.objective_value()
675703
if self.lp_std_form.is_negative():
676704
v = - v
677-
return self.obj_constant_term + v
705+
return v
678706

679707
cpdef get_variable_value(self, int variable):
680708
"""
@@ -701,9 +729,8 @@ cdef class InteractiveLPBackend:
701729
sage: p.get_variable_value(1)
702730
3/2
703731
"""
704-
if str(self.lp.decision_variables()[variable]) != str(self.lp_std_form.decision_variables()[variable]):
705-
raise NotImplementedError("Undoing the standard-form transformation is not implemented")
706-
return self.final_dictionary.basic_solution()[variable]
732+
solution = self.std_form_transformation(self.final_dictionary.basic_solution())
733+
return solution[variable]
707734

708735
cpdef int ncols(self):
709736
"""
@@ -1027,12 +1054,12 @@ cdef class InteractiveLPBackend:
10271054
else:
10281055
if value != bounds[1]:
10291056
bounds = (bounds[0], value)
1030-
A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR()
1057+
A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd()
10311058
variable_types = list(variable_types)
10321059
variable_types[index] = self._variable_type_from_bounds(*bounds)
10331060
self.lp = InteractiveLPProblem(A, b, c, x,
10341061
constraint_types, variable_types,
1035-
problem_type, ring)
1062+
problem_type, ring, objective_constant_term=d)
10361063

10371064
cpdef variable_lower_bound(self, int index, value = False):
10381065
"""
@@ -1071,12 +1098,12 @@ cdef class InteractiveLPBackend:
10711098
else:
10721099
if value != bounds[0]:
10731100
bounds = (value, bounds[1])
1074-
A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR()
1101+
A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd()
10751102
variable_types = list(variable_types)
10761103
variable_types[index] = self._variable_type_from_bounds(*bounds)
10771104
self.lp = InteractiveLPProblem(A, b, c, x,
10781105
constraint_types, variable_types,
1079-
problem_type, ring)
1106+
problem_type, ring, objective_constant_term=d)
10801107

10811108
cpdef bint is_variable_basic(self, int index):
10821109
"""

src/sage/numerical/mip.pyx

+2-1
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,8 @@ cdef class MixedIntegerLinearProgram(SageObject):
598598
sage: p = MixedIntegerLinearProgram(solver='ppl')
599599
sage: p.base_ring()
600600
Rational Field
601-
sage: p = MixedIntegerLinearProgram(solver='InteractiveLP')
601+
sage: from sage.rings.all import AA
602+
sage: p = MixedIntegerLinearProgram(solver='InteractiveLP', base_ring=AA)
602603
sage: p.base_ring()
603604
Algebraic Real Field
604605
sage: d = polytopes.dodecahedron()

0 commit comments

Comments
 (0)