Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/interfaces/magma.py
4057 views
1
r"""
2
Interface to Magma
3
4
.. note::
5
6
You must have ``magma`` installed on your
7
computer for this interface to work. Magma is not free, so it is
8
not included with Sage, but you can obtain it from
9
http://magma.maths.usyd.edu.au/.
10
11
Type ``magma.[tab]`` for a list of all the functions
12
available from your Magma install. Type
13
``magma.[tab]?`` for Magma's help about a given
14
function. Type ``magma(...)`` to create a new Magma
15
object, and ``magma.eval(...)`` to run a string using
16
Magma (and get the result back as a string).
17
18
Sage provides an interface to the Magma computational algebra
19
system. This system provides extensive functionality for number
20
theory, group theory, combinatorics and algebra.
21
22
The Magma interface offers three pieces of functionality:
23
24
25
#. ``magma_console()`` - A function that dumps you
26
into an interactive command-line Magma session.
27
28
#. ``magma(expr)`` - Evaluation of arbitrary Magma
29
expressions, with the result returned as a string.
30
31
#. ``magma.new(expr)`` - Creation of a Sage object that
32
wraps a Magma object. This provides a Pythonic interface to Magma.
33
For example, if ``f=magma.new(10)``, then
34
``f.Factors()`` returns the prime factorization of
35
`10` computed using Magma.
36
37
38
Parameters
39
----------
40
41
Some Magma functions have optional "parameters", which are
42
arguments that in Magma go after a colon. In Sage, you pass these
43
using named function arguments. For example,
44
45
::
46
47
sage: E = magma('EllipticCurve([0,1,1,-1,0])') # optional - magma
48
sage: E.Rank(Bound = 5) # optional - magma
49
0
50
51
Multiple Return Values
52
----------------------
53
54
Some Magma functions return more than one value. You can control
55
how many you get using the ``nvals`` named parameter to
56
a function call::
57
58
sage: n = magma(100) # optional - magma
59
sage: n.IsSquare(nvals = 1) # optional - magma
60
true
61
sage: n.IsSquare(nvals = 2) # optional - magma
62
(true, 10)
63
sage: n = magma(-2006) # optional - magma
64
sage: n.Factorization() # optional - magma
65
[ <2, 1>, <17, 1>, <59, 1> ]
66
sage: n.Factorization(nvals=2) # optional - magma
67
([ <2, 1>, <17, 1>, <59, 1> ], -1)
68
69
We verify that an obviously principal ideal is principal::
70
71
sage: _ = magma.eval('R<x> := PolynomialRing(RationalField())') # optional - magma
72
sage: O = magma.NumberField('x^2+23').MaximalOrder() # optional - magma
73
sage: I = magma('ideal<%s|%s.1>'%(O.name(),O.name())) # optional - magma
74
sage: I.IsPrincipal(nvals=2) # optional - magma
75
(true, [1, 0])
76
77
Long Input
78
----------
79
80
The Magma interface reads in even very long input (using files) in
81
a robust manner.
82
83
::
84
85
sage: t = '"%s"'%10^10000 # ten thousand character string. # optional - magma
86
sage: a = magma.eval(t) # optional - magma
87
sage: a = magma(t) # optional - magma
88
89
Garbage Collection
90
------------------
91
92
There is a subtle point with the Magma interface, which arises from
93
how garbage collection works. Consider the following session:
94
95
First, create a matrix m in Sage::
96
97
sage: m=matrix(ZZ,2,[1,2,3,4]) # optional - magma
98
99
Then I create a corresponding matrix A in Magma::
100
101
sage: A = magma(m) # optional - magma
102
103
It is called _sage_[...] in Magma::
104
105
sage: s = A.name(); s # optional - magma
106
'_sage_[...]'
107
108
It's there::
109
110
sage: magma.eval(s) # optional - magma
111
'[1 2]\n[3 4]'
112
113
Now I delete the reference to that matrix::
114
115
sage: del A # optional - magma
116
117
Now _sage_[...] is "zeroed out" in the Magma session::
118
119
sage: magma.eval(s) # optional - magma
120
'0'
121
122
If Sage did not do this garbage collection, then every single time you
123
ever create any magma object from a sage object, e.g., by doing
124
magma(m), you would use up a lot of memory in that Magma session.
125
This would lead to a horrible memory leak situation, which would make
126
the Magma interface nearly useless for serious work.
127
128
129
Other Examples
130
--------------
131
132
We compute a space of modular forms with character.
133
134
::
135
136
sage: N = 20
137
sage: D = 20
138
sage: eps_top = fundamental_discriminant(D)
139
sage: eps = magma.KroneckerCharacter(eps_top, RationalField()) # optional - magma
140
sage: M2 = magma.ModularForms(eps) # optional - magma
141
sage: print M2 # optional - magma
142
Space of modular forms on Gamma_1(5) ...
143
sage: print M2.Basis() # optional - magma
144
[
145
1 + 10*q^2 + 20*q^3 + 20*q^5 + 60*q^7 + ...
146
q + q^2 + 2*q^3 + 3*q^4 + 5*q^5 + 2*q^6 + ...
147
]
148
149
In Sage/Python (and sort of C++) coercion of an element x into a
150
structure S is denoted by S(x). This also works for the Magma
151
interface::
152
153
sage: G = magma.DirichletGroup(20) # optional - magma
154
sage: G.AssignNames(['a', 'b']) # optional - magma
155
sage: (G.1).Modulus() # optional - magma
156
20
157
sage: e = magma.DirichletGroup(40)(G.1) # optional - magma
158
sage: print e # optional - magma
159
$.1
160
sage: print e.Modulus() # optional - magma
161
40
162
163
We coerce some polynomial rings into Magma::
164
165
sage: R.<y> = PolynomialRing(QQ)
166
sage: S = magma(R) # optional - magma
167
sage: print S # optional - magma
168
Univariate Polynomial Ring in y over Rational Field
169
sage: S.1 # optional - magma
170
y
171
172
This example illustrates that Sage doesn't magically extend how
173
Magma implicit coercion (what there is, at least) works. The errors
174
below are the result of Magma having a rather limited automatic
175
coercion system compared to Sage's::
176
177
sage: R.<x> = ZZ[]
178
sage: x * 5
179
5*x
180
sage: x * 1.0
181
x
182
sage: x * (2/3)
183
2/3*x
184
sage: y = magma(x) # optional - magma
185
sage: y * 5 # optional - magma
186
5*x
187
sage: y * 1.0 # optional - magma
188
$.1
189
sage: y * (2/3) # optional - magma
190
Traceback (most recent call last):
191
...
192
TypeError: Error evaluating Magma code.
193
...
194
Runtime error in '*': Bad argument types
195
Argument types given: RngUPolElt[RngInt], FldRatElt
196
197
198
AUTHORS:
199
200
- William Stein (2005): initial version
201
202
- William Stein (2006-02-28): added extensive tab completion and
203
interactive IPython documentation support.
204
205
- William Stein (2006-03-09): added nvals argument for
206
magma.functions...
207
"""
208
209
#*****************************************************************************
210
# Copyright (C) 2005 William Stein <[email protected]>
211
#
212
# Distributed under the terms of the GNU General Public License (GPL)
213
#
214
# This code is distributed in the hope that it will be useful,
215
# but WITHOUT ANY WARRANTY; without even the implied warranty of
216
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
217
# General Public License for more details.
218
#
219
# The full text of the GPL is available at:
220
#
221
# http://www.gnu.org/licenses/
222
#*****************************************************************************
223
224
import re, sys
225
226
from sage.structure.parent import Parent
227
from expect import console, Expect, ExpectElement, ExpectFunction, FunctionElement
228
PROMPT = ">>>"
229
230
SAGE_REF = "_sage_ref"
231
SAGE_REF_RE = re.compile('%s\d+'%SAGE_REF)
232
233
import sage.misc.misc
234
import sage.misc.sage_eval
235
236
INTRINSIC_CACHE = '%s/magma_intrinsic_cache.sobj'%sage.misc.misc.DOT_SAGE
237
238
239
EXTCODE_DIR = None
240
def extcode_dir():
241
"""
242
Return directory that contains all the Magma extcode. This is put
243
in a writable directory owned by the user, since when attached,
244
Magma has to write sig and lck files.
245
246
EXAMPLES:
247
sage: sage.interfaces.magma.extcode_dir()
248
'...dir_.../data/'
249
"""
250
global EXTCODE_DIR
251
if not EXTCODE_DIR:
252
import shutil
253
tmp = sage.misc.misc.tmp_dir()
254
shutil.copytree('%s/magma/'%sage.misc.misc.SAGE_EXTCODE, tmp + '/data')
255
EXTCODE_DIR = "%s/data/"%tmp
256
return EXTCODE_DIR
257
258
259
class Magma(Expect):
260
r"""
261
Interface to the Magma interpreter.
262
263
Type ``magma.[tab]`` for a list of all the functions
264
available from your Magma install. Type
265
``magma.[tab]?`` for Magma's help about a given
266
function. Type ``magma(...)`` to create a new Magma
267
object, and ``magma.eval(...)`` to run a string using
268
Magma (and get the result back as a string).
269
270
.. note::
271
272
If you do not own a local copy of Magma, try using the
273
``magma_free`` command instead, which uses the free demo web
274
interface to Magma.
275
276
EXAMPLES:
277
278
You must use nvals = 0 to call a function that doesn't return
279
anything, otherwise you'll get an error. (nvals is the number of
280
return values.)
281
282
::
283
284
sage: magma.SetDefaultRealFieldPrecision(200, nvals=0) # magma >= v2.12; optional - magma
285
sage: magma.eval('1.1') # optional - magma
286
'1.1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
287
sage: magma.SetDefaultRealFieldPrecision(30, nvals=0) # optional - magma
288
"""
289
def __init__(self, maxread=10000, script_subdirectory=None,
290
logfile=None, server=None, server_tmpdir=None, user_config=False):
291
"""
292
INPUT:
293
294
295
- ``maxread`` - affects buffering
296
297
- ``script_subdirectory`` - directory where scripts
298
are read from
299
300
- ``logfile`` - output logged to this file
301
302
- ``server`` - address of remote server
303
304
- ``user_config`` - if True, then local user
305
configuration files will be read by Magma. If False (the default),
306
then Magma is started with the -n option which suppresses user
307
configuration files.
308
309
310
EXAMPLES::
311
312
sage: Magma(maxread=1000, logfile=tmp_filename())
313
Magma
314
"""
315
# If the -b argument is given to Magma, the opening banner and all other
316
# introductory messages are suppressed. The final "total time" message is
317
# also suppressed.
318
#command = 'sage-native-execute magma -b '
319
command = 'sage-native-execute magma'
320
if not user_config:
321
command += ' -n'
322
Expect.__init__(self,
323
name = "magma",
324
prompt = ">>SAGE>>",
325
command = command,
326
maxread = maxread,
327
server = server,
328
server_tmpdir = server_tmpdir,
329
script_subdirectory = script_subdirectory,
330
restart_on_ctrlc = False,
331
logfile = logfile,
332
eval_using_file_cutoff=100)
333
# We use "-n" above in the Magma startup command so
334
# local user startup configuration is not read.
335
336
self.__seq = 0
337
self.__ref = 0
338
self.__available_var = []
339
self.__cache = {}
340
self._preparse_colon_equals = False # if set to try, all "=" become ":=" (some users really appreciate this)
341
342
def __reduce__(self):
343
"""
344
Used to pickle a magma interface instance.
345
346
Unpickling results in the default magma interpreter; this is a
347
choice, and perhaps not the most logical one! It means that if you
348
make two distinct magma interfaces, pickle both, then unpickle
349
them, you get back exactly the same one. We illustrate this
350
behavior below.
351
352
OUTPUT: function, empty tuple
353
354
EXAMPLES::
355
356
sage: loads(dumps(magma)) is magma
357
True
358
359
Unpickling always gives the default global magma interpreter::
360
361
sage: m1 = Magma(); m2 = Magma()
362
sage: m1 is m2
363
False
364
sage: loads(dumps(m1)) is loads(dumps(m2))
365
True
366
sage: loads(dumps(m1)) is magma
367
True
368
"""
369
return reduce_load_Magma, tuple([])
370
371
def _read_in_file_command(self, filename):
372
"""
373
Return the command in Magma that reads in the contents of the given
374
file.
375
376
INPUT:
377
378
379
- ``filename`` - string
380
381
382
OUTPUT:
383
384
385
- ``string`` - a magma command
386
387
388
EXAMPLES::
389
390
sage: magma._read_in_file_command('file.m')
391
'load "file.m";'
392
"""
393
return 'load "%s";'%filename
394
395
def _post_process_from_file(self, s):
396
r"""
397
Used internally in the Magma interface to post-process the result
398
of evaluating a string using a file. For Magma what this does is
399
delete the first output line, since that is a verbose output line
400
that Magma displays about loading a file.
401
402
INPUT:
403
404
405
- ``s`` - a string
406
407
408
OUTPUT: a string
409
410
EXAMPLES::
411
412
sage: magma._post_process_from_file("Loading ...\nHello")
413
'Hello'
414
sage: magma._post_process_from_file("Hello")
415
''
416
"""
417
if not isinstance(s, str):
418
raise RuntimeError, "Error evaluating object in %s:\n%s"%(self,s)
419
# Chop off the annoying "Loading ... " message that Magma
420
# always outputs no matter what.
421
i = s.find('\n')
422
if i == -1: # special case -- command produced no output, so no \n
423
return ''
424
return s[i+1:]
425
426
def __getattr__(self, attrname):
427
"""
428
Return a formal wrapper around a Magma function, or raise an
429
AttributeError if attrname starts with an underscore.
430
431
INPUT:
432
433
434
- ``attrname`` - a string
435
436
437
OUTPUT: MagmaFunction instance
438
439
EXAMPLES::
440
441
sage: g = magma.__getattr__('EllipticCurve')
442
sage: g
443
EllipticCurve
444
sage: type(g)
445
<class 'sage.interfaces.magma.MagmaFunction'>
446
447
In fact, __getattr__ is called implicitly in the following
448
case::
449
450
sage: f = magma.EllipticCurve
451
sage: type(f)
452
<class 'sage.interfaces.magma.MagmaFunction'>
453
sage: f
454
EllipticCurve
455
"""
456
if attrname[:1] == "_":
457
raise AttributeError
458
return MagmaFunction(self, attrname)
459
460
def chdir(self, dir):
461
"""
462
Change the Magma interpreters current working directory.
463
464
INPUT:
465
466
467
- ``dir`` - a string
468
469
470
EXAMPLES::
471
472
sage: magma.eval('System("pwd")') # random, optional - magma
473
'/Users/was/s/devel/sage-main/sage'
474
sage: magma.chdir('..') # optional - magma
475
sage: magma.eval('System("pwd")') # random, optional - magma
476
'/Users/was/s/devel/sage-main'
477
"""
478
self.eval('ChangeDirectory("%s")'%dir, strip=False)
479
480
def eval(self, x, strip=True, **kwds):
481
"""
482
Evaluate the given block x of code in Magma and return the output
483
as a string.
484
485
INPUT:
486
487
- ``x`` - string of code
488
489
- ``strip`` - ignored
490
491
492
OUTPUT: string
493
494
EXAMPLES:
495
496
We evaluate a string that involves assigning to a
497
variable and printing.
498
499
::
500
501
sage: magma.eval("a := 10;print 2+a;") # optional - magma
502
'12'
503
504
We evaluate a large input line (note that no weird output appears
505
and that this works quickly).
506
507
::
508
509
sage: magma.eval("a := %s;"%(10^10000)) # optional - magma
510
''
511
512
Verify that trac 9705 is fixed::
513
514
sage: nl=chr(10) # newline character
515
sage: magma.eval( # optional - magma
516
... "_<x>:=PolynomialRing(Rationals());"+nl+
517
... "repeat"+nl+
518
... " g:=3*b*x^4+18*c*x^3-6*b^2*x^2-6*b*c*x-b^3-9*c^2 where b:=Random([-10..10]) where c:=Random([-10..10]);"+nl+
519
... "until g ne 0 and Roots(g) ne [];"+nl+
520
... "print \"success\";")
521
'success'
522
523
Verify that trac 11401 is fixed::
524
525
sage: nl=chr(10) # newline character
526
sage: magma.eval("a:=3;"+nl+"b:=5;") == nl # optional - magma
527
True
528
sage: magma.eval("[a,b];") # optional - magma
529
'[ 3, 5 ]'
530
531
"""
532
x = self._preparse(x)
533
x = str(x).rstrip()
534
if len(x) == 0 or x[len(x) - 1] != ';':
535
x += ';'
536
ans = Expect.eval(self, x, **kwds).replace('\\\n','')
537
if 'Runtime error' in ans or 'User error' in ans:
538
raise RuntimeError, "Error evaluating Magma code.\nIN:%s\nOUT:%s"%(x, ans)
539
return ans
540
541
def _preparse(self, s):
542
"""
543
All input gets preparsed by calling this function before it gets evaluated.
544
545
EXAMPLES::
546
547
sage: magma._preparse_colon_equals = False
548
sage: magma._preparse('a = 5')
549
'a = 5'
550
sage: magma._preparse_colon_equals = True
551
sage: magma._preparse('a = 5')
552
'a := 5'
553
sage: magma._preparse('a = 5; b := 7; c =a+b;')
554
'a := 5; b := 7; c :=a+b;'
555
"""
556
try: # this is in a try/except only because of the possibility of old pickled Magma interfaces.
557
if self._preparse_colon_equals:
558
s = s.replace(':=','=').replace('=',':=')
559
except AttributeError: pass
560
return s
561
562
def _start(self):
563
"""
564
Initialize a Magma interface instance. This involves (1) setting up
565
an obfuscated prompt, and (2) attaching the MAGMA_SPEC file (see
566
EXTCODE_DIR/spec file (see sage.interfaces.magma.EXTCODE_DIR/spec).
567
568
EXAMPLES: This is not too exciting::
569
570
sage: magma._start() # optional - magma
571
"""
572
self._change_prompt('>')
573
Expect._start(self)
574
self.eval('SetPrompt("%s"); SetLineEditor(false); SetColumns(0);'%PROMPT)
575
self._change_prompt(PROMPT)
576
self.expect().expect(PROMPT)
577
self.expect().expect(PROMPT)
578
self.expect().expect(PROMPT)
579
self.attach_spec(extcode_dir() + '/spec')
580
581
def set(self, var, value):
582
"""
583
Set the variable var to the given value in the Magma interpreter.
584
585
INPUT:
586
587
588
- ``var`` - string; a variable name
589
590
- ``value`` - string; what to set var equal to
591
592
593
EXAMPLES::
594
595
sage: magma.set('abc', '2 + 3/5') # optional - magma
596
sage: magma('abc') # optional - magma
597
13/5
598
"""
599
out = self.eval("%s:=%s"%(var, value))
600
if out.lower().find("error") != -1:
601
raise TypeError, "Error executing Magma code:\n%s"%out
602
603
def get(self, var):
604
"""
605
Get the value of the variable var.
606
607
INPUT:
608
609
610
- ``var`` - string; name of a variable defined in the
611
Magma session
612
613
614
OUTPUT:
615
616
617
- ``string`` - string representation of the value of
618
the variable.
619
620
621
EXAMPLES::
622
623
sage: magma.set('abc', '2 + 3/5') # optional - magma
624
sage: magma.get('abc') # optional - magma
625
'13/5'
626
"""
627
return self.eval("%s"%var)
628
629
def objgens(self, value, gens):
630
"""
631
Create a new object with given value and gens.
632
633
INPUT:
634
635
636
- ``value`` - something coercible to an element of this Magma
637
interface
638
639
- ``gens`` - string; comma separated list of variable names
640
641
642
OUTPUT: new Magma element that is equal to value with given gens
643
644
EXAMPLES::
645
646
sage: R = magma.objgens('PolynomialRing(Rationals(),2)', 'alpha,beta') # optional - magma
647
sage: R.gens() # optional - magma
648
[alpha, beta]
649
650
Because of how Magma works you can use this to change the variable
651
names of the generators of an object::
652
653
sage: S = magma.objgens(R, 'X,Y') # optional - magma
654
sage: R # optional - magma
655
Polynomial ring of rank 2 over Rational Field
656
Order: Lexicographical
657
Variables: X, Y
658
sage: S # optional - magma
659
Polynomial ring of rank 2 over Rational Field
660
Order: Lexicographical
661
Variables: X, Y
662
"""
663
var = self._next_var_name()
664
value = self(value)
665
out = self.eval("_zsage_<%s> := %s; %s := _zsage_"%(gens, value.name(), var))
666
if out.lower().find("error") != -1:
667
raise TypeError, "Error executing Magma code:\n%s"%out
668
return self(var)
669
670
def __call__(self, x, gens=None):
671
"""
672
Coerce x into this Magma interpreter interface.
673
674
INPUT:
675
676
677
- ``x`` - object
678
679
- ``gens`` - string; names of generators of self,
680
separated by commas
681
682
683
OUTPUT: MagmaElement
684
685
EXAMPLES::
686
687
sage: magma(EllipticCurve('37a')) # optional - magma
688
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
689
sage: magma('EllipticCurve([GF(5)|1,2,3,4,1])') # optional - magma
690
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 1 over GF(5)
691
sage: magma('PowerSeriesRing(Rationals())', 't') # optional - magma
692
Power series ring in t over Rational Field
693
sage: magma('PolynomialRing(RationalField(), 3)', 'x,y,z') # optional - magma
694
Polynomial ring of rank 3 over Rational Field
695
Order: Lexicographical
696
Variables: x, y, z
697
698
We test a coercion between different Magma instances::
699
700
sage: m = Magma()
701
sage: n = Magma()
702
sage: a = n(m(2)) # optional - magma
703
sage: a.parent() is n # optional - magma
704
True
705
sage: a.parent() is m # optional - magma
706
False
707
708
We test caching::
709
710
sage: R.<x> = ZZ[] # optional - magma
711
sage: magma(R) is magma(R) # optional - magma
712
True
713
sage: m = Magma() # optional - magma
714
sage: m(R) # optional - magma
715
Univariate Polynomial Ring in x over Integer Ring
716
sage: m(R) is magma(R) # optional - magma
717
False
718
sage: R._magma_cache # optional - magma
719
{Magma: Univariate Polynomial Ring in x over Integer Ring,
720
Magma: Univariate Polynomial Ring in x over Integer Ring}
721
"""
722
if isinstance(x, bool):
723
return Expect.__call__(self, 'true' if x else 'false')
724
725
if gens is not None: # get rid of this at some point -- it's weird
726
return self.objgens(x, gens)
727
728
# This is mostly about caching the Magma element in the object
729
# itself below. Note that it is *very* important that caching
730
# happen on the object itself, and not in a dictionary that is
731
# held by the Magma interface, since we want garbage collection
732
# of the objects in the Magma interface to work correctly.
733
has_cache = hasattr(x, '_magma_cache')
734
try:
735
if has_cache and x._magma_cache.has_key(self):
736
A = x._magma_cache[self]
737
if A._session_number == self._session_number:
738
return A
739
except AttributeError:
740
# This happens when x has _magma_cache as a cdef public object attribute.
741
x._magma_cache = {}
742
743
try:
744
if self.__cache.has_key(x):
745
A = self.__cache[x]
746
if A._session_number == self._session_number:
747
return A
748
except TypeError: # if x isn't hashable
749
pass
750
751
A = Expect.__call__(self, x)
752
if has_cache:
753
x._magma_cache[self] = A
754
else:
755
try: # use try/except here, because if x is cdef'd we won't be able to set this.
756
x._magma_cache = {self:A}
757
except AttributeError, msg:
758
# Unfortunately, we *have* do have this __cache
759
# attribute, which can lead to "leaks" in the working
760
# Magma session. This is because it is critical that
761
# parent objects get cached, but sometimes they can't
762
# be cached in the object itself, because the object
763
# doesn't have a _magma_cache attribute. So in such
764
# cases when the object is a parent we cache it in
765
# the magma interface.
766
if isinstance(x, Parent):
767
self.__cache[x] = A
768
return A
769
770
771
def _coerce_from_special_method(self, x):
772
"""
773
Tries to coerce to self by calling a special underscore method.
774
775
If no such method is defined, raises an AttributeError instead of a
776
TypeError.
777
778
EXAMPLES::
779
780
sage: magma._coerce_from_special_method(-3/5) # optional - magma
781
-3/5
782
783
Note that AttributeError::
784
785
sage: magma._coerce_from_special_method('2 + 3') # optional - magma
786
Traceback (most recent call last):
787
...
788
AttributeError: 'str' object has no attribute '_magma_init_'
789
"""
790
s = x._magma_init_(self)
791
a = self(s)
792
793
# dereference all _sage_ref's used in this string.
794
while True:
795
z = SAGE_REF_RE.search(s)
796
if not z: break
797
self.eval('delete %s;'%s[z.start():z.end()])
798
s = s[z.end()+1:]
799
return a
800
801
def _with_names(self, s, names):
802
"""
803
Return s but wrapped by a call to SageCreateWithNames. This is just
804
a very simple convenience function so that code is cleaner.
805
806
INPUT:
807
808
809
- ``s`` - string
810
811
- ``names`` - list of strings
812
813
814
OUTPUT: string
815
816
EXAMPLES::
817
818
sage: magma._with_names('PolynomialRing(RationalField())', ['y']) # optional - magma
819
'SageCreateWithNames(PolynomialRing(RationalField()),["y"])'
820
"""
821
return 'SageCreateWithNames(%s,[%s])'%(s, ','.join('"%s"'%x for x in names))
822
823
def clear(self, var):
824
"""
825
Clear the variable named var and make it available to be used
826
again.
827
828
INPUT:
829
830
831
- ``var`` - a string
832
833
834
EXAMPLES::
835
836
sage: magma = Magma() # optional - magma
837
sage: magma.clear('foo') # sets foo to 0 in magma; optional - magma
838
sage: magma.eval('foo') # optional - magma
839
'0'
840
841
Because we cleared foo, it is set to be used as a variable name in
842
the future::
843
844
sage: a = magma('10') # optional - magma
845
sage: a.name() # optional - magma
846
'foo'
847
848
The following tests that the whole variable clearing and freeing
849
system is working correctly.
850
851
::
852
853
sage: magma = Magma() # optional - magma
854
sage: a = magma('100') # optional - magma
855
sage: a.name() # optional - magma
856
'_sage_[1]'
857
sage: del a # optional - magma
858
sage: b = magma('257') # optional - magma
859
sage: b.name() # optional - magma
860
'_sage_[1]'
861
sage: del b # optional - magma
862
sage: magma('_sage_[1]') # optional - magma
863
0
864
"""
865
self.__available_var.insert(0,var) # adds var to front of list
866
self.eval("%s:=0"%var)
867
868
def cputime(self, t=None):
869
"""
870
Return the CPU time in seconds that has elapsed since this Magma
871
session started. This is a floating point number, computed by
872
Magma.
873
874
If t is given, then instead return the floating point time from
875
when t seconds had elapsed. This is useful for computing elapsed
876
times between two points in a running program.
877
878
INPUT:
879
880
881
- ``t`` - float (default: None); if not None, return
882
cputime since t
883
884
885
OUTPUT:
886
887
888
- ``float`` - seconds
889
890
891
EXAMPLES::
892
893
sage: type(magma.cputime()) # optional - magma
894
<type 'float'>
895
sage: magma.cputime() # random, optional - magma
896
1.9399999999999999
897
sage: t = magma.cputime() # optional - magma
898
sage: magma.cputime(t) # random, optional - magma
899
0.02
900
"""
901
if t:
902
return float(self.eval('Cputime(%s)'%t))
903
else:
904
return float(self.eval('Cputime()'))
905
906
def chdir(self, dir):
907
"""
908
Change to the given directory.
909
910
INPUT:
911
912
913
- ``dir`` - string; name of a directory
914
915
916
EXAMPLES::
917
918
sage: magma.chdir('/') # optional - magma
919
sage: magma.eval('System("pwd")') # optional - magma
920
'/'
921
"""
922
magma.eval('ChangeDirectory("%s")'%dir)
923
924
def attach(self, filename):
925
r"""
926
Attach the given file to the running instance of Magma.
927
928
Attaching a file in Magma makes all intrinsics defined in the file
929
available to the shell. Moreover, if the file doesn't start with
930
the ``freeze;`` command, then the file is reloaded
931
whenever it is changed. Note that functions and procedures defined
932
in the file are *not* available. For only those, use
933
``magma.load(filename)``.
934
935
INPUT:
936
937
938
- ``filename`` - a string
939
940
941
EXAMPLES: Attaching a file that exists is fine::
942
943
sage: magma.attach('%s/data/extcode/magma/sage/basic.m'%SAGE_ROOT) # optional - magma
944
945
Attaching a file that doesn't exist raises an exception::
946
947
sage: magma.attach('%s/data/extcode/magma/sage/basic2.m'%SAGE_ROOT) # optional - magma
948
Traceback (most recent call last):
949
...
950
RuntimeError: Error evaluating Magma code...
951
"""
952
self.eval('Attach("%s")'%filename)
953
954
Attach = attach
955
956
def attach_spec(self, filename):
957
r"""
958
Attach the given spec file to the running instance of Magma.
959
960
This can attach numerous other files to the running Magma (see the
961
Magma documentation for more details).
962
963
INPUT:
964
965
966
- ``filename`` - a string
967
968
969
EXAMPLES::
970
971
sage: magma.attach_spec('%s/data/extcode/magma/spec'%SAGE_ROOT) # optional - magma
972
sage: magma.attach_spec('%s/data/extcode/magma/spec2'%SAGE_ROOT) # optional - magma
973
Traceback (most recent call last):
974
...
975
RuntimeError: Can't open package spec file .../data/extcode/magma/spec2 for reading (No such file or directory)
976
"""
977
s = self.eval('AttachSpec("%s")'%filename)
978
if s:
979
raise RuntimeError, s.strip()
980
981
AttachSpec = attach_spec
982
983
def load(self, filename):
984
r"""
985
Load the file with given filename using the 'load' command in the
986
Magma shell.
987
988
Loading a file in Magma makes all the functions and procedures in
989
the file available. The file should not contain any intrinsics (or
990
you'll get errors). It also runs code in the file, which can
991
produce output.
992
993
INPUT:
994
995
996
- ``filename`` - string
997
998
999
OUTPUT: output printed when loading the file
1000
1001
EXAMPLES::
1002
1003
sage: open(SAGE_TMP + 'a.m','w').write('function f(n) return n^2; end function;\nprint "hi";')
1004
sage: print magma.load(SAGE_TMP + 'a.m') # optional - magma
1005
Loading ".../.sage//temp/.../a.m"
1006
hi
1007
sage: magma('f(12)') # optional - magma
1008
144
1009
"""
1010
return self.eval('load "%s"'%filename)
1011
1012
def _next_var_name(self):
1013
"""
1014
Return the next available variable name in Magma.
1015
1016
OUTPUT: string
1017
1018
EXAMPLES::
1019
1020
sage: m = Magma()
1021
sage: m._next_var_name() # optional - magma
1022
'_sage_[1]'
1023
sage: m._next_var_name() # optional - magma
1024
'_sage_[2]'
1025
sage: a = m(3/8); a # optional - magma
1026
3/8
1027
sage: a.name() # optional - magma
1028
'_sage_[3]'
1029
sage: m._next_var_name() # optional - magma
1030
'_sage_[4]'
1031
"""
1032
if self.__seq == 0:
1033
self.eval('_sage_ := [* *];')
1034
else:
1035
try:
1036
self.eval('Append(~_sage_, 0);')
1037
except:
1038
# this exception could happen if the Magma process
1039
# was interrupted during startup / initialization.
1040
self.eval('_sage_ := [* 0 : i in [1..%s] *];'%self.__seq)
1041
try:
1042
return self.__available_var.pop()
1043
except IndexError:
1044
self.__seq += 1
1045
return '_sage_[%s]'%self.__seq
1046
1047
def _next_ref_name(self):
1048
"""
1049
Return the next reference name. This is used internally to deal
1050
with Magma objects that would be deallocated before they are used
1051
in constructing another object.
1052
1053
OUTPUT: string
1054
1055
EXAMPLES::
1056
1057
sage: magma._next_ref_name()
1058
'_sage_ref...'
1059
"""
1060
self.__ref += 1
1061
return '%s%s'%(SAGE_REF, self.__ref)
1062
1063
def function_call(self, function, args=[], params={}, nvals=1):
1064
"""
1065
Return result of evaluating a Magma function with given input,
1066
parameters, and asking for nvals as output.
1067
1068
INPUT:
1069
1070
1071
- ``function`` - string, a Magma function name
1072
1073
- ``args`` - list of objects coercible into this magma
1074
interface
1075
1076
- ``params`` - Magma parameters, passed in after a
1077
colon
1078
1079
- ``nvals`` - number of return values from the
1080
function to ask Magma for
1081
1082
1083
OUTPUT: MagmaElement or tuple of nvals MagmaElement's
1084
1085
EXAMPLES::
1086
1087
sage: magma.function_call('Factorization', 100) # optional - magma
1088
[ <2, 2>, <5, 2> ]
1089
sage: magma.function_call('NextPrime', 100, {'Proof':False}) # optional - magma
1090
101
1091
sage: magma.function_call('PolynomialRing', [QQ,2]) # optional - magma
1092
Polynomial ring of rank 2 over Rational Field
1093
Order: Lexicographical
1094
Variables: $.1, $.2
1095
1096
Next, we illustrate multiple return values::
1097
1098
sage: magma.function_call('IsSquare', 100) # optional - magma
1099
true
1100
sage: magma.function_call('IsSquare', 100, nvals=2) # optional - magma
1101
(true, 10)
1102
sage: magma.function_call('IsSquare', 100, nvals=3) # optional - magma
1103
Traceback (most recent call last):
1104
...
1105
RuntimeError: Error evaluating Magma code...
1106
Runtime error in :=: Expected to assign 3 value(s) but only computed 2 value(s)
1107
"""
1108
args, params = self._convert_args_kwds(args, params)
1109
nvals = int(nvals)
1110
if len(params) == 0:
1111
par = ''
1112
else:
1113
par = ' : ' + ','.join(['%s:=%s'%(a,b.name()) for a,b in params.items()])
1114
1115
fun = "%s(%s%s)"%(function, ",".join([s.name() for s in args]), par)
1116
1117
return self._do_call(fun, nvals)
1118
1119
def _do_call(self, code, nvals):
1120
"""
1121
Evaluate the given code expression assuming that it outputs nvals
1122
distinct values. Return the resulting values as a tuple if nvals =
1123
2.
1124
1125
INPUT:
1126
1127
1128
- ``code`` - a string; code to evaluate
1129
1130
- ``nvals`` - an integer; number of return values
1131
1132
1133
OUTPUT: nvals distinct values
1134
1135
EXAMPLES::
1136
1137
sage: magma._do_call('SetVerbose("Groebner",2)', 0) # optional - magma
1138
sage: magma._do_call('Factorization(-5)', 1) # optional - magma
1139
[ <5, 1> ]
1140
1141
Here we get two outputs, as a tuple.
1142
1143
::
1144
1145
sage: magma._do_call('Factorization(-5)', 2) # optional - magma
1146
([ <5, 1> ], -1)
1147
1148
You can also do this::
1149
1150
sage: F, sign = magma._do_call('Factorization(-5)', 2) # optional - magma
1151
sage: F # optional - magma
1152
[ <5, 1> ]
1153
sage: sign # optional - magma
1154
-1
1155
1156
An expression that has one value.
1157
1158
::
1159
1160
sage: magma._do_call('3^5', 1) # optional - magma
1161
243
1162
"""
1163
if nvals <= 0:
1164
out = self.eval(code)
1165
ans = None
1166
elif nvals == 1:
1167
return self(code)
1168
else:
1169
v = [self._next_var_name() for _ in range(nvals)]
1170
vars = ", ".join(v)
1171
cmd = "%s := %s;"%(vars, code)
1172
out = self.eval(cmd)
1173
ans = tuple([MagmaElement(self, x, is_name = True) for x in v])
1174
1175
if out.lower().find("error") != -1:
1176
raise TypeError, "Error executing Magma code:\n%s"%out
1177
return ans
1178
1179
def bar_call(self, left, name, gens, nvals=1):
1180
"""
1181
This is a wrapper around the Magma constructor
1182
1183
nameleft gens
1184
1185
returning nvals.
1186
1187
INPUT:
1188
1189
1190
- ``left`` - something coerceable to a magma object
1191
1192
- ``name`` - name of the constructor, e.g., sub, quo,
1193
ideal, etc.
1194
1195
- ``gens`` - if a list/tuple, each item is coerced to
1196
magma; otherwise gens itself is converted to magma
1197
1198
- ``nvals`` - positive integer; number of return
1199
values
1200
1201
1202
OUTPUT: a single magma object if nvals == 1; otherwise a tuple of
1203
nvals magma objects.
1204
1205
EXAMPLES: The bar_call function is used by the sub, quo, and ideal
1206
methods of Magma elements. Here we illustrate directly using
1207
bar_call to create quotients::
1208
1209
sage: V = magma.RModule(ZZ,3) # optional - magma
1210
sage: V # optional - magma
1211
RModule(IntegerRing(), 3)
1212
sage: magma.bar_call(V, 'quo', [[1,2,3]], nvals=1) # optional - magma
1213
RModule(IntegerRing(), 2)
1214
sage: magma.bar_call(V, 'quo', [[1,2,3]], nvals=2) # optional - magma
1215
(RModule(IntegerRing(), 2),
1216
Mapping from: RModule(IntegerRing(), 3) to RModule(IntegerRing(), 2))
1217
sage: magma.bar_call(V, 'quo', V, nvals=2) # optional - magma
1218
(RModule(IntegerRing(), 0),
1219
Mapping from: RModule(IntegerRing(), 3) to RModule(IntegerRing(), 0))
1220
"""
1221
magma = self
1222
# coerce each arg to be a Magma element
1223
if isinstance(gens, (list, tuple)):
1224
gens = [magma(z) for z in gens]
1225
# make comma separated list of names (in Magma) of each of the gens
1226
v = ', '.join([w.name() for w in gens])
1227
else:
1228
gens = magma(gens)
1229
v = gens.name()
1230
# construct the string that evaluates in Magma to define the subobject,
1231
# and return it evaluated in Magma.
1232
s = '%s< %s | %s >'%(name, left.name(), v)
1233
return self._do_call(s, nvals)
1234
1235
def _object_class(self):
1236
"""
1237
Return the Python class of elements of the Magma interface.
1238
1239
OUTPUT: a Python class
1240
1241
EXAMPLES::
1242
1243
sage: magma._object_class()
1244
<class 'sage.interfaces.magma.MagmaElement'>
1245
"""
1246
return MagmaElement
1247
1248
# Usually "Sequences" are what you want in Magma, not "lists".
1249
# It's very painful using the interface without this.
1250
def _left_list_delim(self):
1251
"""
1252
Return the left sequence delimiter in Magma. Despite the name in
1253
this function, this is really the least painful choice.
1254
1255
EXAMPLES::
1256
1257
sage: magma._left_list_delim()
1258
'['
1259
"""
1260
#return "[*"
1261
return "["
1262
1263
def _right_list_delim(self):
1264
"""
1265
Return the right sequence delimiter in Magma. Despite the name in
1266
this function, this is really the least painful choice.
1267
1268
EXAMPLES::
1269
1270
sage: magma._right_list_delim()
1271
']'
1272
"""
1273
#return "*]"
1274
return "]"
1275
1276
def _assign_symbol(self):
1277
"""
1278
Returns the assignment symbol in Magma.
1279
1280
EXAMPLES::
1281
1282
sage: magma._assign_symbol()
1283
':='
1284
"""
1285
return ":="
1286
1287
def _equality_symbol(self):
1288
"""
1289
Returns the equality testing logical symbol in Magma.
1290
1291
EXAMPLES::
1292
1293
sage: magma._equality_symbol()
1294
'eq'
1295
"""
1296
return 'eq'
1297
1298
def _lessthan_symbol(self):
1299
"""
1300
Returns the less than testing logical symbol in Magma.
1301
1302
EXAMPLES::
1303
1304
sage: magma._lessthan_symbol()
1305
' lt '
1306
"""
1307
return ' lt '
1308
1309
def _greaterthan_symbol(self):
1310
"""
1311
Returns the greater than testing logical symbol in Magma.
1312
1313
EXAMPLES::
1314
1315
sage: magma._greaterthan_symbol()
1316
' gt '
1317
"""
1318
return ' gt '
1319
1320
# For efficiency purposes, you should definitely override these
1321
# in your derived class.
1322
def _true_symbol(self):
1323
"""
1324
Returns the string representation of "truth" in Magma.
1325
1326
EXAMPLES::
1327
1328
sage: magma._true_symbol()
1329
'true'
1330
"""
1331
return 'true'
1332
1333
def _false_symbol(self):
1334
"""
1335
Returns the string representation of "false" in Magma.
1336
1337
EXAMPLES::
1338
1339
sage: magma._false_symbol()
1340
'false'
1341
"""
1342
return 'false'
1343
1344
def console(self):
1345
"""
1346
Run a command line Magma session. This session is completely
1347
separate from this Magma interface.
1348
1349
EXAMPLES::
1350
1351
sage: magma.console() # not tested
1352
Magma V2.14-9 Sat Oct 11 2008 06:36:41 on one [Seed = 1157408761]
1353
Type ? for help. Type <Ctrl>-D to quit.
1354
>
1355
Total time: 2.820 seconds, Total memory usage: 3.95MB
1356
"""
1357
magma_console()
1358
1359
def version(self):
1360
"""
1361
Return the version of Magma that you have in your PATH on your
1362
computer.
1363
1364
OUTPUT:
1365
1366
1367
- ``numbers`` - 3-tuple: major, minor, etc.
1368
1369
- ``string`` - version as a string
1370
1371
1372
EXAMPLES::
1373
1374
sage: magma.version() # random, optional - magma
1375
((2, 14, 9), 'V2.14-9')
1376
"""
1377
return magma_version()
1378
1379
def help(self, s):
1380
"""
1381
Return Magma help on string s.
1382
1383
This returns what typing ?s would return in Magma.
1384
1385
INPUT:
1386
1387
1388
- ``s`` - string
1389
1390
1391
OUTPUT: string
1392
1393
EXAMPLES::
1394
1395
sage: magma.help("NextPrime") # optional - magma
1396
===============================================================================
1397
PATH: /magma/ring-field-algebra/integer/prime/next-previous/NextPrime
1398
KIND: Intrinsic
1399
===============================================================================
1400
NextPrime(n) : RngIntElt -> RngIntElt
1401
NextPrime(n: parameter) : RngIntElt -> RngIntElt
1402
...
1403
"""
1404
print self.eval('? %s'%s)
1405
1406
def trait_names(self, verbose=True, use_disk_cache=True):
1407
"""
1408
Return a list of all Magma commands.
1409
1410
This is used as a hook to enable custom command completion.
1411
1412
Magma doesn't provide any fast way to make a list of all commands,
1413
which is why caching is done by default. Note that an adverse
1414
impact of caching is that *new* commands are not picked up, e.g.,
1415
user defined variables or functions.
1416
1417
INPUT:
1418
1419
1420
- ``verbose`` - bool (default: True); whether to
1421
verbosely output status info the first time the command list is
1422
built
1423
1424
- ``use_disk_cache`` - bool (default: True); use
1425
cached command list, which is saved to disk.
1426
1427
1428
OUTPUT: list of strings
1429
1430
EXAMPLES::
1431
1432
sage: len(magma.trait_names(verbose=False)) # random, optional - magma
1433
7261
1434
"""
1435
try:
1436
return self.__trait_names
1437
except AttributeError:
1438
import sage.misc.persist
1439
if use_disk_cache:
1440
try:
1441
self.__trait_names = sage.misc.persist.load(INTRINSIC_CACHE)
1442
return self.__trait_names
1443
except IOError:
1444
pass
1445
if verbose:
1446
print "\nCreating list of all Magma intrinsics for use in tab completion."
1447
print "This takes a few minutes the first time, but is saved to the"
1448
print "file '%s' for future instant use."%INTRINSIC_CACHE
1449
print "Magma may produce errors during this process, which are safe to ignore."
1450
print "Delete that file to force recreation of this cache."
1451
print "Scanning Magma types ..."
1452
tm = sage.misc.misc.cputime()
1453
T = self.eval('ListTypes()').split()
1454
N = []
1455
for t in T:
1456
if verbose:
1457
print t, " ",
1458
sys.stdout.flush()
1459
try:
1460
s = self.eval('ListSignatures(%s)'%t)
1461
for x in s.split('\n'):
1462
i = x.find('(')
1463
N.append(x[:i])
1464
except RuntimeError, msg: # weird internal problems in Magma type system
1465
print 'Error -- %s'%msg
1466
pass
1467
if verbose:
1468
print "Done! (%s seconds)"%sage.misc.misc.cputime(tm)
1469
N = list(set(N))
1470
N.sort()
1471
print "Saving cache to '%s' for future instant use."%INTRINSIC_CACHE
1472
print "Delete the above file to force re-creation of the cache."
1473
sage.misc.persist.save(N, INTRINSIC_CACHE)
1474
self.__trait_names = N
1475
return N
1476
1477
def ideal(self, L):
1478
"""
1479
Return the Magma ideal defined by L.
1480
1481
INPUT:
1482
1483
1484
- ``L`` - a list of elements of a Sage multivariate
1485
polynomial ring.
1486
1487
1488
OUTPUT: The magma ideal generated by the elements of L.
1489
1490
EXAMPLES::
1491
1492
sage: R.<x,y> = QQ[]
1493
sage: magma.ideal([x^2, y^3*x]) # optional - magma
1494
Ideal of Polynomial ring of rank 2 over Rational Field
1495
Order: Graded Reverse Lexicographical
1496
Variables: x, y
1497
Homogeneous
1498
Basis:
1499
[
1500
x^2,
1501
x*y^3
1502
]
1503
"""
1504
P = iter(L).next().parent()
1505
Pn = self(P).name()
1506
k = P.base_ring()
1507
if k.degree() > 1:
1508
i = str(k.gen())
1509
o = self("BaseRing(%s).1"%Pn).name()
1510
self.eval("%s := %s"%(i,o))
1511
mlist = self(L)
1512
return self("ideal<%s|%s>"%(Pn,mlist.name()))
1513
1514
def set_verbose(self, type, level):
1515
"""
1516
Set the verbosity level for a given algorithm, class, etc. in
1517
Magma.
1518
1519
INPUT:
1520
1521
1522
- ``type`` - string (e.g. 'Groebner')
1523
1524
- ``level`` - integer = 0
1525
1526
1527
EXAMPLES::
1528
1529
sage: magma.set_verbose("Groebner", 2) # optional - magma
1530
sage: magma.get_verbose("Groebner") # optional - magma
1531
2
1532
"""
1533
self.SetVerbose(type,level)
1534
1535
def SetVerbose(self, type, level):
1536
"""
1537
Set the verbosity level for a given algorithm class etc. in Magma.
1538
1539
INPUT:
1540
1541
1542
- ``type`` - string (e.g. 'Groebner'), see Magma
1543
documentation
1544
1545
- ``level`` - integer = 0
1546
1547
1548
.. note::
1549
1550
This method is provided to be consistent with the Magma
1551
naming convention.
1552
1553
::
1554
1555
sage: magma.SetVerbose("Groebner", 2) # optional - magma
1556
sage: magma.GetVerbose("Groebner") # optional - magma
1557
2
1558
"""
1559
if level < 0:
1560
raise TypeError, "level must be >= 0"
1561
self.eval('SetVerbose("%s",%d)'%(type,level))
1562
1563
def get_verbose(self, type):
1564
"""
1565
Get the verbosity level of a given algorithm class etc. in Magma.
1566
1567
INPUT:
1568
1569
1570
- ``type`` - string (e.g. 'Groebner'), see Magma
1571
documentation
1572
1573
1574
EXAMPLES::
1575
1576
sage: magma.set_verbose("Groebner", 2) # optional - magma
1577
sage: magma.get_verbose("Groebner") # optional - magma
1578
2
1579
"""
1580
return self.GetVerbose(type)
1581
1582
def GetVerbose(self, type):
1583
"""
1584
Get the verbosity level of a given algorithm class etc. in Magma.
1585
1586
INPUT:
1587
1588
1589
- ``type`` - string (e.g. 'Groebner'), see Magma
1590
documentation
1591
1592
1593
.. note::
1594
1595
This method is provided to be consistent with the Magma
1596
naming convention.
1597
1598
EXAMPLES::
1599
1600
sage: magma.SetVerbose("Groebner", 2) # optional - magma
1601
sage: magma.GetVerbose("Groebner") # optional - magma
1602
2
1603
"""
1604
return int(self.eval('GetVerbose("%s")'%type))
1605
1606
class MagmaFunctionElement(FunctionElement):
1607
def __call__(self, *args, **kwds):
1608
"""
1609
Return the result of calling this Magma function at given inputs.
1610
1611
Use the optional nvals keyword argument to specify that there are
1612
multiple return values.
1613
1614
EXAMPLES: We create a MagmaFunctionElement::
1615
1616
sage: n = magma(-15) # optional - magma
1617
sage: f = n.Factorisation # optional - magma
1618
sage: type(f) # optional - magma
1619
<class 'sage.interfaces.magma.MagmaFunctionElement'>
1620
sage: f() # optional - magma
1621
[ <3, 1>, <5, 1> ]
1622
1623
We verify that the nvals argument works.
1624
1625
::
1626
1627
sage: f(nvals=2) # optional - magma
1628
([ <3, 1>, <5, 1> ], -1)
1629
1630
This illustrates the more conventional way of calling a method on
1631
an object. It's equivalent to the above, but done in all in one
1632
step.
1633
1634
::
1635
1636
sage: n.Factorization(nvals = 2) # optional - magma
1637
([ <3, 1>, <5, 1> ], -1)
1638
"""
1639
nvals = 1
1640
if len(kwds) > 0:
1641
if kwds.has_key('nvals'):
1642
nvals = kwds['nvals']
1643
del kwds['nvals']
1644
M = self._obj.parent()
1645
return M.function_call(self._name,
1646
[self._obj.name()] + list(args),
1647
params = kwds,
1648
nvals = nvals)
1649
1650
def _sage_doc_(self):
1651
"""
1652
Return the docstring for this function of an element.
1653
1654
OUTPUT: string
1655
1656
EXAMPLES::
1657
1658
sage: n = magma(-15) # optional - magma
1659
sage: f = n.Factorisation # optional - magma
1660
sage: print f._sage_doc_() # optional - magma
1661
(<RngIntElt> n) -> RngIntEltFact, RngIntElt, SeqEnum
1662
...
1663
sage: print n.Factorisation._sage_doc_() # optional - magma
1664
(<RngIntElt> n) -> RngIntEltFact, RngIntElt, SeqEnum
1665
...
1666
"""
1667
M = self._obj.parent()
1668
t = str(self._obj.Type())
1669
s = M.eval(self._name)
1670
Z = s.split('(<')[1:]
1671
W = []
1672
tt = '(<%s'%t
1673
for X in Z:
1674
X = '(<' + X
1675
if '(<All>' in X or tt in X:
1676
W.append(X)
1677
s = '\n'.join(W)
1678
s = sage.misc.misc.word_wrap(s)
1679
return s
1680
1681
def __repr__(self):
1682
"""
1683
Return string representation of this partially evaluated function.
1684
1685
This is basically the docstring (as returned by _sage_doc_)
1686
unless self._name is the name of an attribute of the object, in
1687
which case this returns the value of that attribute.
1688
1689
EXAMPLES::
1690
1691
sage: magma(-15).Factorisation # optional - magma
1692
Partially evaluated Magma function or intrinsic 'Factorisation'
1693
...
1694
1695
We create a vector space, set its M attribute to a number, then
1696
display/get the attribute as a string.
1697
1698
::
1699
1700
sage: V = magma('VectorSpace(RationalField(),2)') # optional - magma
1701
sage: V.set_magma_attribute('M', 290398) # optional - magma
1702
sage: V.M # optional - magma
1703
290398
1704
sage: type(V.M) # optional - magma
1705
<class 'sage.interfaces.magma.MagmaFunctionElement'>
1706
sage: type(V.M.__repr__()) # optional - magma
1707
<type 'str'>
1708
1709
Displaying a non-attribute function works as above.
1710
1711
::
1712
1713
sage: V.Dimension # optional - magma
1714
Partially evaluated Magma function or intrinsic 'Dimension'
1715
...
1716
"""
1717
M = self._obj.parent()
1718
try:
1719
return M.eval('%s`%s'%(self._obj.name(), self._name))
1720
except RuntimeError:
1721
return "Partially evaluated Magma function or intrinsic '%s'\n\nSignature:\n\n%s"%(
1722
self._name, self._sage_doc_())
1723
1724
1725
class MagmaFunction(ExpectFunction):
1726
def __call__(self, *args, **kwds):
1727
"""
1728
Return the result of calling this Magma function at given inputs.
1729
1730
Use the optional nvals keyword argument to specify that there are
1731
multiple return values.
1732
1733
EXAMPLES: We create a MagmaFunction::
1734
1735
sage: f = magma.Factorisation # optional - magma
1736
sage: type(f) # optional - magma
1737
<class 'sage.interfaces.magma.MagmaFunction'>
1738
sage: f(-15) # optional - magma
1739
[ <3, 1>, <5, 1> ]
1740
1741
We verify that the nvals argument works.
1742
1743
::
1744
1745
sage: f(-15, nvals=2) # optional - magma
1746
([ <3, 1>, <5, 1> ], -1)
1747
sage: f.__call__(-15, nvals=2) # optional - magma
1748
([ <3, 1>, <5, 1> ], -1)
1749
"""
1750
nvals = 1
1751
if len(kwds) > 0:
1752
if kwds.has_key('nvals'):
1753
nvals = kwds['nvals']
1754
del kwds['nvals']
1755
M = self._parent
1756
return M.function_call(self._name,
1757
list(args),
1758
params = kwds,
1759
nvals = nvals)
1760
def _sage_doc_(self):
1761
"""
1762
Return docstring about this function.
1763
1764
OUTPUT: string
1765
1766
EXAMPLES::
1767
1768
sage: f = magma.Factorisation
1769
sage: type(f)
1770
<class 'sage.interfaces.magma.MagmaFunction'>
1771
sage: print f._sage_doc_() # optional - magma
1772
Intrinsic 'Factorisation'
1773
...
1774
"""
1775
M = self._parent
1776
s = M.eval(self._name)
1777
s = sage.misc.misc.word_wrap(s, 80)
1778
return s
1779
1780
1781
def is_MagmaElement(x):
1782
"""
1783
Return True if x is of type MagmaElement, and False otherwise.
1784
1785
INPUT:
1786
1787
1788
- ``x`` - any object
1789
1790
1791
OUTPUT: bool
1792
1793
EXAMPLES::
1794
1795
sage: from sage.interfaces.magma import is_MagmaElement
1796
sage: is_MagmaElement(2)
1797
False
1798
sage: is_MagmaElement(magma(2)) # optional - magma
1799
True
1800
"""
1801
return isinstance(x, MagmaElement)
1802
1803
class MagmaElement(ExpectElement):
1804
def _ref(self):
1805
"""
1806
Return a variable name that is a new reference to this particular
1807
MagmaElement in Magma. This keeps this object from being garbage
1808
collected by Magma, even if all the Sage references to it are
1809
freed.
1810
1811
Important special behavior: When _ref is used during an implicit
1812
call to _magma_init_, then the reference disappears after the
1813
coercion is done. More precisely, if the output of _ref() appears
1814
as part of the output of a call to _magma_init_ that is then
1815
going to be input to magma(...), then it is deleted in the Magma
1816
interface. The main use for this behavior is that in
1817
_magma_init_ it allows you to get a reference to one object, and
1818
use it exactly once in constructing a string to evaluate in Magma,
1819
without having to worry about that object being deallocated. There
1820
are more sophisticated ways that the same problem (with
1821
_magma_init_ and references) could have been solved, but this
1822
solution is much simpler and easier to understand than all others I
1823
came up with. If this doesn't make sense, read the source code to
1824
_coerce_from_special_method, which is much shorter than this
1825
paragraph.
1826
1827
.. warning::
1828
1829
Use _ref sparingly, since it involves a full call to Magma,
1830
which can be slow.
1831
1832
OUTPUT: string
1833
1834
EXAMPLES::
1835
1836
sage: a = magma('-2/3') # optional - magma
1837
sage: s = a._ref(); s # optional - magma
1838
'_sage_ref...'
1839
sage: magma(s) # optional - magma
1840
-2/3
1841
"""
1842
P = self._check_valid()
1843
n = P._next_ref_name()
1844
P.set(n, self.name())
1845
return n
1846
1847
def __getattr__(self, attrname):
1848
"""
1849
INPUT:
1850
1851
1852
- ``attrname`` - string
1853
1854
1855
OUTPUT: a Magma function partially evaluated with self as the first
1856
input.
1857
1858
.. note::
1859
1860
If the input ``attrname`` starts with an underscore, an
1861
AttributeError is raised so that the actual Python _ method/value
1862
can be accessed.
1863
1864
EXAMPLES::
1865
1866
sage: n = magma(-15) # optional - magma
1867
sage: type(n) # optional - magma
1868
<class 'sage.interfaces.magma.MagmaElement'>
1869
sage: f = n.__getattr__('Factorization') # optional - magma
1870
sage: type(f) # optional - magma
1871
<class 'sage.interfaces.magma.MagmaFunctionElement'>
1872
sage: f # optional - magma
1873
Partially evaluated Magma function or intrinsic 'Factorization'
1874
...
1875
"""
1876
if attrname[:1] == "_":
1877
raise AttributeError
1878
return MagmaFunctionElement(self, attrname)
1879
1880
def _sage_(self):
1881
"""
1882
Return Sage version of this object. Use self.sage() to get the Sage
1883
version.
1884
1885
Edit extcode/magma/sage.m to add functionality.
1886
1887
EXAMPLES: Enumerated Sets::
1888
1889
sage: a = magma('{1,2/3,-5/9}') # optional - magma
1890
sage: a.sage() # optional - magma
1891
{1, -5/9, 2/3}
1892
sage: a._sage_() # optional - magma
1893
{1, -5/9, 2/3}
1894
sage: type(a.sage()) # optional - magma
1895
<class 'sage.sets.set.Set_object_enumerated_with_category'>
1896
sage: a = magma('{1,2/3,-5/9}'); a # optional - magma
1897
{ -5/9, 2/3, 1 }
1898
sage: a.Type() # optional - magma
1899
SetEnum
1900
sage: b = a.sage(); b # optional - magma
1901
{1, -5/9, 2/3}
1902
sage: type(b) # optional - magma
1903
<class 'sage.sets.set.Set_object_enumerated_with_category'>
1904
sage: c = magma(b); c # optional - magma
1905
{ -5/9, 2/3, 1 }
1906
sage: c.Type() # optional - magma
1907
SetEnum
1908
1909
Multisets are converted to lists::
1910
1911
sage: m = magma('{* 1,2,2,2,4^^2,3 *}') # optional - magma
1912
sage: z = m.sage(); z # optional - magma
1913
[1, 2, 2, 2, 3, 4, 4]
1914
sage: type(z) # optional - magma
1915
<type 'list'>
1916
1917
Tuples get converted to tuples::
1918
1919
sage: m = magma('<1,2,<3>>') # optional - magma
1920
sage: z = m.sage(); z # optional - magma
1921
(1, 2, (3,))
1922
sage: type(z) # optional - magma
1923
<type 'tuple'>
1924
1925
Sequences get converted to lists::
1926
1927
sage: m = magma('[<1>,<2>]') # optional - magma
1928
sage: z = m.sage(); z # optional - magma
1929
[(1,), (2,)]
1930
sage: type(z) # optional - magma
1931
<type 'list'>
1932
1933
Matrices::
1934
1935
sage: a = matrix(ZZ,3,3,[1..9])
1936
sage: m = magma(a) # optional - magma
1937
sage: b = m.sage(); b # optional - magma
1938
[1 2 3]
1939
[4 5 6]
1940
[7 8 9]
1941
sage: b == a # optional - magma
1942
True
1943
1944
A nonsquare matrix::
1945
1946
sage: a = matrix(ZZ,2,3,[1..6])
1947
sage: m = magma(a) # optional - magma
1948
sage: m.sage() # optional - magma
1949
[1 2 3]
1950
[4 5 6]
1951
1952
Multivariate polynomials::
1953
1954
sage: R.<x,y,z> = QQ[] # optional - magma
1955
sage: f = x^2+3*y # optional - magma
1956
sage: g = magma(f).sage(); g # optional - magma
1957
x^2 + 3*y
1958
sage: parent(f) == parent(g) # optional - magma
1959
True
1960
1961
Number fields and their elements::
1962
1963
sage: x = var('x')
1964
sage: L.<alpha> = NumberField(x^3+2*x+2)
1965
sage: K = magma(L) # optional - magma
1966
sage: K.sage() # optional - magma
1967
Number Field in alpha with defining polynomial x^3 + 2*x + 2
1968
sage: K.sage() is L # optional - magma
1969
True
1970
sage: magma(alpha).sage() # optional - magma
1971
alpha
1972
1973
Relative number field elements can be converted from Magma
1974
to Sage, but the other direction has not yet been implemented.::
1975
1976
sage: P.<y> = L[]
1977
sage: N.<b> = NumberField(y^2-alpha)
1978
sage: M = magma(N) # optional - magma
1979
sage: M.1.sage() # optional - magma
1980
b
1981
sage: _^2 # optional - magma
1982
alpha
1983
sage: magma(b) # optional - magma
1984
Traceback (most recent call last):
1985
...
1986
TypeError: coercion of relative number field elements to Magma is not implemented
1987
1988
Sage does not have absolute number fields defined by
1989
two polynomials, like Magma does. They are converted
1990
to relative number fields. Conversion of their elements
1991
has not yet been implemented.::
1992
1993
sage: magma.eval('P<x> := PolynomialRing(Rationals());') # optional - magma
1994
''
1995
sage: K = magma('NumberField([x^2-2,x^2-3]:Abs);') # optional - magma
1996
sage: L = K.sage(); L # optional - magma
1997
Number Field in K1 with defining polynomial x^2 - 2 over its base field
1998
sage: L.base_field() # optional - magma
1999
Number Field in K2 with defining polynomial x^2 - 3
2000
sage: K.GeneratorsSequence()[1].sage() # optional - magma
2001
Traceback (most recent call last):
2002
...
2003
NameError: name 'K' is not defined
2004
2005
"""
2006
z, preparse = self.Sage(nvals = 2)
2007
s = str(z)
2008
preparse = str(preparse) == 'true'
2009
return sage.misc.sage_eval.sage_eval(s, preparse=preparse)
2010
2011
def AssignNames(self, names):
2012
"""
2013
EXAMPLES::
2014
2015
sage: G = magma.DirichletGroup(20) # optional - magma
2016
sage: G.AssignNames(['a','b']) # optional - magma
2017
sage: G.1 # optional - magma
2018
a
2019
2020
::
2021
2022
sage: G.Elements() # optional - magma
2023
[
2024
1,
2025
a,
2026
b,
2027
a*b
2028
]
2029
"""
2030
P = self._check_valid()
2031
cmd = 'AssignNames(~%s, [%s])'%(self.name(),
2032
','.join('"%s"'%x for x in names))
2033
P.eval(cmd)
2034
2035
assign_names = AssignNames
2036
2037
def gen(self, n):
2038
"""
2039
Return the n-th generator of this Magma element. Note that
2040
generators are 1-based in Magma rather than 0 based!
2041
2042
INPUT:
2043
2044
2045
- ``n`` - a *positive* integer
2046
2047
2048
OUTPUT: MagmaElement
2049
2050
EXAMPLES::
2051
2052
sage: k.<a> = GF(9)
2053
sage: magma(k).gen(1) # optional -- magma
2054
a
2055
sage: R.<s,t,w> = k[]
2056
sage: m = magma(R) # optional -- magma
2057
sage: m.gen(1) # optional -- magma
2058
s
2059
sage: m.gen(2) # optional -- magma
2060
t
2061
sage: m.gen(3) # optional -- magma
2062
w
2063
sage: m.gen(0) # optional -- magma
2064
Traceback (most recent call last):
2065
...
2066
IndexError: index must be positive since Magma indexes are 1-based
2067
sage: m.gen(4) # optional -- magma
2068
Traceback (most recent call last):
2069
...
2070
IndexError: list index out of range
2071
"""
2072
if n <= 0:
2073
raise IndexError, "index must be positive since Magma indexes are 1-based"
2074
return self.gens()[n-1]
2075
2076
def gens(self):
2077
"""
2078
Return generators for self.
2079
2080
If self is named X is Magma, this function evaluates X.1, X.2,
2081
etc., in Magma until an error occurs. It then returns a Sage list
2082
of the resulting X.i. Note - I don't think there is a Magma command
2083
that returns the list of valid X.i. There are numerous ad hoc
2084
functions for various classes but nothing systematic. This function
2085
gets around that problem. Again, this is something that should
2086
probably be reported to the Magma group and fixed there.
2087
2088
AUTHORS:
2089
2090
- William Stein (2006-07-02)
2091
2092
EXAMPLES::
2093
2094
sage: magma("VectorSpace(RationalField(),3)").gens() # optional - magma
2095
[(1 0 0), (0 1 0), (0 0 1)]
2096
sage: magma("AbelianGroup(EllipticCurve([1..5]))").gens() # optional - magma
2097
[$.1]
2098
"""
2099
try:
2100
return self._magma_gens
2101
except AttributeError:
2102
pass
2103
G = []
2104
i = 1
2105
P = self._check_valid()
2106
n = self.name()
2107
while True:
2108
try:
2109
G.append(P('%s.%s'%(n,i)))
2110
except (RuntimeError, TypeError):
2111
break
2112
i += 1
2113
self._magma_gens = G
2114
return G
2115
2116
def gen_names(self):
2117
"""
2118
Return list of Magma variable names of the generators of self.
2119
2120
.. note::
2121
2122
As illustrated below, these are not the print names of the
2123
the generators of the Magma object, but special variable
2124
names in the Magma session that reference the generators.
2125
2126
EXAMPLES::
2127
2128
sage: R.<x,zw> = QQ[]
2129
sage: S = magma(R) # optional - magma
2130
sage: S.gen_names() # optional - magma
2131
('_sage_[...]', '_sage_[...]')
2132
sage: magma(S.gen_names()[1]) # optional - magma
2133
zw
2134
"""
2135
try:
2136
return self.__gen_names
2137
except AttributeError:
2138
self.__gen_names = tuple([x.name() for x in self.gens()])
2139
return self.__gen_names
2140
2141
def evaluate(self, *args):
2142
"""
2143
Evaluate self at the inputs.
2144
2145
INPUT:
2146
2147
2148
- ``*args`` - import arguments
2149
2150
2151
OUTPUT: self(\*args)
2152
2153
EXAMPLES::
2154
2155
sage: f = magma('Factorization') # optional - magma
2156
sage: f.evaluate(15) # optional - magma
2157
[ <3, 1>, <5, 1> ]
2158
sage: f(15) # optional - magma
2159
[ <3, 1>, <5, 1> ]
2160
sage: f = magma('GCD') # optional - magma
2161
sage: f.evaluate(15,20) # optional - magma
2162
5
2163
"""
2164
P = self._check_valid()
2165
v = [P(a) for a in args]
2166
names = ','.join([str(x) for x in v])
2167
return P('%s(%s)'%(self.name(), names))
2168
2169
eval = evaluate
2170
2171
def __call__(self, *args):
2172
"""
2173
Coerce something into the object (using the Magma ! notation).
2174
2175
For function calls, use self.eval(...).
2176
2177
EXAMPLES::
2178
2179
sage: M = magma.RMatrixSpace(magma.IntegerRing(), 2, 2) # optional - magma
2180
sage: A = M([1,2,3,4]); A # optional - magma
2181
[1 2]
2182
[3 4]
2183
sage: type(A) # optional - magma
2184
<class 'sage.interfaces.magma.MagmaElement'>
2185
sage: A.Type() # optional - magma
2186
ModMatRngElt
2187
"""
2188
if len(args) > 1:
2189
return self.evaluate(*args)
2190
P = self._check_valid()
2191
x = P(args[0])
2192
try:
2193
return P('%s!%s'%(self.name(), x.name()))
2194
except (RuntimeError, TypeError):
2195
return self.evaluate(*args)
2196
2197
def __iter__(self):
2198
"""
2199
Return iterator over this Magma element.
2200
2201
OUTPUT: generator object
2202
2203
.. warning::
2204
2205
Internally this constructs the list of elements in self in
2206
Magma, which is not a lazy operation. This is because Magma
2207
doesn't have a notion of lazy iterators, unfortunately.
2208
2209
EXAMPLES::
2210
2211
sage: V = magma('VectorSpace(GF(3),2)') # optional - magma
2212
sage: V # optional - magma
2213
Full Vector space of degree 2 over GF(3)
2214
sage: w = V.__iter__(); w # optional - magma
2215
<generator object __iter__ at ...>
2216
sage: w.next() # optional - magma
2217
(0 0)
2218
sage: w.next() # optional - magma
2219
(1 0)
2220
sage: list(w) # optional - magma
2221
[(2 0), (0 1), (1 1), (2 1), (0 2), (1 2), (2 2)]
2222
"""
2223
P = self._check_valid()
2224
z = P('[_a : _a in %s]'%self.name())
2225
for i in range(1,len(z)+1):
2226
yield z[i]
2227
2228
def __len__(self):
2229
r"""
2230
Return cardinality of this Magma element.
2231
2232
This is the same as ``#self`` in Magma.
2233
2234
EXAMPLES::
2235
2236
sage: V = magma('VectorSpace(GF(3),2)') # optional - magma
2237
sage: V # optional - magma
2238
Full Vector space of degree 2 over GF(3)
2239
sage: len(V) # optional - magma
2240
9
2241
sage: V.__len__() # optional - magma
2242
9
2243
"""
2244
P = self._check_valid()
2245
return int(P.eval('#%s'%self.name()))
2246
2247
def _polynomial_(self, R):
2248
"""
2249
Try to convert self into a polynomial in the univariate polynomial
2250
ring R.
2251
2252
EXAMPLES::
2253
2254
sage: R.<x> = QQ[]
2255
sage: f = magma(x^2 + 2/3*x + 5) # optional - magma
2256
sage: f # optional - magma
2257
x^2 + 2/3*x + 5
2258
sage: f.Type() # optional - magma
2259
RngUPolElt
2260
sage: f._polynomial_(R) # optional - magma
2261
x^2 + 2/3*x + 5
2262
"""
2263
return R(list(self.Eltseq()))
2264
2265
def _latex_(self):
2266
r"""
2267
Return latex representation of self.
2268
2269
AUTHORS:
2270
2271
- Jennifer Balakrishnan
2272
2273
Types that are nicely latex include:
2274
2275
2276
- rationals
2277
2278
- matrices
2279
2280
- polynomials
2281
2282
- binary quadratic forms
2283
2284
- elements of quadratic, cyclotomic number fields, and general
2285
number fields
2286
2287
- points
2288
2289
- elliptic curves
2290
2291
- power series
2292
2293
2294
IMPLEMENTATION: Calls latex.m, which is in
2295
Sage_ROOT/data/extcode/magma/latex.m
2296
2297
EXAMPLES::
2298
2299
sage: latex(magma('-2/3')) # optional - magma
2300
\frac{-2}{3}
2301
sage: magma('-2/3')._latex_() # optional - magma
2302
'\\frac{-2}{3}'
2303
2304
::
2305
2306
sage: magma.eval('R<x> := PolynomialRing(RationalField()); f := (x-17/2)^3;') # optional - magma
2307
''
2308
sage: latex(magma('f')) # optional - magma
2309
x^{3}-\frac{51}{2}x^{2}+\frac{867}{4}x-\frac{4913}{8}
2310
2311
::
2312
2313
sage: latex(magma('(MatrixAlgebra(RationalField(),3)![0,2,3,4,5,6,7,8,9])^(-1)')) # optional - magma
2314
\left(\begin{array}{ccc}-1&2&-1\\2&-7&4\\-1&\frac{14}{3}&\frac{-8}{3}\end{array}\right)
2315
2316
::
2317
2318
sage: magma.eval('K<a> := CyclotomicField(11)') # optional - magma
2319
''
2320
sage: latex(magma('a^3 + a - 17/3')) # optional - magma
2321
\frac{-17}{3}+\zeta_{11}+\zeta_{11}^{3}
2322
2323
::
2324
2325
sage: latex(magma('EllipticCurve([1,2/3,3/4,4/5,-5/6])')) # optional - magma
2326
y^2+xy+\frac{3}{4}y=x^3+\frac{2}{3}x^2+\frac{4}{5}x-\frac{5}{6}
2327
2328
::
2329
2330
sage: _=magma.eval('R<x> := PolynomialRing(RationalField())') # optional - magma
2331
sage: _=magma.eval('K<a> := NumberField(x^3+17*x+2)') # optional - magma
2332
sage: latex(magma('(1/3)*a^2 - 17/3*a + 2')) # optional - magma
2333
2-\frac{17}{3}a+\frac{1}{3}a^{2}
2334
2335
Sage auto-detects the greek letters and puts backslashes in::
2336
2337
sage: _=magma.eval('R<x> := PolynomialRing(RationalField())') # optional - magma
2338
sage: _=magma.eval('K<alpha> := NumberField(x^3+17*x+2)') # optional - magma
2339
sage: latex(magma('(1/3)*alpha^2 - 17/3*alpha + 2')) # optional - magma
2340
2-\frac{17}{3}\alpha+\frac{1}{3}\alpha^{2}
2341
2342
::
2343
2344
sage: _=magma.eval('R<alpha> := PolynomialRing(RationalField())') # optional - magma
2345
sage: latex(magma('alpha^3-1/7*alpha + 3')) # optional - magma
2346
\alpha^{3}-\frac{1}{7}\alpha+3
2347
2348
Finite field elements::
2349
2350
sage: _=magma.eval('K<a> := GF(27)') # optional - magma
2351
sage: latex(magma('a^2+2')) # optional - magma
2352
2+a^{2}
2353
2354
Printing of unnamed (dollar sign) generators works correctly::
2355
2356
sage: latex(magma('FiniteField(81).1^2+1')) # optional - magma
2357
1+\$.1^{2}
2358
2359
Finite fields::
2360
2361
sage: latex(magma('FiniteField(3)')) # optional - magma
2362
\mathbf{F}_{{3}}
2363
sage: latex(magma('FiniteField(27)')) # optional - magma
2364
\mathbf{F}_{{3}^{3}}
2365
2366
Power Series::
2367
2368
sage: _=magma.eval('R<x> := PowerSeriesRing(RationalField())') # optional - magma
2369
sage: latex(magma('(1/(1+x))')) # optional - magma
2370
1-x+x^{2}-x^{3}+x^{4}-x^{5}+x^{6}-x^{7}+x^{8}-x^{9}+x^{10}-x^{11}+x^{12}-x^{13}+x^{14}-x^{15}+x^{16}-x^{17}+x^{18}-x^{19}+O(x^{20})
2371
sage: _=magma.eval('R<x> := PowerSeriesRing(RationalField())') # optional - magma
2372
sage: latex(magma('(-1/(2+x + O(x^3)))')) # optional - magma
2373
\frac{-1}{2}+\frac{1}{4}x-\frac{1}{8}x^{2}+O(x^{3})
2374
2375
p-adic Numbers::
2376
2377
sage: latex(magma('pAdicField(7,4)!9333294394/49')) # optional - magma
2378
4\cdot{}7^{-2} + 5\cdot{}7^{-1} + 5+ 6\cdot{}7^{1} + O(7^{2})
2379
"""
2380
P = self._check_valid()
2381
s = str(P.eval('Latex(%s)'%self.name()))
2382
v = '\\mathrm{'
2383
if s[:len(v)] == v:
2384
raise AttributeError
2385
return s
2386
2387
def set_magma_attribute(self, attrname, value):
2388
"""
2389
INPUTS: attrname - string value - something coercible to a
2390
MagmaElement
2391
2392
EXAMPLES::
2393
2394
sage: V = magma("VectorSpace(RationalField(),2)") # optional - magma
2395
sage: V.set_magma_attribute('M',10) # optional - magma
2396
sage: V.get_magma_attribute('M') # optional - magma
2397
10
2398
sage: V.M # optional - magma
2399
10
2400
"""
2401
P = self.parent() # instance of Magma that contains this element.
2402
if not (isinstance(value, MagmaElement) and value.parent() is P):
2403
value = P(value)
2404
P.eval('%s`%s := %s'%(self.name(), attrname, value.name()))
2405
2406
def get_magma_attribute(self, attrname):
2407
"""
2408
Return value of a given Magma attribute. This is like selfattrname
2409
in Magma.
2410
2411
OUTPUT: MagmaElement
2412
2413
EXAMPLES::
2414
2415
sage: V = magma("VectorSpace(RationalField(),10)") # optional - magma
2416
sage: V.set_magma_attribute('M','"hello"') # optional - magma
2417
sage: V.get_magma_attribute('M') # optional - magma
2418
hello
2419
sage: V.M # optional - magma
2420
hello
2421
"""
2422
P = self.parent()
2423
return P('%s`%s'%(self.name(), attrname))
2424
2425
def list_attributes(self):
2426
"""
2427
Return the attributes of self, obtained by calling the
2428
ListAttributes function in Magma.
2429
2430
OUTPUT: list of strings
2431
2432
EXAMPLES: We observe that vector spaces in Magma have numerous
2433
funny and mysterious attributes.
2434
2435
::
2436
2437
sage: V = magma("VectorSpace(RationalField(),2)") # optional - magma
2438
sage: v = V.list_attributes(); v.sort(); v # optional - magma
2439
['Coroots', 'Involution', 'M', 'RootDatum', 'Roots', 'StrLocalData', 'T', 'decomp', 'eisen', 'exthom', 'hSplit', 'ip_form', 'p', 'ssbasis', 'weights']
2440
"""
2441
return magma.eval('ListAttributes(Type(%s))'%\
2442
self.name()).split()
2443
2444
def trait_names(self):
2445
"""
2446
Return all Magma functions that have this Magma element as first
2447
input. This is used for tab completion.
2448
2449
.. note::
2450
2451
This function can unfortunately be slow if there are a very
2452
large number of functions, e.g., when self is an
2453
integer. (This could be fixed by the addition of an
2454
appropriate function to the Magma kernel, which is
2455
something that can only be done by the Magma developers.)
2456
2457
OUTPUT:
2458
2459
2460
- ``list`` - sorted list of distinct strings
2461
2462
2463
EXAMPLES::
2464
2465
sage: R.<x> = ZZ[] # optional - magma
2466
sage: v = magma(R).trait_names() # optional - magma
2467
sage: v # optional - magma
2468
["'*'", "'+'", "'.'", "'/'", "'eq'", "'in'", "'meet'", "'subset'", ...]
2469
"""
2470
M = self.methods()
2471
N = []
2472
for x in M:
2473
i = x.find('(')
2474
N.append(x[:i])
2475
v = list(set(N + self.list_attributes()))
2476
v.sort()
2477
return v
2478
2479
def methods(self, any=False):
2480
"""
2481
Return signatures of all Magma intrinsics that can take self as the
2482
first argument, as strings.
2483
2484
INPUT:
2485
2486
2487
- ``any`` - (bool: default is False) if True, also
2488
include signatures with Any as first argument.
2489
2490
2491
OUTPUT: list of strings
2492
2493
EXAMPLES::
2494
2495
sage: v = magma('2/3').methods() # optional - magma
2496
sage: v[0] # optional - magma
2497
"'*'..."
2498
"""
2499
t = str(self.Type())
2500
X = self.parent().eval('ListSignatures(%s)'%self.Type()).split('\n')
2501
tt = "(<"+t
2502
if any:
2503
Y = [x for x in X if tt in x or "(<Any>" in x]
2504
else:
2505
Y = [x for x in X if tt in x]
2506
return Y
2507
2508
def __floordiv__(self, x):
2509
"""
2510
Quotient of division of self by other. This is denoted // ("div" in
2511
magma).
2512
2513
EXAMPLE::
2514
2515
sage: R.<x,y,z> = QQ[]
2516
sage: magma(5)//magma(2) # optional - magma
2517
2
2518
sage: m = magma(x*z + x*y) # optional - magma
2519
sage: n = magma(x) # optional - magma
2520
sage: m//n # optional - magma
2521
y + z
2522
"""
2523
return self.parent()('%s div %s'%(self.name(), x.name()))
2524
2525
def __nonzero__(self):
2526
"""
2527
Return True if self is nonzero according to Magma. If Magma can't
2528
decide, i.e., raising an error then also return True.
2529
2530
EXAMPLES: We define a Magma vector space.
2531
2532
::
2533
2534
sage: V = magma('VectorSpace(GF(3),2)'); V # optional - magma
2535
Full Vector space of degree 2 over GF(3)
2536
2537
The first generator is nonzero.
2538
2539
::
2540
2541
sage: V.gen(1).__nonzero__() # optional - magma
2542
True
2543
2544
The zero element is zero.
2545
2546
::
2547
2548
sage: V(0).__nonzero__() # optional - magma
2549
False
2550
2551
The space itself is nonzero (the default - in Magma no comparison
2552
to 0 is possible).
2553
2554
::
2555
2556
sage: V.__nonzero__() # optional - magma
2557
True
2558
2559
We can also call bool which is the opposite of __nonzero__.
2560
2561
::
2562
2563
sage: bool(V(0)) # optional - magma
2564
False
2565
sage: bool(V.gen(1)) # optional - magma
2566
True
2567
sage: bool(V) # optional - magma
2568
True
2569
2570
Test use in bool conversions of bools::
2571
2572
sage: bool(magma(False)) # optional - magma
2573
False
2574
sage: bool(magma(True)) # optional - magma
2575
True
2576
sage: bool(magma(1)) # optional - magma
2577
True
2578
sage: bool(magma(0)) # optional - magma
2579
False
2580
"""
2581
try:
2582
return not self.parent()("%s eq 0"%self.name()).bool()
2583
except TypeError:
2584
# comparing with 0 didn't work; try comparing with
2585
try:
2586
return not self.parent()("%s eq false"%self.name()).bool()
2587
except TypeError:
2588
pass
2589
return True
2590
2591
def sub(self, gens):
2592
"""
2593
Return the sub-object of self with given gens.
2594
2595
INPUT:
2596
2597
2598
- ``gens`` - object or list/tuple of generators
2599
2600
2601
EXAMPLES::
2602
2603
sage: V = magma('VectorSpace(RationalField(),3)') # optional - magma
2604
sage: W = V.sub([ [1,2,3], [1,1,2] ]); W # optional - magma
2605
Vector space of degree 3, dimension 2 over Rational Field
2606
Generators:
2607
(1 2 3)
2608
(1 1 2)
2609
Echelonized basis:
2610
(1 0 1)
2611
(0 1 1)
2612
"""
2613
return self.parent().bar_call(self, 'sub', gens)
2614
2615
def quo(self, gens):
2616
"""
2617
Return the quotient of self by the given object or list of
2618
generators.
2619
2620
INPUT:
2621
2622
2623
- ``gens`` - object or list/tuple of generators
2624
2625
2626
OUTPUT:
2627
2628
2629
- ``magma element`` - the quotient object
2630
2631
- ``magma element`` - mapping from self to the
2632
quotient object
2633
2634
2635
EXAMPLES::
2636
2637
sage: V = magma('VectorSpace(RationalField(),3)') # optional - magma
2638
sage: V.quo([[1,2,3], [1,1,2]]) # optional - magma
2639
(Full Vector space of degree 1 over Rational Field, Mapping from: Full Vector space of degree 3 over Rational Field to Full Vector space of degree 1 over Rational Field)
2640
2641
We illustrate quotienting out by an object instead of a list of
2642
generators::
2643
2644
sage: W = V.sub([ [1,2,3], [1,1,2] ]) # optional - magma
2645
sage: V.quo(W) # optional - magma
2646
(Full Vector space of degree 1 over Rational Field, Mapping from: Full Vector space of degree 3 over Rational Field to Full Vector space of degree 1 over Rational Field)
2647
2648
We quotient a ZZ module out by a submodule.
2649
2650
::
2651
2652
sage: V = magma.RModule(ZZ,3); V # optional - magma
2653
RModule(IntegerRing(), 3)
2654
sage: W, phi = V.quo([[1,2,3]]) # optional - magma
2655
sage: W # optional - magma
2656
RModule(IntegerRing(), 2)
2657
sage: phi # optional - magma
2658
Mapping from: RModule(IntegerRing(), 3) to RModule(IntegerRing(), 2)
2659
"""
2660
return self.parent().bar_call(self, 'quo', gens, nvals=2)
2661
2662
def ideal(self, gens):
2663
"""
2664
Return the ideal of self with given list of generators.
2665
2666
INPUT:
2667
2668
2669
- ``gens`` - object or list/tuple of generators
2670
2671
2672
OUTPUT:
2673
2674
2675
- ``magma element`` - a Magma ideal
2676
2677
2678
EXAMPLES::
2679
2680
sage: R = magma('PolynomialRing(RationalField())') # optional - magma
2681
sage: R.assign_names(['x']) # optional - magma
2682
sage: x = R.1 # optional - magma
2683
sage: R.ideal([x^2 - 1, x^3 - 1]) # optional - magma
2684
Ideal of Univariate Polynomial Ring in x over Rational Field generated by x - 1
2685
"""
2686
return self.parent().bar_call(self, 'ideal', gens, nvals=1)
2687
2688
###########################################################################
2689
2690
magma = Magma()
2691
2692
def reduce_load_Magma():
2693
"""
2694
Used in unpickling a Magma interface.
2695
2696
This functions just returns the global default Magma interface.
2697
2698
EXAMPLES::
2699
2700
sage: sage.interfaces.magma.reduce_load_Magma()
2701
Magma
2702
"""
2703
return magma
2704
2705
def magma_console():
2706
"""
2707
Run a command line Magma session.
2708
2709
EXAMPLES::
2710
2711
sage: magma_console() # not tested
2712
Magma V2.14-9 Sat Oct 11 2008 06:36:41 on one [Seed = 1157408761]
2713
Type ? for help. Type <Ctrl>-D to quit.
2714
>
2715
Total time: 2.820 seconds, Total memory usage: 3.95MB
2716
"""
2717
console('sage-native-execute magma')
2718
2719
def magma_version():
2720
"""
2721
Return the version of Magma that you have in your PATH on your
2722
computer.
2723
2724
OUTPUT:
2725
2726
2727
- ``numbers`` - 3-tuple: major, minor, etc.
2728
2729
- ``string`` - version as a string
2730
2731
2732
EXAMPLES::
2733
2734
sage: magma_version() # random, optional - magma
2735
((2, 14, 9), 'V2.14-9')
2736
"""
2737
t = tuple([int(n) for n in magma.eval('GetVersion()').split()])
2738
return t, 'V%s.%s-%s'%t
2739
2740
class MagmaGBLogPrettyPrinter:
2741
"""
2742
A device which filters Magma Groebner basis computation logs.
2743
"""
2744
cmd_inpt = re.compile("^>>>$")
2745
app_inpt = re.compile("^Append\(~_sage_, 0\);$")
2746
2747
deg_curr = re.compile("^Basis length\: (\d+), queue length\: (\d+), step degree\: (\d+), num pairs\: (\d+)$")
2748
pol_curr = re.compile("^Number of pair polynomials\: (\d+), at (\d+) column\(s\), .*")
2749
2750
def __init__(self, verbosity=1, style='magma'):
2751
"""
2752
Construct a new Magma Groebner Basis log pretty printer.
2753
2754
INPUT:
2755
2756
- ``verbosity`` - how much information should be printed
2757
(between 0 and 1)
2758
2759
- ``style`` - if "magma" the full Magma log is printed; if
2760
'sage' only the current degree and the number of pairs in
2761
the queue is printed (default: "magma").
2762
2763
EXAMPLE::
2764
2765
sage: P.<x,y,z> = GF(32003)[]
2766
sage: I = sage.rings.ideal.Cyclic(P)
2767
sage: _ = I.groebner_basis('magma',prot='sage') # indirect doctest, optional - magma, not tested
2768
2769
Leading term degree: 2. Critical pairs: 2.
2770
Leading term degree: 3. Critical pairs: 1.
2771
2772
Highest degree reached during computation: 3.
2773
2774
sage: P.<x,y,z> = GF(32003)[]
2775
sage: I = sage.rings.ideal.Cyclic(P)
2776
sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma, not tested
2777
2778
Homogeneous weights search
2779
Number of variables: 3, nullity: 1
2780
Exact search time: 0.000
2781
********************
2782
FAUGERE F4 ALGORITHM
2783
********************
2784
Coefficient ring: GF(32003)
2785
Rank: 3
2786
Order: Graded Reverse Lexicographical
2787
NEW hash table
2788
Matrix kind: Modular FP
2789
Datum size: 4
2790
No queue sort
2791
Initial length: 3
2792
Inhomogeneous
2793
2794
Initial queue setup time: 0.000
2795
Initial queue length: 2
2796
2797
*******
2798
STEP 1
2799
Basis length: 3, queue length: 2, step degree: 2, num pairs: 1
2800
Basis total mons: 8, average length: 2.667
2801
Number of pair polynomials: 1, at 4 column(s), 0.000
2802
...
2803
Total Faugere F4 time: 0.000, real time: 0.000
2804
2805
sage: set_random_seed(1)
2806
sage: sr = mq.SR(1,1,2,4)
2807
sage: F,s = sr.polynomial_system()
2808
sage: I = F.ideal()
2809
sage: _ = I.groebner_basis('magma',prot='sage') # indirect doctest, optional - magma, not tested
2810
Leading term degree: 1. Critical pairs: 40.
2811
Leading term degree: 2. Critical pairs: 40.
2812
Leading term degree: 3. Critical pairs: 38.
2813
Leading term degree: 2. Critical pairs: 327.
2814
Leading term degree: 2. Critical pairs: 450.
2815
Leading term degree: 2. Critical pairs: 416.
2816
Leading term degree: 3. Critical pairs: 415.
2817
Leading term degree: 4. Critical pairs: 98 (all pairs of current degree eliminated by criteria).
2818
Leading term degree: 5. Critical pairs: 3 (all pairs of current degree eliminated by criteria).
2819
2820
Highest degree reached during computation: 3.
2821
"""
2822
self.verbosity = verbosity
2823
self.style = style
2824
2825
self.curr_deg = 0 # current degree
2826
self.curr_npairs = 0 # current number of pairs to be considered
2827
self.max_deg = 0 # maximal degree in total
2828
2829
self.storage = "" # stores incomplete strings
2830
self.sync = None # should we expect a sync integer?
2831
2832
def write(self, s):
2833
"""
2834
EXAMPLE::
2835
2836
sage: P.<x,y,z> = GF(32003)[]
2837
sage: I = sage.rings.ideal.Katsura(P)
2838
sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma
2839
<BLANKLINE>
2840
Homogeneous weights search
2841
...
2842
Total Faugere F4 time: ..., real time: ...
2843
"""
2844
verbosity,style = self.verbosity,self.style
2845
2846
if self.storage:
2847
s = self.storage + s
2848
self.storage = ""
2849
2850
for line in s.splitlines():
2851
#print "l: '%s'"%line
2852
# deal with the Sage <-> Magma syncing code
2853
match = re.match(MagmaGBLogPrettyPrinter.cmd_inpt,line)
2854
if match:
2855
self.sync = 1
2856
continue
2857
2858
if self.sync:
2859
if self.sync == 1:
2860
self.sync = line
2861
continue
2862
else:
2863
if line == '':
2864
continue
2865
self.sync = None
2866
continue
2867
2868
if re.match(MagmaGBLogPrettyPrinter.app_inpt,line):
2869
continue
2870
2871
if re.match(MagmaGBLogPrettyPrinter.deg_curr,line):
2872
match = re.match(MagmaGBLogPrettyPrinter.deg_curr,line)
2873
2874
nbasis,npairs,deg,npairs_deg = map(int,match.groups())
2875
2876
self.curr_deg = deg
2877
self.curr_npairs = npairs
2878
2879
if re.match(MagmaGBLogPrettyPrinter.pol_curr,line):
2880
match = re.match(MagmaGBLogPrettyPrinter.pol_curr,line)
2881
pol_curr,col_curr = map(int,match.groups())
2882
2883
if pol_curr != 0:
2884
if self.max_deg < self.curr_deg:
2885
self.max_deg = self.curr_deg
2886
2887
if style == "sage" and verbosity >= 1:
2888
print "Leading term degree: %2d. Critical pairs: %d."%(self.curr_deg,self.curr_npairs)
2889
else:
2890
if style == "sage" and verbosity >= 1:
2891
print "Leading term degree: %2d. Critical pairs: %d (all pairs of current degree eliminated by criteria)."%(self.curr_deg,self.curr_npairs)
2892
2893
if style == "magma" and verbosity >= 1:
2894
print line
2895
2896
def flush(self):
2897
"""
2898
EXAMPLE::
2899
2900
sage: from sage.interfaces.magma import MagmaGBLogPrettyPrinter
2901
sage: logs = MagmaGBLogPrettyPrinter()
2902
sage: logs.flush()
2903
"""
2904
import sys
2905
sys.stdout.flush()
2906
2907