Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/numerical/linear_functions.pyx
8815 views
1
"""
2
Linear Functions and Constraints
3
4
This module implements linear functions (see :class:`LinearFunction`)
5
in formal variables and chained (in)equalities between them (see
6
:class:`LinearConstraint`). By convention, these are always written as
7
either equalities or less-or-equal. For example::
8
9
sage: p = MixedIntegerLinearProgram()
10
sage: x = p.new_variable()
11
sage: f = 1 + x[1] + 2*x[2]; f # a linear function
12
1 + x_0 + 2*x_1
13
sage: type(f)
14
<type 'sage.numerical.linear_functions.LinearFunction'>
15
16
sage: c = (0 <= f); c # a constraint
17
0 <= 1 + x_0 + 2*x_1
18
sage: type(c)
19
<type 'sage.numerical.linear_functions.LinearConstraint'>
20
21
Note that you can use this module without any reference to linear
22
programming, it only implements linear functions over a base ring and
23
constraints. However, for ease of demonstration we will always
24
construct them out of linear programs (see
25
:mod:`~sage.numerical.mip`).
26
27
Constraints can be equations or (non-strict) inequalities. They can be
28
chained::
29
30
sage: p = MixedIntegerLinearProgram()
31
sage: x = p.new_variable()
32
sage: x[0] == x[1] == x[2] == x[3]
33
x_0 == x_1 == x_2 == x_3
34
35
sage: ieq_01234 = x[0] <= x[1] <= x[2] <= x[3] <= x[4]
36
sage: ieq_01234
37
x_0 <= x_1 <= x_2 <= x_3 <= x_4
38
39
If necessary, the direction of inequality is flipped to always write
40
inqualities as less or equal::
41
42
sage: x[5] >= ieq_01234
43
x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5
44
45
sage: (x[5]<=x[6]) >= ieq_01234
46
x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5 <= x_6
47
sage: (x[5]<=x[6]) <= ieq_01234
48
x_5 <= x_6 <= x_0 <= x_1 <= x_2 <= x_3 <= x_4
49
50
TESTS:
51
52
See :trac:`12091` ::
53
54
sage: p = MixedIntegerLinearProgram()
55
sage: b = p.new_variable()
56
sage: b[0] <= b[1] <= 2
57
x_0 <= x_1 <= 2
58
sage: list(b[0] <= b[1] <= 2)
59
[x_0, x_1, 2]
60
sage: 1 >= b[1] >= 2*b[0]
61
2*x_0 <= x_1 <= 1
62
sage: b[2] >= b[1] >= 2*b[0]
63
2*x_0 <= x_1 <= x_2
64
"""
65
66
67
#*****************************************************************************
68
# Copyright (C) 2012 Nathann Cohen <[email protected]>
69
# Copyright (C) 2012 Volker Braun <[email protected]>
70
#
71
# Distributed under the terms of the GNU General Public License (GPL)
72
# as published by the Free Software Foundation; either version 2 of
73
# the License, or (at your option) any later version.
74
# http://www.gnu.org/licenses/
75
#*****************************************************************************
76
77
include "sage/ext/stdsage.pxi"
78
include "sage/ext/interrupt.pxi"
79
include "sage/ext/cdefs.pxi"
80
from cpython.object cimport *
81
82
cdef extern from "limits.h":
83
long LONG_MAX
84
85
86
from sage.structure.parent cimport Parent
87
from sage.structure.element cimport ModuleElement, Element
88
from sage.misc.cachefunc import cached_function
89
90
91
92
#*****************************************************************************
93
#
94
# Utility functions to test that something is a linear function / constraint
95
#
96
#*****************************************************************************
97
98
def is_LinearFunction(x):
99
"""
100
Test whether ``x`` is a linear function
101
102
INPUT:
103
104
- ``x`` -- anything.
105
106
OUTPUT:
107
108
Boolean.
109
110
EXAMPLES::
111
112
sage: p = MixedIntegerLinearProgram()
113
sage: x = p.new_variable()
114
sage: from sage.numerical.linear_functions import is_LinearFunction
115
sage: is_LinearFunction(x[0] - 2*x[2])
116
True
117
sage: is_LinearFunction('a string')
118
False
119
"""
120
return isinstance(x, LinearFunction)
121
122
def is_LinearConstraint(x):
123
"""
124
Test whether ``x`` is a linear constraint
125
126
INPUT:
127
128
- ``x`` -- anything.
129
130
OUTPUT:
131
132
Boolean.
133
134
EXAMPLES::
135
136
sage: p = MixedIntegerLinearProgram()
137
sage: x = p.new_variable()
138
sage: ieq = (x[0] <= x[1])
139
sage: from sage.numerical.linear_functions import is_LinearConstraint
140
sage: is_LinearConstraint(ieq)
141
True
142
sage: is_LinearConstraint('a string')
143
False
144
"""
145
return isinstance(x, LinearConstraint)
146
147
148
#*****************************************************************************
149
#
150
# Factory functions for the parents to ensure uniqueness
151
#
152
#*****************************************************************************
153
154
@cached_function
155
def LinearFunctionsParent(base_ring):
156
"""
157
Return the parent for linear functions over ``base_ring``.
158
159
The output is cached, so only a single parent is ever constructed
160
for a given base ring.
161
162
INPUT:
163
164
- ``base_ring`` -- a ring. The coefficient ring for the linear
165
funcitons.
166
167
OUTPUT:
168
169
The parent of the linear functions over ``base_ring``.
170
171
EXAMPLES::
172
173
sage: from sage.numerical.linear_functions import LinearFunctionsParent
174
sage: LinearFunctionsParent(QQ)
175
Linear functions over Rational Field
176
"""
177
return LinearFunctionsParent_class(base_ring)
178
179
@cached_function
180
def LinearConstraintsParent(linear_functions_parent):
181
"""
182
Return the parent for linear functions over ``base_ring``.
183
184
The output is cached, so only a single parent is ever constructed
185
for a given base ring.
186
187
INPUT:
188
189
- ``linear_functions_parent`` -- a
190
:class:`LinearFunctionsParent_class`. The type of linear
191
functions that the constraints are made out of.
192
193
OUTPUT:
194
195
The parent of the linear constraints with the given linear functions.
196
197
EXAMPLES::
198
199
sage: from sage.numerical.linear_functions import \
200
... LinearFunctionsParent, LinearConstraintsParent
201
sage: LF = LinearFunctionsParent(QQ)
202
sage: LinearConstraintsParent(LF)
203
Linear constraints over Rational Field
204
"""
205
return LinearConstraintsParent_class(linear_functions_parent)
206
207
208
209
#*****************************************************************************
210
#
211
# Parent of linear functions
212
#
213
#*****************************************************************************
214
215
cdef class LinearFunctionsParent_class(Parent):
216
r"""
217
The parent for all linear functions over a fixed base ring.
218
219
.. warning::
220
221
You should use :func:`LinearFunctionsParent` to construct
222
instances of this class.
223
224
INPUT/OUTPUT:
225
226
See :func:`LinearFunctionsParent`
227
228
EXAMPLES::
229
230
sage: from sage.numerical.linear_functions import LinearFunctionsParent_class
231
sage: LinearFunctionsParent_class
232
<type 'sage.numerical.linear_functions.LinearFunctionsParent_class'>
233
"""
234
235
def __init__(self, base_ring):
236
"""
237
The Python constructor
238
239
TESTS::
240
241
sage: from sage.numerical.linear_functions import LinearFunctionsParent
242
sage: LinearFunctionsParent(RDF)
243
Linear functions over Real Double Field
244
"""
245
from sage.categories.modules_with_basis import ModulesWithBasis
246
Parent.__init__(self, base=base_ring, category=ModulesWithBasis(base_ring))
247
self._multiplication_symbol = '*'
248
249
def set_multiplication_symbol(self, symbol='*'):
250
"""
251
Set the multiplication symbol when pretty-printing linear functions.
252
253
INPUT:
254
255
- ``symbol`` -- string, default: ``'*'``. The multiplication
256
symbol to be used.
257
258
EXAMPLES::
259
260
sage: p = MixedIntegerLinearProgram()
261
sage: x = p.new_variable()
262
sage: f = -1-2*x[0]-3*x[1]
263
sage: LF = f.parent()
264
sage: LF._get_multiplication_symbol()
265
'*'
266
sage: f
267
-1 - 2*x_0 - 3*x_1
268
sage: LF.set_multiplication_symbol(' ')
269
sage: f
270
-1 - 2 x_0 - 3 x_1
271
sage: LF.set_multiplication_symbol()
272
sage: f
273
-1 - 2*x_0 - 3*x_1
274
"""
275
self._multiplication_symbol = symbol
276
277
cpdef _get_multiplication_symbol(self):
278
"""
279
Return the multiplication symbol.
280
281
OUTPUT:
282
283
String. By default, this is ``'*'``.
284
285
EXAMPLES::
286
287
sage: LF = MixedIntegerLinearProgram().linear_functions_parent()
288
sage: LF._get_multiplication_symbol()
289
'*'
290
"""
291
return self._multiplication_symbol
292
293
def _repr_(self):
294
"""
295
Return as string representation
296
297
EXAMPLES::
298
299
sage: MixedIntegerLinearProgram().linear_functions_parent()
300
Linear functions over Real Double Field
301
"""
302
return 'Linear functions over '+str(self.base_ring())
303
304
cpdef _element_constructor_(self, x):
305
"""
306
Construt a :class:`LinearFunction` from ``x``.
307
308
EXAMPLES::
309
310
sage: p = MixedIntegerLinearProgram()
311
sage: LF = p.linear_functions_parent()
312
sage: LF._element_constructor_(123)
313
123
314
sage: p(123) # indirect doctest
315
123
316
sage: type(_)
317
<type 'sage.numerical.linear_functions.LinearFunction'>
318
319
sage: p_QQ = MixedIntegerLinearProgram(solver='ppl')
320
sage: LF_QQ = p_QQ.linear_functions_parent()
321
sage: f = LF({-1:1/2, 2:3/4}); f
322
0.5 + 0.75*x_2
323
sage: LF(f) is f
324
True
325
sage: LF_QQ(f)
326
1/2 + 3/4*x_2
327
sage: LF_QQ(f) is f
328
False
329
"""
330
if is_LinearFunction(x):
331
if x.parent() is self:
332
return x
333
else:
334
return LinearFunction(self, (<LinearFunction>x)._f)
335
return LinearFunction(self, x)
336
337
cpdef _coerce_map_from_(self, R):
338
"""
339
Allow coercion of scalars into linear functions.
340
341
INPUT:
342
343
- ``R`` -- a ring.
344
345
OUTPUT:
346
347
Boolean. Whether there is a coercion map.
348
349
EXAMPLES::
350
351
sage: p = MixedIntegerLinearProgram()
352
sage: parent = p.linear_functions_parent()
353
sage: parent.coerce(int(2))
354
2
355
sage: parent._coerce_map_from_(int)
356
True
357
"""
358
if R in [int, float, long, complex]:
359
return True
360
from sage.rings.real_mpfr import mpfr_prec_min
361
from sage.rings.all import RealField
362
if RealField(mpfr_prec_min()).has_coerce_map_from(R):
363
return True
364
return False
365
366
def _an_element_(self):
367
"""
368
Returns an element
369
370
OUTPUT:
371
372
A linear function.
373
374
EXAMPLES::
375
376
sage: p = MixedIntegerLinearProgram().linear_functions_parent()
377
sage: p._an_element_()
378
5*x_2 + 7*x_5
379
sage: p.an_element() # indirect doctest
380
5*x_2 + 7*x_5
381
"""
382
return self._element_constructor_({2:5, 5:7})
383
384
385
#*****************************************************************************
386
#
387
# Elements of linear functions
388
#
389
#*****************************************************************************
390
391
cdef class LinearFunction(ModuleElement):
392
r"""
393
An elementary algebra to represent symbolic linear functions.
394
395
.. warning::
396
397
You should never instantiate :class:`LinearFunction`
398
manually. Use the element constructor in the parent
399
instead. For convenience, you can also call the
400
:class:`MixedIntegerLinearProgram` instance directly.
401
402
EXAMPLES:
403
404
For example, do this::
405
406
sage: p = MixedIntegerLinearProgram()
407
sage: p({0 : 1, 3 : -8})
408
x_0 - 8*x_3
409
410
or this::
411
412
sage: parent = p.linear_functions_parent()
413
sage: parent({0 : 1, 3 : -8})
414
x_0 - 8*x_3
415
416
instead of this::
417
418
sage: from sage.numerical.linear_functions import LinearFunction
419
sage: LinearFunction(p.linear_functions_parent(), {0 : 1, 3 : -8})
420
x_0 - 8*x_3
421
"""
422
423
def __init__(self, parent, f):
424
r"""
425
Constructor taking a dictionary or a numerical value as its argument.
426
427
A linear function is represented as a dictionary. The
428
values are the coefficient of the variable represented
429
by the keys ( which are integers ). The key ``-1``
430
corresponds to the constant term.
431
432
EXAMPLES:
433
434
With a dictionary::
435
436
sage: p = MixedIntegerLinearProgram()
437
sage: p({0 : 1, 3 : -8})
438
x_0 - 8*x_3
439
440
Using the constructor with a numerical value::
441
442
sage: p = MixedIntegerLinearProgram()
443
sage: p(25)
444
25
445
"""
446
ModuleElement.__init__(self, parent)
447
R = self.base_ring()
448
if isinstance(f, dict):
449
self._f = dict( (int(key), R(value)) for key, value in f.iteritems() )
450
else:
451
self._f = {-1: R(f)}
452
453
cpdef iteritems(self):
454
"""
455
Iterate over the index, coefficient pairs
456
457
OUTPUT:
458
459
An iterator over the ``(key, coefficient)`` pairs. The keys
460
are integers indexing the variables. The key ``-1``
461
corresponds to the constant term.
462
463
EXAMPLES::
464
465
sage: p = MixedIntegerLinearProgram(solver = 'ppl')
466
sage: x = p.new_variable()
467
sage: f = 0.5 + 3/2*x[1] + 0.6*x[3]
468
sage: for id, coeff in f.iteritems():
469
... print 'id =', id, ' coeff =', coeff
470
id = 0 coeff = 3/2
471
id = 1 coeff = 3/5
472
id = -1 coeff = 1/2
473
"""
474
return self._f.iteritems()
475
476
def dict(self):
477
r"""
478
Returns the dictionary corresponding to the Linear Function.
479
480
OUTPUT:
481
482
The linear function is represented as a dictionary. The value
483
are the coefficient of the variable represented by the keys (
484
which are integers ). The key ``-1`` corresponds to the
485
constant term.
486
487
EXAMPLE::
488
489
sage: p = MixedIntegerLinearProgram()
490
sage: lf = p({0 : 1, 3 : -8})
491
sage: lf.dict()
492
{0: 1.0, 3: -8.0}
493
"""
494
return dict(self._f)
495
496
cpdef ModuleElement _add_(self, ModuleElement b):
497
r"""
498
Defining the + operator
499
500
EXAMPLE::
501
502
sage: p = MixedIntegerLinearProgram()
503
sage: p({0 : 1, 3 : -8}) + p({2 : 5, 3 : 2}) - 16
504
-16 + x_0 + 5*x_2 - 6*x_3
505
"""
506
e = dict(self._f)
507
for (id,coeff) in b.dict().iteritems():
508
e[id] = self._f.get(id,0) + coeff
509
P = self.parent()
510
return P(e)
511
512
cpdef ModuleElement _neg_(self):
513
r"""
514
Defining the - operator (opposite).
515
516
EXAMPLE::
517
518
sage: p = MixedIntegerLinearProgram()
519
sage: - p({0 : 1, 3 : -8})
520
-1*x_0 + 8*x_3
521
"""
522
P = self.parent()
523
return P(dict([(id,-coeff) for (id, coeff) in self._f.iteritems()]))
524
525
cpdef ModuleElement _sub_(self, ModuleElement b):
526
r"""
527
Defining the - operator (substraction).
528
529
EXAMPLE::
530
531
sage: p = MixedIntegerLinearProgram()
532
sage: p({2 : 5, 3 : 2}) - 3
533
-3 + 5*x_2 + 2*x_3
534
sage: p({0 : 1, 3 : -8}) - p({2 : 5, 3 : 2}) - 16
535
-16 + x_0 - 5*x_2 - 10*x_3
536
"""
537
e = dict(self._f)
538
for (id,coeff) in b.dict().iteritems():
539
e[id] = self._f.get(id,0) - coeff
540
P = self.parent()
541
return P(e)
542
543
cpdef ModuleElement _rmul_(self, RingElement b):
544
r"""
545
Left multiplication by scalars
546
547
EXAMPLE::
548
549
sage: p = MixedIntegerLinearProgram()
550
sage: p({2 : 5, 3 : 2}) * 3
551
15*x_2 + 6*x_3
552
"""
553
P = self.parent()
554
return P(dict([(id,b*coeff) for (id, coeff) in self._f.iteritems()]))
555
556
cpdef ModuleElement _lmul_(self, RingElement b):
557
r"""
558
Right multiplication by scalars
559
560
EXAMPLE::
561
562
sage: p = MixedIntegerLinearProgram()
563
sage: 3 * p({2 : 5, 3 : 2})
564
15*x_2 + 6*x_3
565
"""
566
return self._rmul_(b)
567
568
cpdef _acted_upon_(self, x, bint self_on_left):
569
"""
570
Act with scalars that do not have a natural coercion into
571
``self.base_ring()``
572
573
EXAMPLES:
574
575
Note that there is no coercion from ``RR`` to ``QQ``, but you
576
can explicitly convert them::
577
578
sage: 1/2 * 0.6
579
0.300000000000000
580
581
If there were a coercion, then 0.6 would have been coerced into
582
6/10 and the result would have been rational. This is
583
undesirable, which is why there cannot be a coercion on the
584
level of the coefficient rings.
585
586
By declaring an action of ``RR`` on the linear functions over
587
``QQ`` we make the following possible::
588
589
sage: p = MixedIntegerLinearProgram(solver='ppl')
590
sage: p.base_ring()
591
Rational Field
592
sage: x = p.new_variable()
593
sage: x[0] * 0.6
594
3/5*x_0
595
"""
596
R = self.base_ring()
597
try:
598
x_R = R(x)
599
except TypeError:
600
return None
601
return self._rmul_(x_R)
602
603
def _coeff_formatter(self, coeff, constant_term=False):
604
"""
605
Pretty-print multiplicative coefficient ``x``
606
607
OUTPUT:
608
609
String, including a trailing space if the coefficient is not
610
one. Empty string otherwise.
611
612
EXAMPLES::
613
614
sage: p = MixedIntegerLinearProgram()
615
sage: f = p(1); type(f)
616
<type 'sage.numerical.linear_functions.LinearFunction'>
617
sage: f._coeff_formatter(1)
618
''
619
sage: f._coeff_formatter(1, constant_term=True)
620
'1'
621
sage: f._coeff_formatter(RDF(12.0))
622
'12*'
623
sage: f._coeff_formatter(RDF(12.3))
624
'12.3*'
625
626
sage: q = MixedIntegerLinearProgram(solver='ppl')
627
sage: f = p(1)
628
sage: f._coeff_formatter(13/45)
629
'13/45*'
630
"""
631
R = self.base_ring()
632
if coeff == R.one() and not constant_term:
633
return ''
634
try:
635
from sage.rings.all import ZZ
636
coeff = ZZ(coeff) # print as integer if possible
637
except TypeError:
638
pass
639
if constant_term:
640
return str(coeff)
641
else:
642
return str(coeff) + self.parent()._get_multiplication_symbol()
643
644
def _repr_(self):
645
r"""
646
Returns a string version of the linear function.
647
648
EXAMPLE::
649
650
sage: p = MixedIntegerLinearProgram(solver='GLPK')
651
sage: p({-1: -15, 2 : -5.1, 3 : 2/3})
652
-15 - 5.1*x_2 + 0.666666666667*x_3
653
sage: p = MixedIntegerLinearProgram(solver='ppl')
654
sage: p({-1: -15, 2 : -5.1, 3 : 2/3})
655
-15 - 51/10*x_2 + 2/3*x_3
656
"""
657
cdef dict d = dict(self._f)
658
cdef bint first = True
659
t = ""
660
661
if d.has_key(-1):
662
coeff = d.pop(-1)
663
if coeff!=0:
664
t = self._coeff_formatter(coeff, constant_term=True)
665
first = False
666
667
cdef list l = sorted(d.items())
668
for id,coeff in l:
669
sign = cmp(coeff,0)
670
if sign == 0:
671
continue
672
if not first:
673
if sign == -1:
674
t += ' - '
675
if sign == +1:
676
t += ' + '
677
t += self._coeff_formatter(abs(coeff)) + 'x_' + str(id)
678
else:
679
t += self._coeff_formatter(coeff) + 'x_' + str(id)
680
first = False
681
682
if first:
683
return '0'
684
else:
685
return t
686
687
cpdef is_zero(self):
688
"""
689
Test whether ``self`` is zero.
690
691
OUTPUT:
692
693
Boolean.
694
695
EXAMPLES::
696
697
sage: p = MixedIntegerLinearProgram()
698
sage: x = p.new_variable()
699
sage: (x[1] - x[1] + 0*x[2]).is_zero()
700
True
701
"""
702
for coeff in self._f.values():
703
if not coeff.is_zero():
704
return False
705
return True
706
707
cpdef equals(LinearFunction left, LinearFunction right):
708
"""
709
Logically compare ``left`` and ``right``.
710
711
OUTPUT:
712
713
Boolean.
714
715
EXAMPLES::
716
717
sage: p = MixedIntegerLinearProgram()
718
sage: x = p.new_variable()
719
sage: (x[1] + 1).equals(3/3 + 1*x[1] + 0*x[2])
720
True
721
"""
722
return (left-right).is_zero()
723
724
def __richcmp__(left, right, int op):
725
"""
726
Override the rich comparison.
727
728
The Sage framework sometimes expects that rich comparison
729
results in a boolean value, but we want to return
730
:class:`~sage.numerical.linear_functions.LinearConstraint`
731
objects.
732
733
EXAMPLES::
734
735
sage: p = MixedIntegerLinearProgram()
736
sage: x = p.new_variable()
737
sage: x[0].__le__(x[1]) # indirect doctest
738
x_0 <= x_1
739
"""
740
return (<LinearFunction>left)._richcmp(right, op)
741
742
cdef _richcmp(left, right, int op):
743
"""
744
Create a inequality or equality object.
745
746
EXAMPLE::
747
748
sage: p = MixedIntegerLinearProgram()
749
sage: from sage.numerical.linear_functions import LinearFunction
750
sage: p({2 : 5, 3 : 2}) <= p({2 : 3, 9 : 2})
751
5*x_2 + 2*x_3 <= 3*x_2 + 2*x_9
752
753
sage: p({2 : 5, 3 : 2}) >= p({2 : 3, 9 : 2})
754
3*x_2 + 2*x_9 <= 5*x_2 + 2*x_3
755
756
sage: p({2 : 5, 3 : 2}) == p({2 : 3, 9 : 2})
757
5*x_2 + 2*x_3 == 3*x_2 + 2*x_9
758
759
sage: p({2 : 5, 3 : 2}) < p({2 : 3, 9 : 2})
760
Traceback (most recent call last):
761
...
762
ValueError: strict < is not allowed, use <= instead.
763
764
sage: p({2 : 5, 3 : 2}) > p({2 : 3, 9 : 2})
765
Traceback (most recent call last):
766
...
767
ValueError: strict > is not allowed, use >= instead.
768
769
TESTS::
770
771
sage: cm = sage.structure.element.get_coercion_model()
772
sage: cm.explain(10, p(1), operator.le)
773
Coercion on left operand via
774
Conversion map:
775
From: Integer Ring
776
To: Linear functions over Real Double Field
777
Arithmetic performed after coercions.
778
Result lives in Linear functions over Real Double Field
779
Linear functions over Real Double Field
780
781
sage: x = p.new_variable()
782
sage: operator.le(10, x[0])
783
10 <= x_0
784
sage: x[0] <= 1
785
x_0 <= 1
786
sage: x[0] >= 1
787
1 <= x_0
788
sage: 1 <= x[0]
789
1 <= x_0
790
sage: 1 >= x[0]
791
x_0 <= 1
792
"""
793
LF = left.parent()
794
LC = LinearConstraintsParent(LF)
795
equality = (op == Py_EQ)
796
cdef LinearConstraint left_constraint = LC(left, equality=equality)
797
cdef LinearConstraint right_constraint = LC(right, equality=equality)
798
if op == Py_LT:
799
raise ValueError("strict < is not allowed, use <= instead.")
800
elif op == Py_EQ:
801
return left_constraint._richcmp(right_constraint, op)
802
elif op == Py_GT:
803
raise ValueError("strict > is not allowed, use >= instead.")
804
elif op == Py_LE:
805
return left_constraint._richcmp(right_constraint, op)
806
elif op == Py_NE:
807
raise ValueError("inequality != is not allowed, use one of <=, ==, >=.")
808
elif op == Py_GE:
809
return left_constraint._richcmp(right_constraint, op)
810
else:
811
assert(False) # unreachable
812
813
def __hash__(self):
814
r"""
815
Return a hash.
816
817
EXAMPLES::
818
819
sage: p = MixedIntegerLinearProgram()
820
sage: f = p({2 : 5, 3 : 2})
821
sage: f.__hash__() # random output
822
103987752
823
sage: d = {}
824
sage: d[f] = 3
825
"""
826
# see _cmp_c_impl() if you want to change the hash function
827
return id(self) % LONG_MAX
828
829
def __cmp__(left, right):
830
"""
831
Part of the comparison framework.
832
833
EXAMPLES::
834
835
sage: p = MixedIntegerLinearProgram()
836
sage: f = p({2 : 5, 3 : 2})
837
sage: cmp(f, f)
838
0
839
"""
840
return (<Element>left)._cmp(right)
841
842
cdef int _cmp_c_impl(left, Element right) except -2:
843
"""
844
Implement comparison of two linear functions.
845
846
EXAMPLES::
847
848
sage: p = MixedIntegerLinearProgram()
849
sage: f = p({2 : 5, 3 : 2})
850
sage: cmp(f, f)
851
0
852
sage: abs(cmp(f, f+0)) # since we are comparing by id()
853
1
854
sage: abs(cmp(f, f+1))
855
1
856
sage: len(set([f, f]))
857
1
858
sage: len(set([f, f+0]))
859
2
860
sage: len(set([f, f+1]))
861
2
862
"""
863
# Note: if you want to implement smarter comparison, you also
864
# need to change __hash__(). The comparison function must
865
# satisfy cmp(x,y)==0 => hash(x)==hash(y)
866
return cmp(id(left), id(right))
867
868
#*****************************************************************************
869
#
870
# Parent of linear constraints
871
#
872
#*****************************************************************************
873
874
cdef class LinearConstraintsParent_class(Parent):
875
"""
876
Parent for :class:`LinearConstraint`
877
878
.. warning::
879
880
This class has no reason to be instanciated by the user, and
881
is meant to be used by instances of
882
:class:`MixedIntegerLinearProgram`. Also, use the
883
:func:`LinearConstraintsParent` factory function.
884
885
INPUT/OUTPUT:
886
887
See :func:`LinearFunctionsParent`
888
889
EXAMPLES::
890
891
sage: p = MixedIntegerLinearProgram()
892
sage: LC = p.linear_constraints_parent(); LC
893
Linear constraints over Real Double Field
894
sage: from sage.numerical.linear_functions import LinearConstraintsParent
895
sage: LinearConstraintsParent(p.linear_functions_parent()) is LC
896
True
897
"""
898
899
def __init__(self, linear_functions_parent):
900
"""
901
The Python constructor
902
903
INPUT/OUTPUT:
904
905
See :func:`LinearFunctionsParent`
906
907
TESTS::
908
909
sage: from sage.numerical.linear_functions import LinearFunctionsParent
910
sage: LF = LinearFunctionsParent(RDF)
911
sage: from sage.numerical.linear_functions import LinearConstraintsParent
912
sage: LinearConstraintsParent(LF)
913
Linear constraints over Real Double Field
914
"""
915
Parent.__init__(self)
916
self._LF = linear_functions_parent
917
918
def linear_functions_parent(self):
919
"""
920
Return the parent for the linear functions
921
922
EXAMPLES::
923
924
sage: LC = MixedIntegerLinearProgram().linear_constraints_parent()
925
sage: LC.linear_functions_parent()
926
Linear functions over Real Double Field
927
"""
928
return self._LF
929
930
def _repr_(self):
931
"""
932
Return a string representation
933
934
OUTPUT:
935
936
String.
937
938
EXAMPLES::
939
940
sage: MixedIntegerLinearProgram().linear_constraints_parent()
941
Linear constraints over Real Double Field
942
"""
943
return 'Linear constraints over '+str(self.linear_functions_parent().base_ring())
944
945
cpdef _element_constructor_(self, left, right=None, equality=False):
946
"""
947
Construt a :class:`LinearConstraint`.
948
949
INPUT:
950
951
- ``left`` -- a :class:`LinearFunction`, or something that can
952
be converted into one, a list/tuple of
953
:class:`LinearFunction`, or an existing
954
:class:`LinearConstraint`.
955
956
- ``right`` -- a :class:`LinearFunction` or ``None``
957
(default).
958
959
- ``equality`` -- boolean (default: ``True``). Whether to
960
construct an equation or an inequality.
961
962
OUTPUT:
963
964
The :class:`LinearConstraint` constructed from the input data.
965
966
EXAMPLES::
967
968
sage: p = MixedIntegerLinearProgram()
969
sage: LC = p.linear_constraints_parent()
970
sage: LC._element_constructor_(1, 2)
971
1 <= 2
972
973
sage: x = p.new_variable()
974
sage: LC([x[0], x[1], x[2]])
975
x_0 <= x_1 <= x_2
976
977
sage: LC([x[0], x[1], x[2]], equality=True)
978
x_0 == x_1 == x_2
979
980
sage: type(_)
981
<type 'sage.numerical.linear_functions.LinearConstraint'>
982
983
TESTS::
984
985
sage: inequality = LC([x[0], 1/2*x[1], 3/4*x[2]]); inequality
986
x_0 <= 0.5*x_1 <= 0.75*x_2
987
sage: LC(inequality) is inequality
988
True
989
sage: p_QQ = MixedIntegerLinearProgram(solver='ppl')
990
sage: LC_QQ = p_QQ.linear_constraints_parent()
991
sage: LC_QQ(inequality)
992
x_0 <= 1/2*x_1 <= 3/4*x_2
993
sage: LC_QQ(inequality) is inequality
994
False
995
"""
996
if right is None and is_LinearConstraint(left):
997
if (left.parent() is self) and (left.is_equation() == equality):
998
return left
999
else:
1000
return LinearConstraint(self, (<LinearConstraint>left).constraints,
1001
equality=equality)
1002
if right is None:
1003
if isinstance(left, (list,tuple)):
1004
return LinearConstraint(self, left, equality=equality)
1005
else:
1006
return LinearConstraint(self, [left], equality=equality)
1007
else:
1008
return LinearConstraint(self, [left, right], equality=equality)
1009
1010
cpdef _coerce_map_from_(self, R):
1011
"""
1012
Allow coercion of scalars into linear functions.
1013
1014
EXAMPLES::
1015
1016
sage: p = MixedIntegerLinearProgram()
1017
sage: parent = p.linear_constraints_parent()
1018
sage: parent.coerce(int(2))
1019
trivial constraint starting with 2
1020
sage: parent._coerce_map_from_(int)
1021
True
1022
"""
1023
return self.linear_functions_parent().has_coerce_map_from(R)
1024
1025
def _an_element_(self):
1026
"""
1027
Returns an element
1028
1029
EXAMPLES::
1030
1031
sage: p = MixedIntegerLinearProgram().linear_functions_parent()
1032
sage: p._an_element_()
1033
5*x_2 + 7*x_5
1034
sage: p.an_element() # indirect doctest
1035
5*x_2 + 7*x_5
1036
"""
1037
LF = self.linear_functions_parent()
1038
return self(0) <= LF.an_element()
1039
1040
1041
1042
#*****************************************************************************
1043
#
1044
# Elements of linear constraints
1045
#
1046
#*****************************************************************************
1047
1048
_chained_comparator_hack_search = None
1049
_chained_comparator_hack_replace = None
1050
1051
cdef class LinearConstraint(Element):
1052
"""
1053
A class to represent formal Linear Constraints.
1054
1055
A Linear Constraint being an inequality between
1056
two linear functions, this class lets the user
1057
write ``LinearFunction1 <= LinearFunction2``
1058
to define the corresponding constraint, which
1059
can potentially involve several layers of such
1060
inequalities (``(A <= B <= C``), or even equalities
1061
like ``A == B``.
1062
1063
Trivial constraints (meaning that they have only one term and no
1064
relation) are also allowed. They are required for the coercion
1065
system to work.
1066
1067
.. warning::
1068
1069
This class has no reason to be instanciated by the user, and
1070
is meant to be used by instances of
1071
:class:`MixedIntegerLinearProgram`.
1072
1073
INPUT:
1074
1075
- ``parent`` -- the parent, a :class:`LinearConstraintsParent_class`
1076
1077
- ``terms`` -- a list/tuple/iterable of two or more linear
1078
functions (or things that can be converted into linear
1079
functions).
1080
1081
- ``equality`` -- boolean (default: ``False``). Whether the terms
1082
are the entries of a chained less-or-equal (``<=``) inequality
1083
or a chained equality.
1084
1085
EXAMPLE::
1086
1087
sage: p = MixedIntegerLinearProgram()
1088
sage: b = p.new_variable()
1089
sage: b[2]+2*b[3] <= b[8]-5
1090
x_0 + 2*x_1 <= -5 + x_2
1091
"""
1092
1093
def __init__(self, parent, terms, equality=False):
1094
r"""
1095
Constructor for ``LinearConstraint``
1096
1097
INPUT:
1098
1099
See :class:`LinearConstraint`.
1100
1101
EXAMPLE::
1102
1103
sage: p = MixedIntegerLinearProgram()
1104
sage: b = p.new_variable()
1105
sage: b[2]+2*b[3] <= b[8]-5
1106
x_0 + 2*x_1 <= -5 + x_2
1107
"""
1108
assert len(terms) > 0
1109
super(LinearConstraint, self).__init__(parent)
1110
self.equality = equality
1111
LF = parent.linear_functions_parent()
1112
self.constraints = [ LF(term) for term in terms ]
1113
1114
cpdef equals(LinearConstraint left, LinearConstraint right):
1115
"""
1116
Compare ``left`` and ``right``.
1117
1118
OUTPUT:
1119
1120
Boolean. Whether all terms of ``left`` and ``right`` are
1121
equal. Note that this is stronger than mathematical
1122
equivalence of the relations.
1123
1124
EXAMPLES::
1125
1126
sage: p = MixedIntegerLinearProgram()
1127
sage: x = p.new_variable()
1128
sage: (x[1] + 1 >= 2).equals(3/3 + 1*x[1] + 0*x[2] >= 8/4)
1129
True
1130
sage: (x[1] + 1 >= 2).equals(x[1] + 1-1 >= 1-1)
1131
False
1132
"""
1133
if len(left.constraints) != len(right.constraints):
1134
return False
1135
if left.equality != right.equality:
1136
return False
1137
cdef LinearFunction l, r
1138
for i in range(len(left.constraints)):
1139
l = <LinearFunction>(left.constraints[i])
1140
r = <LinearFunction>(right.constraints[i])
1141
if not l.equals(r):
1142
return False
1143
return True
1144
1145
cdef LinearConstraint _chained_comparator_hack_part1(LinearConstraint left, LinearConstraint right):
1146
"""
1147
Evil hack to allow chained constraints
1148
1149
Python translates ``x < y < z`` into:
1150
1151
.. code-block:: python
1152
1153
temp = x <= y # calls x.__richcmp__(y)
1154
if temp: # calls temp.__nonzero__()
1155
return y <= z # calls y.__richcmp__(z)
1156
else:
1157
return temp
1158
1159
but we would like ``x<=y<=z`` as output. The trick to make it
1160
work is to store ``y`` in the first call to ``__richcmp__()``
1161
and ``temp`` in the call to ``__nonzero__()``. Then we can
1162
replace ``y`` by ``x<=y`` in the second call to
1163
``__richcmp__``.
1164
1165
This function implements the first part of this hack, to be
1166
called from :meth:`__richcmp__`.
1167
"""
1168
# print '__richcmp__', left, ' compared with', right
1169
global _chained_comparator_hack_search
1170
global _chained_comparator_hack_replace
1171
cdef LinearConstraint search = _chained_comparator_hack_search
1172
cdef LinearConstraint replace = _chained_comparator_hack_replace
1173
_chained_comparator_hack_search = right
1174
if replace is None:
1175
return left
1176
assert search is not None
1177
if search.equals(left):
1178
_chained_comparator_hack_replace = None
1179
return replace
1180
else:
1181
return left
1182
1183
cdef _chained_comparator_hack_part2(self):
1184
"""
1185
This function implements the first part of this hack, to be
1186
called from :meth:`__nonzero__`.
1187
"""
1188
# print '__nonzero__', self.constraints
1189
global _chained_comparator_hack_replace
1190
_chained_comparator_hack_replace = self
1191
1192
def is_equation(self):
1193
"""
1194
Whether the constraint is a chained equation
1195
1196
OUTPUT:
1197
1198
Boolean.
1199
1200
EXAMPLES::
1201
1202
sage: p = MixedIntegerLinearProgram()
1203
sage: b = p.new_variable()
1204
sage: (b[0] == b[1]).is_equation()
1205
True
1206
sage: (b[0] <= b[1]).is_equation()
1207
False
1208
"""
1209
return self.equality
1210
1211
def is_less_or_equal(self):
1212
"""
1213
Whether the constraint is a chained less-or_equal inequality
1214
1215
OUTPUT:
1216
1217
Boolean.
1218
1219
EXAMPLES::
1220
1221
sage: p = MixedIntegerLinearProgram()
1222
sage: b = p.new_variable()
1223
sage: (b[0] == b[1]).is_less_or_equal()
1224
False
1225
sage: (b[0] <= b[1]).is_less_or_equal()
1226
True
1227
"""
1228
return not self.equality
1229
1230
def is_trivial(self):
1231
"""
1232
Test whether the constraint is trivial.
1233
1234
EXAMPLES::
1235
1236
sage: p = MixedIntegerLinearProgram()
1237
sage: LC = p.linear_constraints_parent()
1238
sage: ieq = LC(1,2); ieq
1239
1 <= 2
1240
sage: ieq.is_trivial()
1241
False
1242
1243
sage: ieq = LC(1); ieq
1244
trivial constraint starting with 1
1245
sage: ieq.is_trivial()
1246
True
1247
"""
1248
return len(self.constraints) < 2
1249
1250
def __iter__(self):
1251
"""
1252
Iterate over the terms of the chained (in)-equality
1253
1254
OUTPUT:
1255
1256
A generator yielding the individual terms of the constraint in
1257
left-to-right order.
1258
1259
EXAMPLES::
1260
1261
sage: p = MixedIntegerLinearProgram()
1262
sage: b = p.new_variable()
1263
sage: ieq = 1 <= b[0] <= b[2] <= 3 <= b[3]; ieq
1264
1 <= x_0 <= x_1 <= 3 <= x_2
1265
sage: list(ieq)
1266
[1, x_0, x_1, 3, x_2]
1267
sage: for term in ieq:
1268
... print term
1269
1
1270
x_0
1271
x_1
1272
3
1273
x_2
1274
"""
1275
for term in self.constraints:
1276
yield term
1277
1278
def equations(self):
1279
"""
1280
Iterate over the unchained(!) equations
1281
1282
OUTPUT:
1283
1284
An iterator over pairs ``(lhs, rhs)`` such that the individual
1285
equations are ``lhs == rhs``.
1286
1287
EXAMPLES::
1288
1289
sage: p = MixedIntegerLinearProgram()
1290
sage: b = p.new_variable()
1291
sage: eqns = 1 == b[0] == b[2] == 3 == b[3]; eqns
1292
1 == x_0 == x_1 == 3 == x_2
1293
1294
sage: for lhs, rhs in eqns.equations():
1295
... print str(lhs) + ' == ' + str(rhs)
1296
1 == x_0
1297
x_0 == x_1
1298
x_1 == 3
1299
3 == x_2
1300
"""
1301
if not self.is_equation() or self.is_trivial():
1302
raise StopIteration
1303
term_iter = iter(self)
1304
lhs = term_iter.next()
1305
rhs = term_iter.next()
1306
while True:
1307
yield (lhs, rhs)
1308
lhs = rhs
1309
rhs = term_iter.next()
1310
1311
def inequalities(self):
1312
"""
1313
Iterate over the unchained(!) inequalities
1314
1315
OUTPUT:
1316
1317
An iterator over pairs ``(lhs, rhs)`` such that the individual
1318
equations are ``lhs <= rhs``.
1319
1320
EXAMPLES::
1321
1322
sage: p = MixedIntegerLinearProgram()
1323
sage: b = p.new_variable()
1324
sage: ieq = 1 <= b[0] <= b[2] <= 3 <= b[3]; ieq
1325
1 <= x_0 <= x_1 <= 3 <= x_2
1326
1327
sage: for lhs, rhs in ieq.inequalities():
1328
... print str(lhs) + ' <= ' + str(rhs)
1329
1 <= x_0
1330
x_0 <= x_1
1331
x_1 <= 3
1332
3 <= x_2
1333
"""
1334
if not self.is_less_or_equal() or self.is_trivial():
1335
raise StopIteration
1336
term_iter = iter(self)
1337
lhs = term_iter.next()
1338
rhs = term_iter.next()
1339
while True:
1340
yield (lhs, rhs)
1341
lhs = rhs
1342
rhs = term_iter.next()
1343
1344
def _repr_(self):
1345
r"""
1346
Returns a string representation of the constraint.
1347
1348
OUTPUT:
1349
1350
String.
1351
1352
EXAMPLE::
1353
1354
sage: p = MixedIntegerLinearProgram()
1355
sage: b = p.new_variable()
1356
sage: b[3] <= b[8] + 9
1357
x_0 <= 9 + x_1
1358
1359
sage: LC = p.linear_constraints_parent()
1360
sage: LC(b[3], b[8] + 9)
1361
x_0 <= 9 + x_1
1362
sage: LC(b[3])
1363
trivial constraint starting with x_0
1364
"""
1365
comparator = ( ' == ' if self.equality else ' <= ' )
1366
result = comparator.join(map(str, self))
1367
if self.is_trivial():
1368
return 'trivial constraint starting with '+result
1369
return result
1370
1371
def __nonzero__(self):
1372
"""
1373
Part of the hack to allow chained (in)equalities
1374
1375
EXAMPLES::
1376
1377
sage: p = MixedIntegerLinearProgram()
1378
sage: b = p.new_variable()
1379
sage: ieq = (b[3] <= b[8] + 9)
1380
sage: ieq <= ieq <= ieq
1381
x_0 <= 9 + x_1 <= x_0 <= 9 + x_1 <= x_0 <= 9 + x_1
1382
"""
1383
self._chained_comparator_hack_part2()
1384
return True
1385
1386
def __richcmp__(left, right, int op):
1387
"""
1388
Override the rich comparison.
1389
1390
The Sage framework sometimes expects that rich comparison
1391
results in a boolean value, but we want to return
1392
:class:`~sage.numerical.linear_functions.LinearConstraint`
1393
objects.
1394
1395
EXAMPLES::
1396
1397
sage: p = MixedIntegerLinearProgram()
1398
sage: b = p.new_variable()
1399
sage: b[0] <= b[1] <= b[2] <= b[3]
1400
x_0 <= x_1 <= x_2 <= x_3
1401
sage: b[0] <= 1 <= b[1] <= 2 <= b[2] <= 3
1402
x_0 <= 1 <= x_1 <= 2 <= x_2 <= 3
1403
"""
1404
return (<LinearConstraint>left)._richcmp(right, op)
1405
1406
cdef _richcmp(py_left, py_right, int op):
1407
"""
1408
Chain (in)equalities
1409
"""
1410
# print 'richcmp', py_left, ', ', py_right
1411
LC = py_left.parent()
1412
if not is_LinearConstraint(py_right):
1413
py_right = LC(py_right, equality=py_left.is_equation())
1414
elif py_right.parent() is not LC:
1415
py_right = LC(py_right.constraints, equality=py_left.is_equation())
1416
cdef LinearConstraint right = <LinearConstraint>py_right
1417
cdef LinearConstraint left = py_left._chained_comparator_hack_part1(right)
1418
if op == Py_LT:
1419
raise ValueError("strict < is not allowed, use <= instead.")
1420
elif op == Py_EQ:
1421
if not (left.is_equation() and right.is_equation()):
1422
raise ValueError("can only chain together equations")
1423
return LC(left.constraints + right.constraints, equality=True)
1424
elif op == Py_GT:
1425
raise ValueError("strict > is not allowed, use >= instead.")
1426
elif op == Py_LE:
1427
if not (left.is_less_or_equal() and right.is_less_or_equal()):
1428
raise ValueError("can only chain together inequalities")
1429
return LC(left.constraints + right.constraints)
1430
elif op == Py_NE:
1431
raise ValueError("inequality != is not allowed, use one of <=, ==, >=.")
1432
elif op == Py_GE:
1433
if not (left.is_less_or_equal() and right.is_less_or_equal()):
1434
raise ValueError("can only chain together inequalities")
1435
return LC(right.constraints + left.constraints)
1436
else:
1437
assert(False) # unreachable
1438
1439