Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/structure/formal_sum.py
4057 views
1
"""
2
Formal sums
3
4
AUTHORS:
5
6
- David Harvey (2006-09-20): changed FormalSum not to derive from
7
"list" anymore, because that breaks new Element interface
8
9
- Nick Alexander (2006-12-06): added test cases.
10
11
- William Stein (2006, 2009): wrote the first version in 2006, documented it in 2009.
12
13
- Volker Braun (2010-07-19): new-style coercions, documentation
14
added. FormalSums now derives from UniqueRepresentation.
15
16
FUNCTIONS:
17
- ``FormalSums(ring)`` -- create the module of formal finite sums with
18
coefficients in the given ring.
19
20
- ``FormalSum(list of pairs (coeff, number))`` -- create a formal sum
21
22
EXAMPLES::
23
24
sage: A = FormalSum([(1, 2/3)]); A
25
2/3
26
sage: B = FormalSum([(3, 1/5)]); B
27
3*1/5
28
sage: -B
29
-3*1/5
30
sage: A + B
31
3*1/5 + 2/3
32
sage: A - B
33
-3*1/5 + 2/3
34
sage: B*3
35
9*1/5
36
sage: 2*A
37
2*2/3
38
sage: list(2*A + A)
39
[(3, 2/3)]
40
41
TESTS::
42
43
sage: R = FormalSums(QQ)
44
sage: loads(dumps(R)) == R
45
True
46
sage: a = R(2/3) + R(-5/7); a
47
-5/7 + 2/3
48
sage: loads(dumps(a)) == a
49
True
50
"""
51
52
#*****************************************************************************
53
# Copyright (C) 2004 William Stein <[email protected]>
54
#
55
# Distributed under the terms of the GNU General Public License (GPL)
56
#
57
# This code is distributed in the hope that it will be useful,
58
# but WITHOUT ANY WARRANTY; without even the implied warranty of
59
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
60
# General Public License for more details.
61
#
62
# The full text of the GPL is available at:
63
#
64
# http://www.gnu.org/licenses/
65
#*****************************************************************************
66
67
import sage.misc.misc
68
import operator
69
import sage.misc.latex
70
71
from sage.modules.module import Module_old as Module
72
from sage.structure.element import ModuleElement
73
from sage.rings.integer_ring import ZZ
74
from sage.structure.parent import Parent
75
from sage.structure.parent_base import ParentWithBase
76
from sage.structure.coerce import LeftModuleAction, RightModuleAction
77
from sage.categories.action import PrecomposedAction
78
from sage.structure.unique_representation import UniqueRepresentation
79
80
81
class FormalSums(UniqueRepresentation, Module):
82
"""
83
The R-module of finite formal sums with coefficients in some ring R.
84
85
EXAMPLES::
86
87
sage: FormalSums()
88
Abelian Group of all Formal Finite Sums over Integer Ring
89
sage: FormalSums(ZZ[sqrt(2)])
90
Abelian Group of all Formal Finite Sums over Order in Number Field in sqrt2 with defining polynomial x^2 - 2
91
sage: FormalSums(GF(9,'a'))
92
Abelian Group of all Formal Finite Sums over Finite Field in a of size 3^2
93
"""
94
95
@staticmethod
96
def __classcall__(cls, base_ring = ZZ):
97
"""
98
Set the default value for the base ring.
99
100
EXAMPLES::
101
102
sage: FormalSums(ZZ) == FormalSums() # indirect test
103
True
104
"""
105
return UniqueRepresentation.__classcall__(cls, base_ring)
106
107
def __init__(self, base_ring):
108
"""
109
INPUT:
110
111
- ``R`` -- a ring (default: ZZ).
112
113
EXAMPLES::
114
115
sage: FormalSums(ZZ)
116
Abelian Group of all Formal Finite Sums over Integer Ring
117
sage: FormalSums(GF(7))
118
Abelian Group of all Formal Finite Sums over Finite Field of size 7
119
"""
120
ParentWithBase.__init__(self, base_ring)
121
122
def _repr_(self):
123
"""
124
EXAMPLES::
125
sage: FormalSums(GF(7))
126
Abelian Group of all Formal Finite Sums over Finite Field of size 7
127
sage: FormalSums(GF(7))._repr_()
128
'Abelian Group of all Formal Finite Sums over Finite Field of size 7'
129
"""
130
return "Abelian Group of all Formal Finite Sums over %s"%self.base_ring()
131
132
def _element_constructor_(self, x, check=True, reduce=True):
133
"""
134
Make a formal sum in self from x.
135
136
INPUT:
137
138
- ``x`` -- formal sum, list or number
139
140
- ``check`` -- bool (default: True)
141
142
- ``reduce`` -- bool (default: True); whether to combine terms
143
144
EXAMPLES::
145
146
sage: P = FormalSum([(1,2/3)]).parent()
147
sage: P([(1,2/3), (5,-2/9)]) # indirect test
148
5*-2/9 + 2/3
149
"""
150
if isinstance(x, FormalSum):
151
P = x.parent()
152
if P is self:
153
return x
154
elif P == self:
155
return FormalSum(x._data, check=False, reduce=False, parent=self)
156
else:
157
x = x._data
158
if isinstance(x, list):
159
return FormalSum(x, check=check,reduce=reduce,parent=self)
160
if x == 0:
161
return FormalSum([], check=False, reduce=False, parent=self)
162
else:
163
return FormalSum([(self.base_ring()(1), x)], check=False, reduce=False, parent=self)
164
165
def _coerce_map_from_(self, X):
166
r"""
167
Return whether there is a coercion from ``X``
168
169
EXAMPLE::
170
171
sage: FormalSums(QQ).has_coerce_map_from( FormalSums(ZZ) ) # indirect test
172
True
173
174
sage: FormalSums(ZZ).get_action(QQ) # indirect test
175
Right scalar multiplication by Rational Field on Abelian Group of all Formal Finite Sums over Rational Field
176
with precomposition on left by Conversion map:
177
From: Abelian Group of all Formal Finite Sums over Integer Ring
178
To: Abelian Group of all Formal Finite Sums over Rational Field
179
"""
180
if isinstance(X,FormalSums):
181
if self.base_ring().has_coerce_map_from(X.base_ring()):
182
return True
183
return False
184
185
def base_extend(self, R):
186
"""
187
EXAMPLES::
188
189
sage: FormalSums(ZZ).base_extend(GF(7))
190
Abelian Group of all Formal Finite Sums over Finite Field of size 7
191
"""
192
if self.base_ring().has_coerce_map_from(R):
193
return self
194
elif R.has_coerce_map_from(self.base_ring()):
195
return self.__class__(R)
196
197
def _get_action_(self, other, op, self_is_left):
198
"""
199
EXAMPLES::
200
201
sage: A = FormalSums(RR).get_action(RR); A # indirect doctest
202
Right scalar multiplication by Real Field with 53 bits of precision on Abelian Group of all Formal Finite Sums over Real Field with 53 bits of precision
203
204
sage: A = FormalSums(ZZ).get_action(QQ); A
205
Right scalar multiplication by Rational Field on Abelian Group of all Formal Finite Sums over Rational Field
206
with precomposition on left by Conversion map:
207
From: Abelian Group of all Formal Finite Sums over Integer Ring
208
To: Abelian Group of all Formal Finite Sums over Rational Field
209
sage: A = FormalSums(QQ).get_action(ZZ); A
210
Right scalar multiplication by Integer Ring on Abelian Group of all Formal Finite Sums over Rational Field
211
"""
212
if op is operator.mul and isinstance(other, Parent):
213
extended = self.base_extend(other)
214
if self_is_left:
215
action = RightModuleAction(other, extended)
216
if extended is not self:
217
action = PrecomposedAction(action, extended.coerce_map_from(self), None)
218
else:
219
action = LeftModuleAction(other, extended)
220
if extended is not self:
221
action = PrecomposedAction(action, None, extended.coerce_map_from(self))
222
return action
223
224
def _an_element_(self, check=False, reduce=False):
225
"""
226
EXAMPLES::
227
228
sage: FormalSums(ZZ).an_element() # indirect test
229
1
230
sage: FormalSums(QQ).an_element()
231
1/2*1
232
sage: QQ.an_element()
233
1/2
234
"""
235
return FormalSum([(self.base_ring().an_element(), 1)],
236
check=check, reduce=reduce, parent=self)
237
238
239
formal_sums = FormalSums()
240
241
# Formal sums now derives from UniqueRepresentation, which makes the
242
# factory function unnecessary. This is why the name was changed from
243
# class FormalSums_generic to class FormalSums.
244
from sage.structure.sage_object import register_unpickle_override
245
register_unpickle_override('sage.structure.formal_sum', 'FormalSums_generic', FormalSums)
246
247
248
class FormalSum(ModuleElement):
249
"""
250
A formal sum over a ring.
251
"""
252
def __init__(self, x, parent=formal_sums, check=True, reduce=True):
253
"""
254
INPUT:
255
- ``x`` -- object
256
- ``parent`` -- FormalSums(R) module (default: FormalSums(ZZ))
257
- ``check`` -- bool (default: True) if False, might not coerce
258
coefficients into base ring, which can speed
259
up constructing a formal sum.
260
- ``reduce`` -- reduce (default: True) if False, do not
261
combine common terms
262
263
EXAMPLES::
264
265
sage: FormalSum([(1,2/3), (3,2/3), (-5, 7)])
266
4*2/3 - 5*7
267
sage: a = FormalSum([(1,2/3), (3,2/3), (-5, 7)], reduce=False); a
268
2/3 + 3*2/3 - 5*7
269
sage: a.reduce()
270
sage: a
271
4*2/3 - 5*7
272
sage: FormalSum([(1,2/3), (3,2/3), (-5, 7)], parent=FormalSums(GF(5)))
273
4*2/3
274
275
Notice below that the coefficient 5 doesn't get reduced modulo 5::
276
277
sage: FormalSum([(1,2/3), (3,2/3), (-5, 7)], parent=FormalSums(GF(5)), check=False)
278
4*2/3 - 5*7
279
280
Make sure we first reduce before checking coefficient types::
281
282
sage: x,y = var('x, y')
283
sage: FormalSum([(1/2,x), (2,y)], FormalSums(QQ))
284
1/2*x + 2*y
285
sage: FormalSum([(1/2,x), (2,y)], FormalSums(ZZ))
286
Traceback (most recent call last):
287
...
288
TypeError: no conversion of this rational to integer
289
sage: FormalSum([(1/2,x), (1/2,x), (2,y)], FormalSums(ZZ))
290
x + 2*y
291
"""
292
if x == 0:
293
x = []
294
self._data = x
295
if parent is None:
296
parent = formal_sums
297
ModuleElement.__init__(self, parent)
298
if reduce: # first reduce
299
self.reduce()
300
if check: # then check
301
k = parent.base_ring()
302
try:
303
self._data = [(k(t[0]), t[1]) for t in self._data]
304
except (IndexError, KeyError), msg:
305
raise TypeError, "%s\nInvalid formal sum"%msg
306
307
def __iter__(self):
308
"""
309
EXAMPLES::
310
311
sage: for z in FormalSum([(1,2), (5, 1000), (-3, 7)]): print z
312
(1, 2)
313
(-3, 7)
314
(5, 1000)
315
"""
316
return iter(self._data)
317
318
def __getitem__(self, n):
319
"""
320
EXAMPLES::
321
322
sage: v = FormalSum([(1,2), (5, 1000), (-3, 7)]); v
323
2 - 3*7 + 5*1000
324
sage: v[0]
325
(1, 2)
326
sage: v[1]
327
(-3, 7)
328
sage: v[2]
329
(5, 1000)
330
sage: list(v)
331
[(1, 2), (-3, 7), (5, 1000)]
332
"""
333
return self._data[n]
334
335
def __len__(self):
336
"""
337
EXAMPLES::
338
339
sage: v = FormalSum([(1,2), (5, 1000), (-3, 7)]); v
340
2 - 3*7 + 5*1000
341
sage: len(v)
342
3
343
"""
344
return len(self._data)
345
346
def _repr_(self):
347
"""
348
EXAMPLES::
349
350
sage: a = FormalSum([(1,2/3), (-3,4/5), (7,Mod(2,3))])
351
sage: a # random, since comparing Mod(2,3) and rationals ill-defined
352
sage: a._repr_() # random
353
'2/3 - 3*4/5 + 7*2'
354
"""
355
return sage.misc.misc.repr_lincomb([t,c] for c,t in self)
356
357
def _latex_(self):
358
"""
359
EXAMPLES::
360
361
sage: latex(FormalSum([(1,2), (5, 8/9), (-3, 7)]))
362
5\cdot \frac{8}{9} + 2 - 3\cdot 7
363
"""
364
symbols = [z[1] for z in self]
365
coeffs= [z[0] for z in self]
366
return sage.misc.latex.repr_lincomb(symbols, coeffs)
367
# TODO: finish merging sage.misc.latex.repr_lincomb and
368
# sage.misc.misc.repr_lincomb and use instead:
369
# return sage.misc.misc.repr_lincomb([[t,c] for c,t in self], is_latex=True)
370
371
def __cmp__(left, right):
372
"""
373
Compare ``left`` and ``right``.
374
375
INPUT:
376
377
- ``right`` -- a :class:`FormalSum` with the same parent.
378
379
EXAMPLES::
380
381
sage: a = FormalSum([(1,3),(2,5)]); a
382
3 + 2*5
383
sage: b = FormalSum([(1,3),(2,7)]); b
384
3 + 2*7
385
sage: abs(cmp(a,b)) # indirect test
386
1
387
sage: a_QQ = FormalSum([(1,3),(2,5)],parent=FormalSums(QQ))
388
sage: abs(cmp(a,a_QQ)) # a is coerced into FormalSums(QQ)
389
0
390
sage: abs(cmp(a,0)) # 0 is coerced into a.parent()(0)
391
1
392
sage: abs(cmp(a,'string')) # will NOT evaluate via this method
393
1
394
"""
395
# if necessary, left and right have already been coerced to the same parent()
396
return cmp(left._data, right._data)
397
398
def _neg_(self):
399
"""
400
EXAMPLES::
401
402
sage: -FormalSum([(1,3),(2,5)])
403
-3 - 2*5
404
"""
405
return self.__class__([(-c, s) for (c, s) in self._data], check=False, parent=self.parent())
406
407
def _add_(self, other):
408
"""
409
EXAMPLES::
410
411
sage: FormalSum([(1,3/7),(2,5/8)]) + FormalSum([(1,3/7),(-2,5)]) # indirect doctest
412
2*3/7 + 2*5/8 - 2*5
413
"""
414
return self.__class__(self._data + other._data, check=False, parent=self.parent())
415
416
def _lmul_(self, s):
417
"""
418
EXAMPLES::
419
420
sage: FormalSum([(1,3/7),(-2,5)])*(-3)
421
-3*3/7 + 6*5
422
"""
423
return self.__class__([(c*s, x) for (c, x) in self], check=False, parent=self.parent())
424
425
def _rmul_(self, s):
426
"""
427
EXAMPLES::
428
429
sage: -3*FormalSum([(1,3/7),(-2,5)])
430
-3*3/7 + 6*5
431
"""
432
return self.__class__([(s*c, x) for (c, x) in self], check=False, parent=self.parent())
433
434
def __nonzero__(self):
435
"""
436
EXAMPLES::
437
438
sage: bool(FormalSum([(1,3/7),(-2,5)]))
439
True
440
sage: bool(FormalSums(QQ)(0))
441
False
442
sage: bool(FormalSums(QQ)(1))
443
True
444
"""
445
if len(self._data) == 0:
446
return False
447
for c, _ in self._data:
448
if not c.is_zero():
449
return True
450
return False
451
452
def reduce(self):
453
"""
454
EXAMPLES::
455
456
sage: a = FormalSum([(-2,3), (2,3)], reduce=False); a
457
-2*3 + 2*3
458
sage: a.reduce()
459
sage: a
460
0
461
"""
462
if len(self) == 0:
463
return
464
v = [(x,c) for c, x in self if c!=0]
465
if len(v) == 0:
466
self._data = v
467
return
468
v.sort()
469
w = []
470
last = v[0][0]
471
coeff = v[0][1]
472
for x, c in v[1:]:
473
if x == last:
474
coeff += c
475
else:
476
if coeff != 0:
477
w.append((coeff, last))
478
last = x
479
coeff = c
480
if coeff != 0:
481
w.append((coeff,last))
482
self._data = w
483
484