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