Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/algebras/letterplace/free_algebra_letterplace.pyx
8822 views
1
###############################################################################
2
#
3
# Copyright (C) 2011 Simon King <[email protected]>
4
# Distributed under the terms of the GNU General Public License (GPL),
5
# version 2 or any later version. The full text of the GPL is available at:
6
# http://www.gnu.org/licenses/
7
#
8
###############################################################################
9
10
"""
11
Free associative unital algebras, implemented via Singular's letterplace rings
12
13
AUTHOR:
14
15
- Simon King (2011-03-21): :trac:`7797`
16
17
With this implementation, Groebner bases out to a degree bound and
18
normal forms can be computed for twosided weighted homogeneous ideals
19
of free algebras. For now, all computations are restricted to weighted
20
homogeneous elements, i.e., other elements can not be created by
21
arithmetic operations.
22
23
EXAMPLES::
24
25
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
26
sage: F
27
Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
28
sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
29
sage: I
30
Twosided Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
31
sage: x*(x*I.0-I.1*y+I.0*y)-I.1*y*z
32
x*y*x*y + x*y*y*y - x*y*y*z + x*y*z*y + y*x*y*z + y*y*y*z
33
sage: x^2*I.0-x*I.1*y+x*I.0*y-I.1*y*z in I
34
True
35
36
The preceding containment test is based on the computation of Groebner
37
bases with degree bound::
38
39
sage: I.groebner_basis(degbound=4)
40
Twosided Ideal (y*z*y*y - y*z*y*z + y*z*z*y - y*z*z*z, y*z*y*x + y*z*y*z + y*z*z*x + y*z*z*z, y*y*z*y - y*y*z*z + y*z*z*y - y*z*z*z, y*y*z*x + y*y*z*z + y*z*z*x + y*z*z*z, y*y*y - y*y*z + y*z*y - y*z*z, y*y*x + y*y*z + y*z*x + y*z*z, x*y + y*z, x*x - y*x - y*y - y*z) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
41
42
When reducing an element by `I`, the original generators are chosen::
43
44
sage: (y*z*y*y).reduce(I)
45
y*z*y*y
46
47
However, there is a method for computing the normal form of an
48
element, which is the same as reduction by the Groebner basis out to
49
the degree of that element::
50
51
sage: (y*z*y*y).normal_form(I)
52
y*z*y*z - y*z*z*y + y*z*z*z
53
sage: (y*z*y*y).reduce(I.groebner_basis(4))
54
y*z*y*z - y*z*z*y + y*z*z*z
55
56
The default term order derives from the degree reverse lexicographic
57
order on the commutative version of the free algebra::
58
59
sage: F.commutative_ring().term_order()
60
Degree reverse lexicographic term order
61
62
A different term order can be chosen, and of course may yield a
63
different normal form::
64
65
sage: L.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace', order='lex')
66
sage: L.commutative_ring().term_order()
67
Lexicographic term order
68
sage: J = L*[a*b+b*c,a^2+a*b-b*c-c^2]*L
69
sage: J.groebner_basis(4)
70
Twosided Ideal (2*b*c*b - b*c*c + c*c*b, a*c*c - 2*b*c*a - 2*b*c*c - c*c*a, a*b + b*c, a*a - 2*b*c - c*c) of Free Associative Unital Algebra on 3 generators (a, b, c) over Rational Field
71
sage: (b*c*b*b).normal_form(J)
72
1/2*b*c*c*b - 1/2*c*c*b*b
73
74
Here is an example with degree weights::
75
76
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[1,2,3])
77
sage: (x*y+z).degree()
78
3
79
80
TEST::
81
82
sage: TestSuite(F).run()
83
sage: TestSuite(L).run()
84
sage: loads(dumps(F)) is F
85
True
86
87
TODO:
88
89
The computation of Groebner bases only works for global term
90
orderings, and all elements must be weighted homogeneous with respect
91
to positive integral degree weights. It is ongoing work in Singular to
92
lift these restrictions.
93
94
We support coercion from the letterplace wrapper to the corresponding
95
generic implementation of a free algebra
96
(:class:`~sage.algebras.free_algebra.FreeAlgebra_generic`), but there
97
is no coercion in the opposite direction, since the generic
98
implementation also comprises non-homogeneous elements.
99
100
We also do not support coercion from a subalgebra, or between free
101
algebras with different term orderings, yet.
102
103
"""
104
105
from sage.all import PolynomialRing, prod
106
from sage.libs.singular.function import lib, singular_function
107
from sage.rings.polynomial.term_order import TermOrder
108
from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic
109
from sage.categories.algebras import Algebras
110
from sage.rings.noncommutative_ideals import IdealMonoid_nc
111
112
#####################
113
# Define some singular functions
114
lib("freegb.lib")
115
poly_reduce = singular_function("NF")
116
singular_system=singular_function("system")
117
118
# unfortunately we can not set Singular attributes for MPolynomialRing_libsingular
119
# Hence, we must constantly work around Letterplace's sanity checks,
120
# and can not use the following library functions:
121
#set_letterplace_attributes = singular_function("setLetterplaceAttributes")
122
#lpMult = singular_function("lpMult")
123
124
#####################
125
# Auxiliar functions
126
127
cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring,blocks):
128
"""
129
Create a polynomial ring in block order.
130
131
INPUT:
132
133
- ``base_ring``: A multivariate polynomial ring.
134
- ``blocks``: The number of blocks to be formed.
135
136
OUTPUT:
137
138
A multivariate polynomial ring in block order, all blocks
139
isomorphic (as ordered rings) with the given ring, and the
140
variable names of the `n`-th block (`n>0`) ending with
141
``"_%d"%n``.
142
143
TEST:
144
145
Note that, since the algebras are cached, we need to choose
146
a different base ring, since other doctests could have a
147
side effect on the atteined degree bound::
148
149
sage: F.<x,y,z> = FreeAlgebra(GF(17), implementation='letterplace')
150
sage: L.<a,b,c> = FreeAlgebra(GF(17), implementation='letterplace', order='lex')
151
sage: F.set_degbound(4)
152
sage: F.current_ring() # indirect doctest
153
Multivariate Polynomial Ring in x, y, z, x_1, y_1, z_1, x_2, y_2, z_2, x_3, y_3, z_3 over Finite Field of size 17
154
sage: F.current_ring().term_order()
155
Block term order with blocks:
156
(Degree reverse lexicographic term order of length 3,
157
Degree reverse lexicographic term order of length 3,
158
Degree reverse lexicographic term order of length 3,
159
Degree reverse lexicographic term order of length 3)
160
sage: L.set_degbound(2)
161
sage: L.current_ring().term_order()
162
Block term order with blocks:
163
(Lexicographic term order of length 3,
164
Lexicographic term order of length 3)
165
166
"""
167
n = base_ring.ngens()
168
T0 = base_ring.term_order()
169
T = T0
170
cdef i
171
cdef tuple names0 = base_ring.variable_names()
172
cdef list names = list(names0)
173
for i from 1<=i<blocks:
174
T += T0
175
names.extend([x+'_'+str(i) for x in names0])
176
return PolynomialRing(base_ring.base_ring(),len(names),names,order=T)
177
178
#####################
179
# The free algebra
180
181
cdef class FreeAlgebra_letterplace(Algebra):
182
"""
183
Finitely generated free algebra, with arithmetic restricted to weighted homogeneous elements.
184
185
NOTE:
186
187
The restriction to weighted homogeneous elements should be lifted
188
as soon as the restriction to homogeneous elements is lifted in
189
Singular's "Letterplace algebras".
190
191
EXAMPLE::
192
193
sage: K.<z> = GF(25)
194
sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
195
sage: F
196
Free Associative Unital Algebra on 3 generators (a, b, c) over Finite Field in z of size 5^2
197
sage: P = F.commutative_ring()
198
sage: P
199
Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2
200
201
We can do arithmetic as usual, as long as we stay (weighted) homogeneous::
202
203
sage: (z*a+(z+1)*b+2*c)^2
204
(z + 3)*a*a + (2*z + 3)*a*b + (2*z)*a*c + (2*z + 3)*b*a + (3*z + 4)*b*b + (2*z + 2)*b*c + (2*z)*c*a + (2*z + 2)*c*b - c*c
205
sage: a+1
206
Traceback (most recent call last):
207
...
208
ArithmeticError: Can only add elements of the same weighted degree
209
210
"""
211
# It is not really a free algebra over the given generators. Rather,
212
# it is a free algebra over the commutative monoid generated by the given generators.
213
def __init__(self, R, degrees=None):
214
"""
215
INPUT:
216
217
A multivariate polynomial ring of type :class:`~sage.rings.polynomial.multipolynomial_libsingular.MPolynomialRing_libsingular`.
218
219
OUTPUT:
220
221
The free associative version of the given commutative ring.
222
223
NOTE:
224
225
One is supposed to use the `FreeAlgebra` constructor, in order to use the cache.
226
227
TEST::
228
229
sage: from sage.algebras.letterplace.free_algebra_letterplace import FreeAlgebra_letterplace
230
sage: FreeAlgebra_letterplace(QQ['x','y'])
231
Free Associative Unital Algebra on 2 generators (x, y) over Rational Field
232
sage: FreeAlgebra_letterplace(QQ['x'])
233
Traceback (most recent call last):
234
...
235
TypeError: A letterplace algebra must be provided by a polynomial ring of type <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomialRing_libsingular'>
236
237
::
238
239
sage: K.<z> = GF(25)
240
sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
241
sage: TestSuite(F).run(verbose=True)
242
running ._test_additive_associativity() . . . pass
243
running ._test_an_element() . . . pass
244
running ._test_associativity() . . . pass
245
running ._test_category() . . . pass
246
running ._test_characteristic() . . . pass
247
running ._test_distributivity() . . . pass
248
running ._test_elements() . . .
249
Running the test suite of self.an_element()
250
running ._test_category() . . . pass
251
running ._test_eq() . . . pass
252
running ._test_nonzero_equal() . . . pass
253
running ._test_not_implemented_methods() . . . pass
254
running ._test_pickling() . . . pass
255
pass
256
running ._test_elements_eq_reflexive() . . . pass
257
running ._test_elements_eq_symmetric() . . . pass
258
running ._test_elements_eq_transitive() . . . pass
259
running ._test_elements_neq() . . . pass
260
running ._test_eq() . . . pass
261
running ._test_not_implemented_methods() . . . pass
262
running ._test_one() . . . pass
263
running ._test_pickling() . . . pass
264
running ._test_prod() . . . pass
265
running ._test_some_elements() . . . pass
266
running ._test_zero() . . . pass
267
268
"""
269
if not isinstance(R,MPolynomialRing_libsingular):
270
raise TypeError, "A letterplace algebra must be provided by a polynomial ring of type %s"%MPolynomialRing_libsingular
271
self.__ngens = R.ngens()
272
if degrees is None:
273
varnames = R.variable_names()
274
self._nb_slackvars = 0
275
else:
276
varnames = R.variable_names()[:-1]
277
self._nb_slackvars = 1
278
base_ring = R.base_ring()
279
Algebra.__init__(self, base_ring, varnames,
280
normalize=False, category=Algebras(base_ring))
281
self._commutative_ring = R
282
self._current_ring = make_letterplace_ring(R,1)
283
self._degbound = 1
284
if degrees is None:
285
self._degrees = tuple([int(1)]*self.__ngens)
286
else:
287
if (not isinstance(degrees,(tuple,list))) or len(degrees)!=self.__ngens-1 or any([i<=0 for i in degrees]):
288
raise TypeError, "The generator degrees must be given by a list or tuple of %d positive integers"%(self.__ngens-1)
289
self._degrees = tuple([int(i) for i in degrees])
290
self.set_degbound(max(self._degrees))
291
self._populate_coercion_lists_(coerce_list=[base_ring])
292
def __reduce__(self):
293
"""
294
TEST::
295
296
sage: K.<z> = GF(25)
297
sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
298
sage: loads(dumps(F)) is F # indirect doctest
299
True
300
301
"""
302
from sage.algebras.free_algebra import FreeAlgebra
303
if self._nb_slackvars==0:
304
return FreeAlgebra,(self._commutative_ring,)
305
return FreeAlgebra,(self._commutative_ring,None,None,None,None,None,None,None,self._degrees)
306
# Small methods
307
def ngens(self):
308
"""
309
Return the number of generators.
310
311
EXAMPLE::
312
313
sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
314
sage: F.ngens()
315
3
316
317
"""
318
return self.__ngens-self._nb_slackvars
319
def gen(self,i):
320
"""
321
Return the `i`-th generator.
322
323
INPUT:
324
325
`i` -- an integer.
326
327
OUTPUT:
328
329
Generator number `i`.
330
331
EXAMPLE::
332
333
sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
334
sage: F.1 is F.1 # indirect doctest
335
True
336
sage: F.gen(2)
337
c
338
339
"""
340
if i>=self.__ngens-self._nb_slackvars:
341
raise ValueError, "This free algebra only has %d generators"%(self.__ngens-self._nb_slackvars)
342
if self._gens is not None:
343
return self._gens[i]
344
deg = self._degrees[i]
345
#self.set_degbound(deg)
346
p = self._current_ring.gen(i)
347
cdef int n
348
cdef int j = self.__ngens-1
349
for n from 1<=n<deg:
350
j += self.__ngens
351
p *= self._current_ring.gen(j)
352
return FreeAlgebraElement_letterplace(self, p)
353
def current_ring(self):
354
"""
355
Return the commutative ring that is used to emulate
356
the non-commutative multiplication out to the current degree.
357
358
EXAMPLE::
359
360
sage: F.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace')
361
sage: F.current_ring()
362
Multivariate Polynomial Ring in a, b, c over Rational Field
363
sage: a*b
364
a*b
365
sage: F.current_ring()
366
Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1 over Rational Field
367
sage: F.set_degbound(3)
368
sage: F.current_ring()
369
Multivariate Polynomial Ring in a, b, c, a_1, b_1, c_1, a_2, b_2, c_2 over Rational Field
370
371
"""
372
return self._current_ring
373
def commutative_ring(self):
374
"""
375
Return the commutative version of this free algebra.
376
377
NOTE:
378
379
This commutative ring is used as a unique key of the free algebra.
380
381
EXAMPLE::
382
383
sage: K.<z> = GF(25)
384
sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace')
385
sage: F
386
Free Associative Unital Algebra on 3 generators (a, b, c) over Finite Field in z of size 5^2
387
sage: F.commutative_ring()
388
Multivariate Polynomial Ring in a, b, c over Finite Field in z of size 5^2
389
sage: FreeAlgebra(F.commutative_ring()) is F
390
True
391
392
"""
393
return self._commutative_ring
394
def term_order_of_block(self):
395
"""
396
Return the term order that is used for the commutative version of this free algebra.
397
398
EXAMPLE::
399
400
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
401
sage: F.term_order_of_block()
402
Degree reverse lexicographic term order
403
sage: L.<a,b,c> = FreeAlgebra(QQ, implementation='letterplace',order='lex')
404
sage: L.term_order_of_block()
405
Lexicographic term order
406
407
"""
408
return self._commutative_ring.term_order()
409
410
def generator_degrees(self):
411
return self._degrees
412
413
# Some basic properties of this ring
414
def is_commutative(self):
415
"""
416
Tell whether this algebra is commutative, i.e., whether the generator number is one.
417
418
EXAMPLE::
419
420
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
421
sage: F.is_commutative()
422
False
423
sage: FreeAlgebra(QQ, implementation='letterplace', names=['x']).is_commutative()
424
True
425
426
"""
427
return self.__ngens-self._nb_slackvars <= 1
428
429
def is_field(self):
430
"""
431
Tell whether this free algebra is a field.
432
433
NOTE:
434
435
This would only be the case in the degenerate case of no generators.
436
But such an example can not be constructed in this implementation.
437
438
TEST::
439
440
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
441
sage: F.is_field()
442
False
443
444
"""
445
return (not (self.__ngens-self._nb_slackvars)) and self._base.is_field()
446
447
def _repr_(self):
448
"""
449
EXAMPLE::
450
451
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
452
sage: F # indirect doctest
453
Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
454
455
The degree weights are not part of the string representation::
456
457
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[2,1,3])
458
sage: F
459
Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
460
461
462
"""
463
return "Free Associative Unital Algebra on %d generators %s over %s"%(self.__ngens-self._nb_slackvars,self.gens(),self._base)
464
465
def _latex_(self):
466
"""
467
Representation of this free algebra in LaTeX.
468
469
EXAMPLE::
470
471
sage: F.<bla,alpha,z> = FreeAlgebra(QQ, implementation='letterplace', degrees=[1,2,3])
472
sage: latex(F)
473
\Bold{Q}\langle \mbox{bla}, \alpha, z\rangle
474
475
"""
476
from sage.all import latex
477
return "%s\\langle %s\\rangle"%(latex(self.base_ring()),', '.join(self.latex_variable_names()))
478
479
def degbound(self):
480
"""
481
Return the degree bound that is currently used.
482
483
NOTE:
484
485
When multiplying two elements of this free algebra, the degree
486
bound will be dynamically adapted. It can also be set by
487
:meth:`set_degbound`.
488
489
EXAMPLE:
490
491
In order to avoid we get a free algebras from the cache that
492
was created in another doctest and has a different degree
493
bound, we choose a base ring that does not appear in other tests::
494
495
sage: F.<x,y,z> = FreeAlgebra(ZZ, implementation='letterplace')
496
sage: F.degbound()
497
1
498
sage: x*y
499
x*y
500
sage: F.degbound()
501
2
502
sage: F.set_degbound(4)
503
sage: F.degbound()
504
4
505
506
"""
507
return self._degbound
508
def set_degbound(self,d):
509
"""
510
Increase the degree bound that is currently in place.
511
512
NOTE:
513
514
The degree bound can not be decreased.
515
516
EXAMPLE:
517
518
In order to avoid we get a free algebras from the cache that
519
was created in another doctest and has a different degree
520
bound, we choose a base ring that does not appear in other tests::
521
522
sage: F.<x,y,z> = FreeAlgebra(GF(251), implementation='letterplace')
523
sage: F.degbound()
524
1
525
sage: x*y
526
x*y
527
sage: F.degbound()
528
2
529
sage: F.set_degbound(4)
530
sage: F.degbound()
531
4
532
sage: F.set_degbound(2)
533
sage: F.degbound()
534
4
535
536
"""
537
if d<=self._degbound:
538
return
539
self._degbound = d
540
self._current_ring = make_letterplace_ring(self._commutative_ring,d)
541
542
# def base_extend(self, R):
543
# if self._base.has_coerce_map_from(R):
544
# return self
545
546
################################################
547
## Ideals
548
549
def _ideal_class_(self, n=0):
550
"""
551
Return the class :class:`~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal`.
552
553
EXAMPLE::
554
555
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
556
sage: I = [x*y+y*z,x^2+x*y-y*x-y^2]*F
557
sage: I
558
Right Ideal (x*y + y*z, x*x + x*y - y*x - y*y) of Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field
559
sage: type(I) is F._ideal_class_()
560
True
561
562
"""
563
from sage.algebras.letterplace.letterplace_ideal import LetterplaceIdeal
564
return LetterplaceIdeal
565
566
def ideal_monoid(self):
567
"""
568
Return the monoid of ideals of this free algebra.
569
570
EXAMPLE::
571
572
sage: F.<x,y> = FreeAlgebra(GF(2), implementation='letterplace')
573
sage: F.ideal_monoid()
574
Monoid of ideals of Free Associative Unital Algebra on 2 generators (x, y) over Finite Field of size 2
575
sage: F.ideal_monoid() is F.ideal_monoid()
576
True
577
578
"""
579
if self.__monoid is None:
580
self.__monoid = IdealMonoid_nc(self)
581
return self.__monoid
582
583
# Auxiliar methods
584
cdef str exponents_to_string(self, E):
585
"""
586
This auxiliary method is used for the string representation of elements of this free algebra.
587
588
EXAMPLE::
589
590
sage: F.<x,y,z> = FreeAlgebra(GF(2), implementation='letterplace')
591
sage: x*y*x*z # indirect doctest
592
x*y*x*z
593
594
It should be possible to use the letterplace algebra to implement the
595
free algebra generated by the elements of a finitely generated free abelian
596
monoid. However, we can not use it, yet. So, for now, we raise an error::
597
598
sage: from sage.algebras.letterplace.free_algebra_element_letterplace import FreeAlgebraElement_letterplace
599
sage: P = F.commutative_ring()
600
sage: FreeAlgebraElement_letterplace(F, P.0*P.1^2+P.1^3) # indirect doctest
601
Traceback (most recent call last):
602
...
603
NotImplementedError:
604
Apparently you tried to view the letterplace algebra with
605
shift-multiplication as the free algebra over a finitely
606
generated free abelian monoid.
607
In principle, this is correct, but it is not implemented, yet.
608
609
"""
610
cdef int ngens = self.__ngens
611
cdef int nblocks = len(E)/ngens
612
cdef int i,j,base, exp, var_ind
613
cdef list out = []
614
cdef list tmp
615
for i from 0<=i<nblocks:
616
base = i*ngens
617
tmp = [(j,E[base+j]) for j in xrange(ngens) if E[base+j]]
618
if not tmp:
619
continue
620
var_ind, exp = tmp[0]
621
if len(tmp)>1 or exp>1:
622
raise NotImplementedError, "\n Apparently you tried to view the letterplace algebra with\n shift-multiplication as the free algebra over a finitely\n generated free abelian monoid.\n In principle, this is correct, but it is not implemented, yet."
623
624
out.append(self._names[var_ind])
625
i += (self._degrees[var_ind]-1)
626
### This was the original implementation, with "monoid hack" but without generator degrees
627
#s = '.'.join([('%s^%d'%(x,e) if e>1 else x) for x,e in zip(self._names,E[i*ngens:(i+1)*ngens]) if e])
628
#if s:
629
# out.append(s)
630
return '*'.join(out)
631
632
# Auxiliar methods
633
cdef str exponents_to_latex(self, E):
634
"""
635
This auxiliary method is used for the representation of elements of this free algebra as a latex string.
636
637
EXAMPLE::
638
639
sage: K.<z> = GF(25)
640
sage: F.<a,b,c> = FreeAlgebra(K, implementation='letterplace', degrees=[1,2,3])
641
sage: -(a*b*(z+1)-c)^2
642
(2*z + 1)*a*b*a*b + (z + 1)*a*b*c + (z + 1)*c*a*b - c*c
643
sage: latex(-(a*b*(z+1)-c)^2) # indirect doctest
644
\left(2 z + 1\right) a b a b + \left(z + 1\right) a b c + \left(z + 1\right) c a b - c c
645
646
"""
647
cdef int ngens = self.__ngens
648
cdef int nblocks = len(E)/ngens
649
cdef int i,j,base, exp, var_ind
650
cdef list out = []
651
cdef list tmp
652
cdef list names = self.latex_variable_names()
653
for i from 0<=i<nblocks:
654
base = i*ngens
655
tmp = [(j,E[base+j]) for j in xrange(ngens) if E[base+j]]
656
if not tmp:
657
continue
658
var_ind, exp = tmp[0]
659
if len(tmp)>1 or exp>1:
660
raise NotImplementedError, "\n Apparently you tried to view the letterplace algebra with\n shift-multiplication as the free algebra over a finitely\n generated free abelian monoid.\n In principle, this is correct, but it is not implemented, yet."
661
662
out.append(names[var_ind])
663
i += (self._degrees[var_ind]-1)
664
return ' '.join(out)
665
666
def _reductor_(self, g, d):
667
"""
668
Return a commutative ideal that can be used to compute the normal
669
form of a free algebra element of a given degree.
670
671
INPUT:
672
673
``g`` - a list of elements of this free algebra.
674
``d`` - an integer.
675
676
OUTPUT:
677
678
An ideal such that reduction of a letterplace polynomial by that ideal corresponds
679
to reduction of an element of degree at most ``d`` by ``g``.
680
681
EXAMPLE::
682
683
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
684
sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F
685
sage: p = y*x*y + y*y*y + y*z*y - y*z*z
686
sage: p.reduce(I)
687
y*y*y - y*y*z + y*z*y - y*z*z
688
sage: G = F._reductor_(I.gens(),3); G
689
Ideal (x*y_1 + y*z_1, x_1*y_2 + y_1*z_2, x*x_1 + x*y_1 - y*x_1 - y*y_1, x_1*x_2 + x_1*y_2 - y_1*x_2 - y_1*y_2) of Multivariate Polynomial Ring in x, y, z, x_1, y_1, z_1, x_2, y_2, z_2... over Rational Field
690
691
We do not use the usual reduction method for polynomials in
692
Sage, since it does the reductions in a different order
693
compared to Singular. Therefore, we call the original Singular
694
reduction method, and prevent a warning message by asserting
695
that `G` is a Groebner basis.
696
697
sage: from sage.libs.singular.function import singular_function
698
sage: poly_reduce = singular_function("NF")
699
sage: q = poly_reduce(p.letterplace_polynomial(), G, ring=F.current_ring(), attributes={G:{"isSB":1}}); q
700
y*y_1*y_2 - y*y_1*z_2 + y*z_1*y_2 - y*z_1*z_2
701
sage: p.reduce(I).letterplace_polynomial() == q
702
True
703
704
"""
705
cdef list out = []
706
C = self.current_ring()
707
cdef FreeAlgebraElement_letterplace x
708
ngens = self.__ngens
709
degbound = self._degbound
710
cdef list G = [C(x._poly) for x in g]
711
for y in G:
712
out.extend([y]+[singular_system("stest",y,n+1,degbound,ngens,ring=C) for n in xrange(d-y.degree())])
713
return C.ideal(out)
714
715
###########################
716
## Coercion
717
cpdef _coerce_map_from_(self,S):
718
"""
719
A ring ``R`` coerces into self, if
720
721
- it coerces into the current polynomial ring, or
722
- it is a free graded algebra in letterplace implementation,
723
the generator names of ``R`` are a proper subset of the
724
generator names of self, the degrees of equally named
725
generators are equal, and the base ring of ``R`` coerces
726
into the base ring of self.
727
728
TEST:
729
730
Coercion from the base ring::
731
732
sage: F.<x,y,z> = FreeAlgebra(GF(5), implementation='letterplace')
733
sage: 5 == F.zero() # indirect doctest
734
True
735
736
Coercion from another free graded algebra::
737
738
sage: F.<t,y,z> = FreeAlgebra(ZZ, implementation='letterplace', degrees=[4,2,3])
739
sage: G = FreeAlgebra(GF(5), implementation='letterplace', names=['x','y','z','t'], degrees=[1,2,3,4])
740
sage: t*G.0 # indirect doctest
741
t*x
742
743
"""
744
if self==S or self._current_ring.has_coerce_map_from(S):
745
return True
746
cdef int i
747
# Do we have another letterplace algebra?
748
if not isinstance(S, FreeAlgebra_letterplace):
749
return False
750
# Do the base rings coerce?
751
if not self.base_ring().has_coerce_map_from(S.base_ring()):
752
return False
753
# Do the names match?
754
cdef tuple degs, Sdegs, names, Snames
755
names = self.variable_names()
756
Snames = S.variable_names()
757
if not set(names).issuperset(Snames):
758
return False
759
# Do the degrees match
760
degs = self._degrees
761
Sdegs = (<FreeAlgebra_letterplace>S)._degrees
762
for i from 0<=i<S.ngens():
763
if degs[names.index(Snames[i])] != Sdegs[i]:
764
return False
765
return True
766
767
def _an_element_(self):
768
"""
769
Return an element.
770
771
EXAMPLE::
772
773
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
774
sage: F.an_element() # indirect doctest
775
x
776
777
"""
778
return FreeAlgebraElement_letterplace(self, self._current_ring.an_element(), check=False)
779
780
# def random_element(self, degree=2, terms=5):
781
# """
782
# Return a random element of a given degree and with a given number of terms.
783
#
784
# INPUT:
785
#
786
# - ``degree`` -- the maximal degree of the output (default 2).
787
# - ``terms`` -- the maximal number of terms of the output (default 5).
788
#
789
# NOTE:
790
#
791
# This method is currently not useful at all.
792
#
793
# Not tested.
794
# """
795
# self.set_degbound(degree)
796
# while(1):
797
# p = self._current_ring.random_element(degree=degree,terms=terms)
798
# if p.is_homogeneous():
799
# break
800
# return FreeAlgebraElement_letterplace(self, p, check=False)
801
802
def _from_dict_(self, D, check=True):
803
"""
804
Create an element from a dictionary.
805
806
INPUT:
807
808
- A dictionary. Keys: tuples of exponents. Values:
809
The coefficients of the corresponding monomial
810
in the to-be-created element.
811
- ``check`` (optional bool, default ``True``):
812
This is forwarded to the initialisation of
813
:class:`~sage.algebas.letterplace.free_algebra_element_letterplace.FreeAlgebraElement_letterplace`.
814
815
TEST:
816
817
This method applied to the dictionary of any element must
818
return the same element. This must hold true even if the
819
underlying letterplace ring has been extended in the meantime.
820
::
821
822
sage: F.<x,y,z> = FreeAlgebra(QQ, implementation='letterplace')
823
sage: p = 3*x*y+2*z^2
824
sage: F.set_degbound(10)
825
sage: p == F._from_dict_(dict(p))
826
True
827
828
For the empty dictionary, zero is returned::
829
830
sage: F._from_dict_({})
831
0
832
833
"""
834
if not D:
835
return self.zero_element()
836
cdef int l
837
for e in D.iterkeys():
838
l = len(e)
839
break
840
cdef dict out = {}
841
self.set_degbound(l/self.__ngens)
842
cdef int n = self._current_ring.ngens()
843
for e,c in D.iteritems():
844
out[tuple(e)+(0,)*(n-l)] = c
845
return FreeAlgebraElement_letterplace(self,self._current_ring(out),
846
check=check)
847
848
def _element_constructor_(self, x):
849
"""
850
Return an element of this free algebra.
851
852
INPUT:
853
854
An element of a free algebra with a proper subset of generator
855
names, or anything that can be interpreted in the polynomial
856
ring that is used to implement the letterplace algebra out to
857
the current degree bound, or a string that can be interpreted
858
as an expression in the algebra (provided that the
859
coefficients are numerical).
860
861
EXAMPLE::
862
863
sage: F.<t,y,z> = FreeAlgebra(ZZ, implementation='letterplace', degrees=[4,2,3])
864
865
Conversion of a number::
866
867
sage: F(3)
868
3
869
870
Interpretation of a string as an algebra element::
871
872
sage: F('t*y+3*z^2')
873
t*y + 3*z*z
874
875
Conversion from the currently underlying polynomial ring::
876
877
sage: F.set_degbound(3)
878
sage: P = F.current_ring()
879
sage: F(P.0*P.7*P.11*P.15*P.17*P.23 - 2*P.2*P.7*P.11*P.14*P.19*P.23)
880
t*y - 2*z*z
881
882
Conversion from a graded sub-algebra::
883
884
sage: G = FreeAlgebra(GF(5), implementation='letterplace', names=['x','y','z','t'], degrees=[1,2,3,4])
885
sage: G(t*y + 2*y^3 - 4*z^2) # indirect doctest
886
(2)*y*y*y + z*z + t*y
887
888
"""
889
if isinstance(x, basestring):
890
from sage.all import sage_eval
891
return sage_eval(x,locals=self.gens_dict())
892
try:
893
P = x.parent()
894
except AttributeError:
895
P = None
896
if P is self:
897
(<FreeAlgebraElement_letterplace>x)._poly = self._current_ring((<FreeAlgebraElement_letterplace>x)._poly)
898
return x
899
if isinstance(P, FreeAlgebra_letterplace):
900
self.set_degbound(P.degbound())
901
Ppoly = (<FreeAlgebra_letterplace>P)._current_ring
902
Gens = self._current_ring.gens()
903
Names = self._current_ring.variable_names()
904
PNames = list(Ppoly.variable_names())
905
# translate the slack variables
906
PNames[P.ngens(): len(PNames): P.ngens()+1] = list(Names[self.ngens(): len(Names): self.ngens()+1])[:P.degbound()]
907
x = Ppoly.hom([Gens[Names.index(asdf)] for asdf in PNames])(x.letterplace_polynomial())
908
return FreeAlgebraElement_letterplace(self,self._current_ring(x))
909
910