Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/linear_expression.py
8817 views
1
"""
2
Linear Expressions
3
4
A linear expression is just a linear polynomial in some (fixed)
5
variables. This class only implements linear expressions for others to
6
use.
7
8
EXAMPLES::
9
10
sage: from sage.geometry.linear_expression import LinearExpressionModule
11
sage: L.<x,y,z> = LinearExpressionModule(QQ); L
12
Module of linear expressions in variables x, y, z over Rational Field
13
sage: x + 2*y + 3*z + 4
14
x + 2*y + 3*z + 4
15
16
You can also pass coefficients and a constant term to construct linear
17
expressions::
18
19
sage: L([1, 2, 3], 4)
20
x + 2*y + 3*z + 4
21
sage: L([(1, 2, 3), 4])
22
x + 2*y + 3*z + 4
23
sage: L([4, 1, 2, 3]) # note: constant is first in single-tuple notation
24
x + 2*y + 3*z + 4
25
26
The linear expressions are a module under the base ring, so you can
27
add them and multiply them with scalars::
28
29
sage: m = x + 2*y + 3*z + 4
30
sage: 2*m
31
2*x + 4*y + 6*z + 8
32
sage: m+m
33
2*x + 4*y + 6*z + 8
34
sage: m-m
35
0*x + 0*y + 0*z + 0
36
"""
37
38
from sage.structure.parent import Parent
39
from sage.structure.element import ModuleElement
40
from sage.structure.unique_representation import UniqueRepresentation
41
from sage.misc.cachefunc import cached_method
42
43
44
45
class LinearExpression(ModuleElement):
46
"""
47
A linear expression.
48
49
A linear expression is just a linear polynomial in some (fixed)
50
variables.
51
52
EXAMPLES::
53
54
sage: from sage.geometry.linear_expression import LinearExpressionModule
55
sage: L.<x,y,z> = LinearExpressionModule(QQ)
56
sage: m = L([1, 2, 3], 4); m
57
x + 2*y + 3*z + 4
58
sage: m2 = L([(1, 2, 3), 4]); m2
59
x + 2*y + 3*z + 4
60
sage: m3 = L([4, 1, 2, 3]); m3 # note: constant is first in single-tuple notation
61
x + 2*y + 3*z + 4
62
sage: m == m2
63
True
64
sage: m2 == m3
65
True
66
sage: L.zero()
67
0*x + 0*y + 0*z + 0
68
sage: a = L([12, 2/3, -1], -2)
69
sage: a - m
70
11*x - 4/3*y - 4*z - 6
71
sage: LZ.<x,y,z> = LinearExpressionModule(ZZ)
72
sage: a - LZ([2, -1, 3], 1)
73
10*x + 5/3*y - 4*z - 3
74
"""
75
def __init__(self, parent, coefficients, constant, check=True):
76
"""
77
Initialize ``self``.
78
79
TESTS::
80
81
sage: from sage.geometry.linear_expression import LinearExpressionModule
82
sage: L.<x,y,z> = LinearExpressionModule(QQ)
83
sage: linear = L([1, 2, 3], 4) # indirect doctest
84
sage: linear.parent() is L
85
True
86
87
sage: TestSuite(linear).run()
88
"""
89
super(LinearExpression, self).__init__(parent)
90
self._coeffs = coefficients
91
self._const = constant
92
if check:
93
if self._coeffs.parent() is not self.parent().ambient_module():
94
raise ValueError("cofficients are not in the ambient module")
95
if not self._coeffs.is_immutable():
96
raise ValueError("cofficients are not immutable")
97
if self._const.parent() is not self.parent().base_ring():
98
raise ValueError("the constant is not in the base ring")
99
100
def A(self):
101
"""
102
Return the coefficient vector.
103
104
OUTPUT:
105
106
The coefficient vector of the linear expression.
107
108
EXAMPLES::
109
110
sage: from sage.geometry.linear_expression import LinearExpressionModule
111
sage: L.<x,y,z> = LinearExpressionModule(QQ)
112
sage: linear = L([1, 2, 3], 4); linear
113
x + 2*y + 3*z + 4
114
sage: linear.A()
115
(1, 2, 3)
116
sage: linear.b()
117
4
118
"""
119
return self._coeffs
120
121
def b(self):
122
"""
123
Return the constant term.
124
125
OUTPUT:
126
127
The constant term of the linear expression.
128
129
EXAMPLES::
130
131
sage: from sage.geometry.linear_expression import LinearExpressionModule
132
sage: L.<x,y,z> = LinearExpressionModule(QQ)
133
sage: linear = L([1, 2, 3], 4); linear
134
x + 2*y + 3*z + 4
135
sage: linear.A()
136
(1, 2, 3)
137
sage: linear.b()
138
4
139
"""
140
return self._const
141
142
constant_term = b
143
144
def coefficients(self):
145
"""
146
Return all coefficients.
147
148
OUTPUT:
149
150
The constant (as first entry) and coefficients of the linear
151
terms (as subsequent entries) in a list.
152
153
EXAMPLES::
154
155
sage: from sage.geometry.linear_expression import LinearExpressionModule
156
sage: L.<x,y,z> = LinearExpressionModule(QQ)
157
sage: linear = L([1, 2, 3], 4); linear
158
x + 2*y + 3*z + 4
159
sage: linear.coefficients()
160
[4, 1, 2, 3]
161
"""
162
return [self._const] + list(self._coeffs)
163
164
def _repr_vector(self, variable='x'):
165
"""
166
Return a string representation.
167
168
INPUT:
169
170
- ``variable`` -- string; the name of the variable vector
171
172
EXAMPLES::
173
174
sage: from sage.geometry.linear_expression import LinearExpressionModule
175
sage: L.<x,y,z> = LinearExpressionModule(QQ)
176
sage: L([1, 2, 3], 4)._repr_vector()
177
'(1, 2, 3) x + 4 = 0'
178
sage: L([-1, -2, -3], -4)._repr_vector('u')
179
'(-1, -2, -3) u - 4 = 0'
180
"""
181
atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic')
182
constant = repr(self._const)
183
if not atomic_repr:
184
constant = '({0})'.format(constant)
185
constant = '+ {0}'.format(constant).replace('+ -', '- ')
186
return '{0} {1} {2} = 0'.format(repr(self._coeffs), variable, constant)
187
188
def _repr_linear(self, include_zero=True, include_constant=True, multiplication='*'):
189
"""
190
Return a representation as a linear polynomial.
191
192
INPUT:
193
194
- ``include_zero`` -- whether to include terms with zero
195
coefficient
196
197
- ``include_constant`` -- whether to include the constant
198
term
199
200
- ``multiplication`` -- string (optional, default: ``*``); the
201
multiplication symbol to use
202
203
OUTPUT:
204
205
A string.
206
207
EXAMPLES::
208
209
sage: from sage.geometry.linear_expression import LinearExpressionModule
210
sage: L.<x,y,z> = LinearExpressionModule(QQ)
211
sage: L([1, 2, 3], 4)._repr_linear()
212
'x + 2*y + 3*z + 4'
213
sage: L([-1, -2, -3], -4)._repr_linear()
214
'-x - 2*y - 3*z - 4'
215
sage: L([0, 0, 0], 1)._repr_linear()
216
'0*x + 0*y + 0*z + 1'
217
sage: L([0, 0, 0], 0)._repr_linear()
218
'0*x + 0*y + 0*z + 0'
219
220
sage: R.<u,v> = QQ[]
221
sage: L.<x,y,z> = LinearExpressionModule(R)
222
sage: L([-u+v+1, -3*u-2, 3], -4*u+v)._repr_linear()
223
'(-u + v + 1)*x + (-3*u - 2)*y + 3*z - 4*u + v'
224
225
sage: L.<x,y,z> = LinearExpressionModule(QQ)
226
sage: L([1, 0, 3], 0)._repr_linear()
227
'x + 0*y + 3*z + 0'
228
sage: L([1, 0, 3], 0)._repr_linear(include_zero=False)
229
'x + 3*z'
230
sage: L([1, 0, 3], 1)._repr_linear(include_constant=False, multiplication='.')
231
'x + 0.y + 3.z'
232
sage: L([1, 0, 3], 1)._repr_linear(include_zero=False, include_constant=False)
233
'x + 3*z'
234
sage: L([0, 0, 0], 0)._repr_linear(include_zero=False)
235
'0'
236
"""
237
atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic')
238
names = [multiplication+n for n in self.parent()._names]
239
terms = zip(self._coeffs, names)
240
if include_constant:
241
terms += [(self._const, '')]
242
if not include_zero:
243
terms = [t for t in terms if t[0] != 0]
244
if len(terms) == 0:
245
return '0'
246
summands = []
247
for coeff, name in terms:
248
coeff = str(coeff)
249
if not atomic_repr and name != '' and any(c in coeff for c in ['+', '-']):
250
coeff = '({0})'.format(coeff)
251
summands.append(coeff+name)
252
s = ' ' + ' + '.join(summands)
253
s = s.replace(' + -', ' - ')
254
s = s.replace(' 1'+multiplication, ' ')
255
s = s.replace(' -1'+multiplication, ' -')
256
return s[1:]
257
258
_repr_ = _repr_linear
259
260
def _add_(self, other):
261
"""
262
Add two linear expressions.
263
264
EXAMPLES::
265
266
sage: from sage.geometry.linear_expression import LinearExpressionModule
267
sage: L.<x,y,z> = LinearExpressionModule(QQ)
268
sage: a = L([1, 2, 3], 4)
269
sage: b = L([-1, 3, -3], 0)
270
sage: a + b
271
0*x + 5*y + 0*z + 4
272
sage: a - b
273
2*x - y + 6*z + 4
274
"""
275
const = self._const + other._const
276
coeffs = self._coeffs + other._coeffs
277
coeffs.set_immutable()
278
return self.__class__(self.parent(), coeffs, const)
279
280
def _rmul_(self, scalar):
281
"""
282
Multiply a linear expression by a scalar.
283
284
EXAMPLES::
285
286
sage: from sage.geometry.linear_expression import LinearExpressionModule
287
sage: L.<x,y,z> = LinearExpressionModule(QQ)
288
sage: a = L([1, 2, 3], 4); a
289
x + 2*y + 3*z + 4
290
sage: 2 * a
291
2*x + 4*y + 6*z + 8
292
sage: a * 2
293
2*x + 4*y + 6*z + 8
294
sage: -a
295
-x - 2*y - 3*z - 4
296
sage: RDF(1) * a
297
1.0*x + 2.0*y + 3.0*z + 4.0
298
299
TESTS::
300
301
sage: a._lmul_(2)
302
2*x + 4*y + 6*z + 8
303
"""
304
const = scalar * self._const
305
coeffs = scalar * self._coeffs
306
coeffs.set_immutable()
307
return self.__class__(self.parent(), coeffs, const)
308
309
_lmul_ = _rmul_
310
311
def _acted_upon_(self, scalar, self_on_left):
312
"""
313
Action by scalars that do not live in the base ring.
314
315
EXAMPLES::
316
317
sage: from sage.geometry.linear_expression import LinearExpressionModule
318
sage: L.<x,y,z> = LinearExpressionModule(QQ)
319
sage: a = x + 2*y + 3*z + 4
320
sage: a * RDF(3)
321
3.0*x + 6.0*y + 9.0*z + 12.0
322
"""
323
base_ring = scalar.base_ring()
324
parent = self.parent().change_ring(base_ring)
325
changed = parent(self)
326
return changed._rmul_(scalar)
327
328
def change_ring(self, base_ring):
329
"""
330
Change the base ring of this linear expression.
331
332
INPUT:
333
334
- ``base_ring`` -- a ring; the new base ring
335
336
OUTPUT:
337
338
A new linear expression over the new base ring.
339
340
EXAMPLES::
341
342
sage: from sage.geometry.linear_expression import LinearExpressionModule
343
sage: L.<x,y,z> = LinearExpressionModule(QQ)
344
sage: a = x + 2*y + 3*z + 4; a
345
x + 2*y + 3*z + 4
346
sage: a.change_ring(RDF)
347
1.0*x + 2.0*y + 3.0*z + 4.0
348
"""
349
P = self.parent()
350
if P.base_ring() is base_ring:
351
return self
352
return P.change_ring(base_ring)(self)
353
354
def __cmp__(self, other):
355
"""
356
Compare two linear expressions.
357
358
INPUT:
359
360
- ``other`` -- another linear expression (will be enforced by
361
the coercion framework)
362
363
EXAMPLES::
364
365
sage: from sage.geometry.linear_expression import LinearExpressionModule
366
sage: L.<x> = LinearExpressionModule(QQ)
367
sage: x == L([0, 1])
368
True
369
sage: x == x + 1
370
False
371
372
sage: M.<x> = LinearExpressionModule(ZZ)
373
sage: L.gen(0) == M.gen(0) # because there is a conversion
374
True
375
sage: L.gen(0) == L(M.gen(0)) # this is the conversion
376
True
377
378
sage: x == 'test'
379
False
380
"""
381
assert (type(self) is type(other)) and (self.parent() is other.parent()) # guaranteed by framework
382
c = cmp(self._coeffs, other._coeffs)
383
if c != 0: return c
384
c = cmp(self._const, other._const)
385
return c
386
387
def evaluate(self, point):
388
"""
389
Evaluate the linear expression.
390
391
INPUT:
392
393
- ``point`` -- list/tuple/iterable of coordinates; the
394
coordinates of a point
395
396
OUTPUT:
397
398
The linear expression `Ax + b` evaluated at the point `x`.
399
400
EXAMPLES::
401
402
sage: from sage.geometry.linear_expression import LinearExpressionModule
403
sage: L.<x,y> = LinearExpressionModule(QQ)
404
sage: ex = 2*x + 3* y + 4
405
sage: ex.evaluate([1,1])
406
9
407
sage: ex([1,1]) # syntactic sugar
408
9
409
sage: ex([pi, e])
410
2*pi + 3*e + 4
411
"""
412
try:
413
point = self.parent().ambient_module()(point)
414
except TypeError:
415
from sage.matrix.constructor import vector
416
point = vector(point)
417
return self._coeffs * point + self._const
418
419
__call__ = evaluate
420
421
422
423
424
class LinearExpressionModule(Parent, UniqueRepresentation):
425
"""
426
The module of linear expressions.
427
428
This is the module of linear polynomials which is the parent for
429
linear expressions.
430
431
EXAMPLES::
432
433
sage: from sage.geometry.linear_expression import LinearExpressionModule
434
sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))
435
sage: L
436
Module of linear expressions in variables x, y, z over Rational Field
437
sage: L.an_element()
438
x + 0*y + 0*z + 0
439
"""
440
Element = LinearExpression
441
442
def __init__(self, base_ring, names=tuple()):
443
"""
444
Initialize ``self``.
445
446
TESTS::
447
448
sage: from sage.geometry.linear_expression import LinearExpressionModule
449
sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))
450
sage: type(L)
451
<class 'sage.geometry.linear_expression.LinearExpressionModule_with_category'>
452
sage: L.base_ring()
453
Rational Field
454
455
sage: TestSuite(L).run()
456
457
sage: L = LinearExpressionModule(QQ)
458
sage: TestSuite(L).run()
459
"""
460
from sage.categories.modules import Modules
461
super(LinearExpressionModule, self).__init__(base_ring, category=Modules(base_ring))
462
self._names = names
463
464
@cached_method
465
def ngens(self):
466
"""
467
Return the number of linear variables.
468
469
OUTPUT:
470
471
An integer.
472
473
EXAMPLES::
474
475
sage: from sage.geometry.linear_expression import LinearExpressionModule
476
sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))
477
sage: L.ngens()
478
3
479
"""
480
return len(self._names)
481
482
@cached_method
483
def gens(self):
484
"""
485
Return the generators.
486
487
OUTPUT:
488
489
A tuple of linear expressions, one for each linear variable.
490
491
EXAMPLES::
492
493
sage: from sage.geometry.linear_expression import LinearExpressionModule
494
sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))
495
sage: L.gens()
496
(x + 0*y + 0*z + 0, 0*x + y + 0*z + 0, 0*x + 0*y + z + 0)
497
"""
498
from sage.matrix.constructor import identity_matrix
499
identity = identity_matrix(self.base_ring(), self.ngens())
500
return tuple(self(e, 0) for e in identity.rows())
501
502
def gen(self, i):
503
"""
504
Return the `i`-th generator.
505
506
INPUT:
507
508
- ``i`` -- integer
509
510
OUTPUT:
511
512
A linear expression.
513
514
EXAMPLES::
515
516
sage: from sage.geometry.linear_expression import LinearExpressionModule
517
sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))
518
sage: L.gen(0)
519
x + 0*y + 0*z + 0
520
"""
521
return self.gens()[i]
522
523
def _element_constructor_(self, arg0, arg1=None):
524
"""
525
The element constructor.
526
527
This is part of the Sage parent/element framework.
528
529
TESTS::
530
531
sage: from sage.geometry.linear_expression import LinearExpressionModule
532
sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))
533
534
Construct from coeffients and constant term::
535
536
sage: L._element_constructor([1, 2, 3], 4)
537
x + 2*y + 3*z + 4
538
sage: L._element_constructor(vector(ZZ, [1, 2, 3]), 4)
539
x + 2*y + 3*z + 4
540
541
Construct constant linear expression term::
542
543
sage: L._element_constructor(4)
544
0*x + 0*y + 0*z + 4
545
546
Construct from list/tuple/iterable::
547
548
sage: L._element_constructor(vector([4, 1, 2, 3]))
549
x + 2*y + 3*z + 4
550
551
Construct from a pair ``(coefficients, constant)``::
552
553
sage: L([(1, 2, 3), 4])
554
x + 2*y + 3*z + 4
555
556
Construct from linear expression::
557
558
sage: M = LinearExpressionModule(ZZ, ('u', 'v', 'w'))
559
sage: m = M([1, 2, 3], 4)
560
sage: L._element_constructor(m)
561
x + 2*y + 3*z + 4
562
"""
563
R = self.base_ring()
564
if arg1 is None:
565
if arg0 in R:
566
const = arg0
567
coeffs = self.ambient_module().zero()
568
elif isinstance(arg0, LinearExpression):
569
# Construct from linear expression
570
const = arg0.b()
571
coeffs = arg0.A()
572
elif isinstance(arg0, (list, tuple)) and len(arg0) == 2 and isinstance(arg0[0], (list, tuple)):
573
# Construct from pair
574
coeffs = arg0[0]
575
const = arg0[1]
576
else:
577
# Construct from list/tuple/iterable::
578
try:
579
arg0 = arg0.coefficients()
580
except AttributeError:
581
arg0 = list(arg0)
582
const = arg0[0]
583
coeffs = arg0[1:]
584
else:
585
# arg1 is not None, construct from coeffients and constant term::
586
coeffs = list(arg0)
587
const = arg1
588
coeffs = self.ambient_module()(coeffs)
589
coeffs.set_immutable()
590
const = R(const)
591
return self.element_class(self, coeffs, const)
592
593
def random_element(self):
594
"""
595
Return a random element.
596
597
EXAMPLES::
598
599
sage: from sage.geometry.linear_expression import LinearExpressionModule
600
sage: L.<x,y,z> = LinearExpressionModule(QQ)
601
sage: L.random_element()
602
-1/2*x - 1/95*y + 1/2*z - 12
603
"""
604
A = self.ambient_module().random_element()
605
b = self.base_ring().random_element()
606
return self(A, b)
607
608
@cached_method
609
def ambient_module(self):
610
"""
611
Return the ambient module.
612
613
.. SEEALSO::
614
615
:meth:`ambient_vector_space`
616
617
OUTPUT:
618
619
The domain of the linear expressions as a free module over the
620
base ring.
621
622
EXAMPLES::
623
624
sage: from sage.geometry.linear_expression import LinearExpressionModule
625
sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))
626
sage: L.ambient_module()
627
Vector space of dimension 3 over Rational Field
628
sage: M = LinearExpressionModule(ZZ, ('r', 's'))
629
sage: M.ambient_module()
630
Ambient free module of rank 2 over the principal ideal domain Integer Ring
631
sage: M.ambient_vector_space()
632
Vector space of dimension 2 over Rational Field
633
"""
634
from sage.modules.all import FreeModule
635
return FreeModule(self.base_ring(), self.ngens())
636
637
@cached_method
638
def ambient_vector_space(self):
639
"""
640
Return the ambient vector space.
641
642
.. SEEALSO::
643
644
:meth:`ambient_module`
645
646
OUTPUT:
647
648
The vector space (over the fraction field of the base ring)
649
where the linear expressions live.
650
651
EXAMPLES::
652
653
sage: from sage.geometry.linear_expression import LinearExpressionModule
654
sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z'))
655
sage: L.ambient_vector_space()
656
Vector space of dimension 3 over Rational Field
657
sage: M = LinearExpressionModule(ZZ, ('r', 's'))
658
sage: M.ambient_module()
659
Ambient free module of rank 2 over the principal ideal domain Integer Ring
660
sage: M.ambient_vector_space()
661
Vector space of dimension 2 over Rational Field
662
"""
663
from sage.modules.all import VectorSpace
664
field = self.base_ring().fraction_field()
665
return VectorSpace(field, self.ngens())
666
667
def _coerce_map_from_(self, P):
668
"""
669
Return whether there is a coercion.
670
671
TESTS::
672
673
sage: from sage.geometry.linear_expression import LinearExpressionModule
674
sage: L.<x> = LinearExpressionModule(QQ)
675
sage: M.<y> = LinearExpressionModule(ZZ)
676
sage: L.coerce_map_from(M)
677
Conversion map:
678
From: Module of linear expressions in variable y over Integer Ring
679
To: Module of linear expressions in variable x over Rational Field
680
sage: M.coerce_map_from(L)
681
682
sage: M.coerce_map_from(ZZ)
683
Conversion map:
684
From: Integer Ring
685
To: Module of linear expressions in variable y over Integer Ring
686
sage: M.coerce_map_from(QQ)
687
"""
688
if self.base().has_coerce_map_from(P):
689
return True
690
try:
691
return self.ngens() == P.ngens() and \
692
self.base().has_coerce_map_from(P.base())
693
except AttributeError:
694
pass
695
return super(LinearExpressionModule, self)._coerce_map_from_(P)
696
697
def _repr_(self):
698
"""
699
Return a string representation.
700
701
OUTPUT:
702
703
A string.
704
705
EXAMPLES::
706
707
sage: from sage.geometry.linear_expression import LinearExpressionModule
708
sage: L.<x> = LinearExpressionModule(QQ); L
709
Module of linear expressions in variable x over Rational Field
710
"""
711
return 'Module of linear expressions in variable{2} {0} over {1}'.format(
712
', '.join(self._names), self.base_ring(), 's' if self.ngens() > 1 else '')
713
714
def change_ring(self, base_ring):
715
"""
716
Return a new module with a changed base ring.
717
718
INPUT:
719
720
- ``base_ring`` -- a ring; the new base ring
721
722
OUTPUT:
723
724
A new linear expression over the new base ring.
725
726
EXAMPLES::
727
728
sage: from sage.geometry.linear_expression import LinearExpressionModule
729
sage: M.<y> = LinearExpressionModule(ZZ)
730
sage: L = M.change_ring(QQ); L
731
Module of linear expressions in variable y over Rational Field
732
733
TESTS::
734
735
sage: L.change_ring(QQ) is L
736
True
737
"""
738
return LinearExpressionModule(base_ring, self._names)
739
740
741