Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/interfaces/macaulay2.py
4048 views
1
r"""
2
Interface to Macaulay2
3
4
\note{You must have \code{Macaulay2} installed on your computer
5
for this interface to work. Macaulay2 is not included with \sage,
6
but you can obtain it from \url{http://www.math.uiuc.edu/Macaulay2/}.
7
Note additional optional \sage packages are required.}
8
9
Sage provides an interface to the Macaulay2 computational algebra
10
system. This system provides extensive functionality for commutative
11
algebra. You do not have to install any optional packages.
12
13
The Macaulay2 interface offers three pieces of functionality:
14
\begin{enumerate}
15
16
\item \code{Macaulay2_console()} -- A function that dumps you
17
into an interactive command-line Macaulay2 session.
18
19
\item \code{Macaulay2(expr)} -- Evaluation of arbitrary Macaulay2
20
expressions, with the result returned as a string.
21
22
\item \code{Macaulay2.new(expr)} -- Creation of a Sage object that wraps a
23
Macaulay2 object. This provides a Pythonic interface to Macaulay2. For
24
example, if \code{f=Macaulay2.new(10)}, then \code{f.gcd(25)} returns the
25
GCD of $10$ and $25$ computed using Macaulay2.
26
27
\end{enumerate}
28
29
EXAMPLES:
30
sage: print macaulay2('3/5 + 7/11') #optional
31
68
32
--
33
55
34
sage: f = macaulay2('f = i -> i^3') #optional
35
sage: f #optional
36
f
37
sage: f(5) #optional
38
125
39
40
sage: R = macaulay2('ZZ/5[x,y,z]') #optional
41
sage: print R #optional
42
ZZ
43
--[x..z, Degrees => {3:1}, Heft => {1}, MonomialOrder => {MonomialSize => 32}, DegreeRank => 1]
44
5 {GRevLex => {3:1} }
45
{Position => Up }
46
sage: x = macaulay2('x') #optional
47
sage: y = macaulay2('y') #optional
48
sage: print (x+y)^5 #optional
49
5 5
50
x + y
51
sage: parent((x+y)^5) #optional
52
Macaulay2
53
54
sage: R = macaulay2('QQ[x,y,z,w]') #optional
55
sage: f = macaulay2('x^4 + 2*x*y^3 + x*y^2*w + x*y*z*w + x*y*w^2 + 2*x*z*w^2 + y^4 + y^3*w + 2*y^2*z*w + z^4 + w^4') #optional
56
sage: print f #optional
57
4 3 4 4 2 3 2 2 2 4
58
x + 2x*y + y + z + x*y w + y w + x*y*z*w + 2y z*w + x*y*w + 2x*z*w + w
59
sage: g = f * macaulay2('x+y^5') #optional
60
sage: print g.factor() #optional
61
4 3 4 4 2 3 2 2 2 4 5
62
(x + 2x*y + y + z + x*y w + y w + x*y*z*w + 2y z*w + x*y*w + 2x*z*w + w )(y + x)
63
64
65
AUTHORS:
66
-- Kiran Kedlaya and David Roe (2006-02-05, during Sage coding sprint)
67
-- William Stein (2006-02-09): inclusion in Sage; prompt uses regexp,
68
calling of Macaulay2 functions via __call__.
69
-- William Stein (2006-02-09): fixed bug in reading from file and
70
improved output cleaning.
71
-- Kiran Kedlaya (2006-02-12): added ring and ideal constructors,
72
list delimiters, is_Macaulay2Element, sage_polystring,
73
__floordiv__, __mod__, __iter__, __len__; stripped extra
74
leading space and trailing newline from output.
75
76
TODO:
77
-- get rid of all numbers in output, e.g., in ideal function below.
78
"""
79
80
#*****************************************************************************
81
# Copyright (C) 2006 Kiran S. Kedlaya <[email protected]>
82
# David Roe <[email protected]>
83
# William Stein <[email protected]>
84
#
85
# Distributed under the terms of the GNU General Public License (GPL)
86
#
87
# This code is distributed in the hope that it will be useful,
88
# but WITHOUT ANY WARRANTY; without even the implied warranty of
89
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
90
# General Public License for more details.
91
#
92
# The full text of the GPL is available at:
93
#
94
# http://www.gnu.org/licenses/
95
#*****************************************************************************
96
97
import os
98
99
from expect import Expect, ExpectElement, AsciiArtString, ExpectFunction
100
101
from sage.misc.multireplace import multiple_replace
102
103
import re
104
105
def remove_output_labels(s):
106
"""
107
Remove output labels of Macaulay2 from a string.
108
109
:param s: output of Macaulay2
110
111
:type s: string
112
113
:returns: the input string with `n` symbols removed from the beginning
114
of each line, where `n` is the minimal number of spaces or
115
symbols of Macaulay2 output labels (looking like 'o39 = ')
116
present on every non-empty line.
117
118
:rtype: string
119
120
:note: If ``s`` consists of several outputs and their lables have
121
different width, it is possible that some strings will have leading
122
spaces (or maybe even pieces of output labels). However, this
123
function will try not cut any messages.
124
125
EXAMPLES:
126
sage: from sage.interfaces.macaulay2 import remove_output_labels
127
sage: output = 'o1 = QQ [x, y]\n\no1 : PolynomialRing\n'
128
sage: remove_output_labels(output)
129
'QQ [x, y]\n\nPolynomialRing\n'
130
"""
131
label = re.compile("^o[0-9]+ (=|:) |^ *")
132
lines = s.split("\n")
133
matches = [label.match(l) for l in lines if l != ""]
134
if len(matches) == 0:
135
return s
136
else:
137
n = min(m.end() - m.start() for m in matches)
138
return "\n".join(l[n:] for l in lines)
139
140
141
PROMPT = "_EGAS_ : "
142
143
144
class Macaulay2(Expect):
145
"""
146
Interface to the Macaulay2 interpreter.
147
"""
148
def __init__(self, maxread=10000, script_subdirectory="",
149
logfile=None, server=None,server_tmpdir=None):
150
"""
151
Initialize a Macaulay2 interface instance.
152
153
We replace the standard input prompt with a strange one, so that
154
we do not catch input prompts inside the documentation.
155
156
We replace the standard input continuation prompt, which is
157
just a bunch of spaces and cannot be automatically detected in a
158
reliable way. This is necessary for allowing commands that occupy
159
several strings.
160
161
We also change the starting line number to make all the output
162
labels to be of the same length. This allows correct stripping of
163
the output of several commands.
164
165
TESTS:
166
sage: macaulay2 == loads(dumps(macaulay2))
167
True
168
"""
169
init_str = (
170
# Prompt changing commands
171
"""ZZ#{Standard,Core#"private dictionary"#"InputPrompt"} = lineno -> "%s";""" % PROMPT +
172
"""ZZ#{Standard,Core#"private dictionary"#"InputContinuationPrompt"} = lineno -> "%s";""" % PROMPT +
173
# Also prevent line wrapping in Macaulay2
174
"printWidth = 0;" +
175
# And make all output labels to be of the same width
176
"lineNumber = 10^9;")
177
Expect.__init__(self,
178
name = 'macaulay2',
179
prompt = PROMPT,
180
command = "M2 --no-debug --no-readline --silent -e '%s'" % init_str,
181
maxread = maxread,
182
server = server,
183
server_tmpdir = server_tmpdir,
184
script_subdirectory = script_subdirectory,
185
verbose_start = False,
186
logfile = logfile,
187
eval_using_file_cutoff=500)
188
189
# Macaulay2 provides no "clear" function. However, Macaulay2 does provide
190
# garbage collection; since expect automatically reuses variable names,
191
# garbage collection in Sage properly sets up garbage collection in
192
# Macaulay2.
193
194
def __reduce__(self):
195
"""
196
Used in serializing an Macaulay2 interface.
197
198
EXAMPLES:
199
sage: rlm2, t = macaulay2.__reduce__()
200
sage: rlm2(*t)
201
Macaulay2
202
"""
203
return reduce_load_macaulay2, tuple([])
204
205
def _read_in_file_command(self, filename):
206
"""
207
Load and *execute* the content of ``filename`` in Macaulay2.
208
209
:param filename: the name of the file to be loaded and executed.
210
:type filename: string
211
:returns: Macaulay2 command loading and executing commands in
212
``filename``, that is, ``'load "filename"'``.
213
:rtype: string
214
215
TESTS::
216
217
sage: from sage.misc.misc import tmp_filename
218
sage: filename = tmp_filename()
219
sage: f = open(filename, "w")
220
sage: f.write("sage_test = 7;")
221
sage: f.close()
222
sage: command = macaulay2._read_in_file_command(filename)
223
sage: macaulay2.eval(command) #optional
224
sage: macaulay2.eval("sage_test") #optional
225
7
226
sage: import os
227
sage: os.unlink(filename)
228
sage: macaulay2._read_in_file_command("test")
229
'load "test"'
230
sage: macaulay2(10^10000) == 10^10000 #optional
231
True
232
"""
233
return 'load "%s"' % filename
234
235
def __getattr__(self, attrname):
236
"""
237
EXAMPLES:
238
sage: gb = macaulay2.gb #optional
239
sage: type(gb) #optional
240
<class 'sage.interfaces.macaulay2.Macaulay2Function'>
241
sage: gb._name #optional
242
'gb'
243
"""
244
if attrname[:1] == "_":
245
raise AttributeError
246
return Macaulay2Function(self, attrname)
247
248
def eval(self, code, strip=True, **kwds):
249
"""
250
Send the code x to the Macaulay2 interpreter and return the output
251
as a string suitable for input back into Macaulay2, if possible.
252
253
INPUT:
254
code -- str
255
strip -- ignored
256
257
EXAMPLES:
258
sage: macaulay2.eval("2+2") #optional
259
4
260
"""
261
code = code.strip()
262
# TODO: in some cases change toExternalString to toString??
263
ans = Expect.eval(self, code, strip=strip, **kwds).strip('\n')
264
if strip:
265
ans = remove_output_labels(ans)
266
return AsciiArtString(ans)
267
268
def restart(self):
269
r"""
270
Restart Macaulay2 interpreter.
271
272
TEST:
273
sage: macaulay2.restart() # optional
274
"""
275
# If we allow restart to be called as a function, there will be
276
# parasitic output
277
self.eval("restart")
278
279
def get(self, var):
280
"""
281
Get the value of the variable var.
282
283
EXAMPLES:
284
sage: macaulay2.set("a", "2") #optional
285
sage: macaulay2.get("a") #optional
286
2
287
"""
288
return self.eval("describe %s"%var, strip=True)
289
290
def set(self, var, value):
291
"""
292
Set the variable var to the given value.
293
294
EXAMPLES:
295
sage: macaulay2.set("a", "2") #optional
296
sage: macaulay2.get("a") #optional
297
2
298
"""
299
cmd = '%s=%s;'%(var,value)
300
ans = Expect.eval(self, cmd)
301
if ans.find("stdio:") != -1:
302
raise RuntimeError, "Error evaluating Macaulay2 code.\nIN:%s\nOUT:%s"%(cmd, ans)
303
304
def _object_class(self):
305
"""
306
Returns the class of Macaulay2 elements.
307
308
EXAMPLES:
309
sage: macaulay2._object_class()
310
<class 'sage.interfaces.macaulay2.Macaulay2Element'>
311
312
"""
313
return Macaulay2Element
314
315
def console(self):
316
"""
317
Spawn a new M2 command-line session.
318
319
EXAMPLES:
320
sage: macaulay2.console() # not tested
321
Macaulay 2, version 1.1
322
with packages: Classic, Core, Elimination, IntegralClosure, LLLBases, Parsing, PrimaryDecomposition, SchurRings, TangentCone
323
...
324
325
"""
326
macaulay2_console()
327
328
def _left_list_delim(self):
329
"""
330
Returns the Macaulay2 left delimiter for lists.
331
332
EXAMPLES:
333
sage: macaulay2._left_list_delim()
334
'{'
335
"""
336
return '{'
337
338
def _right_list_delim(self):
339
"""
340
Returns the Macaulay2 right delimiter for lists.
341
342
EXAMPLES:
343
sage: macaulay2._right_list_delim()
344
'}'
345
"""
346
return '}'
347
348
def _true_symbol(self):
349
"""
350
Returns the Macaulay2 symbol for True.
351
352
EXAMPLES:
353
sage: macaulay2._true_symbol()
354
'true'
355
"""
356
return 'true'
357
358
def _false_symbol(self):
359
"""
360
Returns the Macaulay2 symbol for False.
361
362
EXAMPLES:
363
sage: macaulay2._false_symbol()
364
'false'
365
"""
366
return 'false'
367
368
def _equality_symbol(self):
369
"""
370
Returns the Macaulay2 symbol for equality.
371
372
EXAMPLES:
373
sage: macaulay2._false_symbol()
374
'false'
375
"""
376
return '=='
377
378
def cputime(self, t=None):
379
"""
380
EXAMPLES:
381
sage: R = macaulay2("QQ[x,y]") #optional
382
sage: x,y = R.gens() #optional
383
sage: a = (x+y+1)^20 #optional
384
sage: macaulay2.cputime() #optional random
385
0.48393700000000001
386
"""
387
_t = float(self.cpuTime().to_sage())
388
if t:
389
return _t - t
390
else:
391
return _t
392
393
def version(self):
394
"""
395
Returns the version of Macaulay2.
396
397
EXAMPLES:
398
sage: macaulay2.version() #optional
399
(1, 3, 1)
400
"""
401
s = self.eval("version")
402
r = re.compile("VERSION => (.*?)\n")
403
s = r.search(s).groups()[0]
404
return tuple(int(i) for i in s.split("."))
405
406
### Constructors
407
408
def ideal(self, *gens):
409
"""
410
Return the ideal generated by gens.
411
412
INPUT:
413
gens -- list or tuple of Macaulay2 objects (or objects that can be
414
made into Macaulay2 objects via evaluation)
415
OUTPUT:
416
the Macaulay2 ideal generated by the given list of gens
417
418
EXAMPLES:
419
sage: R2 = macaulay2.ring('QQ', '[x, y]'); R2 # optional
420
QQ[x..y, Degrees => {2:1}, Heft => {1}, MonomialOrder => {MonomialSize => 16}, DegreeRank => 1]
421
{Lex => 2 }
422
{Position => Up }
423
sage: I = macaulay2.ideal( ('y^2 - x^3', 'x - y') ); I # optional
424
3 2
425
ideal (- x + y , x - y)
426
sage: J = I^3; J.gb().gens().transpose() # optional
427
{-9} | y9-3y8+3y7-y6 |
428
{-7} | xy6-2xy5+xy4-y7+2y6-y5 |
429
{-5} | x2y3-x2y2-2xy4+2xy3+y5-y4 |
430
{-3} | x3-3x2y+3xy2-y3 |
431
432
"""
433
if len(gens) == 1 and isinstance(gens[0], (list, tuple)):
434
gens = gens[0]
435
gens2 = []
436
for g in gens:
437
if not isinstance(g, Macaulay2Element):
438
gens2.append(self(g))
439
else:
440
gens2.append(g)
441
return self('ideal {%s}'%(",".join([g.name() for g in gens2])))
442
443
def ring(self, base_ring='ZZ', vars='[x]', order='Lex'):
444
r"""
445
Create a Macaulay2 ring.
446
447
INPUT:
448
base_ring -- base ring (see examples below)
449
vars -- a tuple or string that defines the variable names
450
order -- string -- the monomial order (default: 'Lex')
451
452
OUTPUT:
453
a Macaulay2 ring (with base ring ZZ)
454
455
EXAMPLES:
456
This is a ring in variables named a through d over the finite field
457
of order 7, with graded reverse lex ordering:
458
sage: R1 = macaulay2.ring('ZZ/7', '[a..d]', 'GRevLex'); R1 # optional
459
ZZ
460
--[a..d, Degrees => {4:1}, Heft => {1}, MonomialOrder => {MonomialSize => 16}, DegreeRank => 1]
461
7 {GRevLex => {4:1} }
462
{Position => Up }
463
sage: R1.char() # optional
464
7
465
466
This is a polynomial ring over the rational numbers:
467
sage: R2 = macaulay2.ring('QQ', '[x, y]'); R2 # optional
468
QQ[x..y, Degrees => {2:1}, Heft => {1}, MonomialOrder => {MonomialSize => 16}, DegreeRank => 1]
469
{Lex => 2 }
470
{Position => Up }
471
"""
472
varstr = str(vars)[1:-1]
473
if ".." in varstr:
474
varstr = "symbol " + varstr[0] + ".." + "symbol " + varstr[-1]
475
else:
476
varstr = ", ".join(["symbol " + v for v in varstr.split(", ")])
477
return self.new('%s[%s, MonomialSize=>16, MonomialOrder=>%s]'%(base_ring, varstr, order))
478
479
def help(self, s):
480
"""
481
EXAMPLES:
482
sage: macaulay2.help("load") # optional
483
load -- read Macaulay2 commands
484
*******************************
485
...
486
* "input" -- read Macaulay2 commands and echo
487
* "notify" -- whether to notify the user when a file is loaded
488
"""
489
r = self.eval("help %s" % s)
490
end = r.rfind("\n\nDIV")
491
if end != -1:
492
r = r[:end]
493
return AsciiArtString(r)
494
495
def trait_names(self):
496
"""
497
Return a list of tab completions for Macaulay2.
498
499
:returns: dynamically built sorted list of commands obtained using
500
Macaulay2 "apropos" command.
501
502
:rtype: list of strings
503
504
TESTS:
505
sage: names = macaulay2.trait_names() # optional
506
sage: 'ring' in names # optional
507
True
508
sage: macaulay2.eval("abcabc = 4") # optional
509
4
510
sage: names = macaulay2.trait_names() # optional
511
sage: "abcabc" in names # optional
512
True
513
"""
514
# Get all the names from Macaulay2 except numbered outputs like
515
# o1, o2, etc. and automatic Sage variable names sage0, sage1, etc.
516
# It is faster to get it back as a string.
517
r = macaulay2.eval(r"""
518
toString select(
519
apply(apropos "^[[:alnum:]]+$", toString),
520
s -> not match("^(o|sage)[0-9]+$", s))
521
""")
522
# Now split this string into separate names
523
r = r[1:-1].split(", ")
524
# Macaulay2 sorts things like A, a, B, b, ...
525
r.sort()
526
return r
527
528
def use(self, R):
529
"""
530
Use the Macaulay2 ring R.
531
532
EXAMPLES:
533
sage: R = macaulay2("QQ[x,y]") #optional
534
sage: P = macaulay2("ZZ/7[symbol x, symbol y]") #optional
535
sage: macaulay2("x").cls() #optional
536
ZZ
537
--[x..y, Degrees => {2:1}, Heft => {1}, MonomialOrder => {MonomialSize => 32}, DegreeRank => 1]
538
7 {GRevLex => {2:1} }
539
{Position => Up }
540
sage: macaulay2.use(R) #optional
541
sage: macaulay2("x").cls() #optional
542
QQ[x..y, Degrees => {2:1}, Heft => {1}, MonomialOrder => {MonomialSize => 32}, DegreeRank => 1]
543
{GRevLex => {2:1} }
544
{Position => Up }
545
"""
546
R = self(R)
547
self.eval("use %s"%R.name())
548
549
def new_from(self, type, value):
550
"""
551
Returns a new Macaulay2Element of type type constructed from
552
value.
553
554
EXAMPLES:
555
sage: l = macaulay2.new_from("MutableList", [1,2,3]) #optional
556
sage: l #optional
557
MutableList{...3...}
558
sage: list(l) #optional
559
[1, 2, 3]
560
561
"""
562
type = self(type)
563
value = self(value)
564
return self.new("new %s from %s"%(type.name(), value.name()))
565
566
567
class Macaulay2Element(ExpectElement):
568
def _latex_(self):
569
"""
570
EXAMPLES:
571
sage: m = macaulay2('matrix {{1,2},{3,4}}') #optional
572
sage: m #optional
573
| 1 2 |
574
| 3 4 |
575
sage: latex(m) #optional
576
\begin{pmatrix}1& {2}\\ {3}& {4}\\ \end{pmatrix}
577
"""
578
s = self.tex().external_string().strip('"').strip('$').replace('\\\\','\\')
579
s = s.replace(r"\bgroup","").replace(r"\egroup","")
580
return s
581
582
def __iter__(self):
583
"""
584
EXAMPLES:
585
sage: l = macaulay2([1,2,3]) #optional
586
sage: list(iter(l)) #optional
587
[1, 2, 3]
588
"""
589
for i in range(len(self)): # zero-indexed!
590
yield self[i]
591
592
def __str__(self):
593
"""
594
EXAMPLES:
595
sage: R = macaulay2("QQ[x,y,z]/(x^3-y^3-z^3)") #optional
596
sage: x = macaulay2('x') #optional
597
sage: y = macaulay2('y') #optional
598
sage: print x+y #optional
599
x + y
600
sage: print macaulay2("QQ[x,y,z]") #optional
601
QQ[x..z, Degrees => {3:1}, Heft => {1}, MonomialOrder => {MonomialSize => 32}, DegreeRank => 1]
602
{GRevLex => {3:1} }
603
{Position => Up }
604
sage: print macaulay2("QQ[x,y,z]/(x+y+z)") #optional
605
QQ[x, y, z]
606
-----------
607
x + y + z
608
"""
609
P = self._check_valid()
610
return P.get(self._name)
611
612
repr = __str__
613
614
def external_string(self):
615
"""
616
EXAMPLES:
617
sage: R = macaulay2("QQ[symbol x, symbol y]") #optional
618
sage: R.external_string() #optional
619
'QQ[x..y, Degrees => {2:1}, Heft => {1}, MonomialOrder => VerticalList{MonomialSize => 32, GRevLex => {2:1}, Position => Up}, DegreeRank => 1]'
620
"""
621
P = self._check_valid()
622
code = 'toExternalString(%s)'%self.name()
623
X = P.eval(code, strip=True)
624
625
if 'stdio:' in X:
626
if 'to external string' in X:
627
return P.eval('%s'%self.name())
628
raise RuntimeError, "Error evaluating Macaulay2 code.\nIN:%s\nOUT:%s"%(code, X)
629
630
s = multiple_replace({'\r':'', '\n':' '}, X)
631
return s
632
633
def __len__(self):
634
"""
635
EXAMPLES:
636
sage: l = macaulay2([1,2,3]) #optional
637
sage: len(l) #optional
638
3
639
sage: type(_) #optional
640
<type 'int'>
641
"""
642
self._check_valid()
643
return int(self.parent()("#%s"%self.name()))
644
645
def __getitem__(self, n):
646
"""
647
EXAMPLES:
648
sage: l = macaulay2([1,2,3]) #optional
649
sage: l[0] #optional
650
1
651
"""
652
self._check_valid()
653
n = self.parent()(n)
654
return self.parent().new('%s # %s'%(self.name(), n.name()))
655
656
def __setitem__(self, index, value):
657
"""
658
EXAMPLES:
659
sage: l = macaulay2.new_from("MutableList", [1,2,3]) #optional
660
sage: l[0] = 4 #optional
661
sage: list(l) #optional
662
[4, 2, 3]
663
664
"""
665
P = self.parent()
666
index = P(index)
667
value = P(value)
668
res = P.eval("%s # %s = %s"%(self.name(), index.name(), value.name()))
669
if "assignment attempted to element of immutable list" in res:
670
raise TypeError, "item assignment not supported"
671
672
def __call__(self, x):
673
"""
674
EXAMPLES:
675
sage: R = macaulay2("QQ[x, y]") #optional
676
sage: x,y = R.gens() #optional
677
sage: I = macaulay2.ideal(x*y, x+y) #optional
678
sage: gb = macaulay2.gb #optional
679
sage: gb(I) #optional
680
GroebnerBasis[status: done; S-pairs encountered up to degree 1]
681
"""
682
self._check_valid()
683
P = self.parent()
684
r = P(x)
685
return P('%s %s'%(self.name(), r.name()))
686
687
def __floordiv__(self, x):
688
"""
689
Quotient of division of self by other. This is denoted //.
690
691
EXAMPLE:
692
sage: R.<x,y> = GF(7)[]
693
694
Now make the M2 version of R, so we can coerce elements of R to M2:
695
sage: macaulay2(R) # optional
696
ZZ
697
--[x..y, Degrees => {2:1}, Heft => {1}, MonomialOrder => {MonomialSize => 16}, DegreeRank => 1]
698
7 {GRevLex => {2:1} }
699
{Position => Up }
700
sage: f = (x^3 + 2*y^2*x)^7; f
701
x^21 + 2*x^7*y^14
702
sage: h = macaulay2(f); h # optional
703
21 7 14
704
x + 2x y
705
sage: f1 = (x^2 + 2*y*x) # optional
706
sage: h1 = macaulay2(f1) # optional
707
sage: f2 = (x^3 + 2*y*x) # optional
708
sage: h2 = macaulay2(f2) # optional
709
sage: u = h // [h1,h2] # optional
710
sage: h == u[0]*h1 + u[1]*h2 + (h % [h1,h2]) # optional
711
True
712
"""
713
if isinstance(x, (list, tuple)):
714
y = self.parent(x)
715
z = self.parent().new('%s // matrix{%s}'%(self.name(), y.name()))
716
return list(z.entries().flatten())
717
else:
718
return self.parent().new('%s // %s'%(self.name(), x.name()))
719
720
def __mod__(self, x):
721
"""
722
Remainder of division of self by other. This is denoted %.
723
724
EXAMPLE:
725
sage: R.<x,y> = GF(7)[]
726
727
Now make the M2 version of R, so we can coerce elements of R to M2:
728
sage: macaulay2(R) # optional
729
ZZ
730
--[x..y, Degrees => {2:1}, Heft => {1}, MonomialOrder => {MonomialSize => 16}, DegreeRank => 1]
731
7 {GRevLex => {2:1} }
732
{Position => Up }
733
sage: f = (x^3 + 2*y^2*x)^7; f # optional
734
x^21 + 2*x^7*y^14
735
sage: h = macaulay2(f); print h # optional
736
21 7 14
737
x + 2x y
738
sage: f1 = (x^2 + 2*y*x) # optional
739
sage: h1 = macaulay2(f1) # optional
740
sage: f2 = (x^3 + 2*y*x) # optional
741
sage: h2 = macaulay2(f2) # optional
742
sage: h % [h1,h2] # optional
743
-3x*y
744
sage: u = h // [h1,h2] # optional
745
sage: h == u[0]*h1 + u[1]*h2 + (h % [h1,h2]) # optional
746
True
747
"""
748
if isinstance(x, (list, tuple)):
749
y = self.parent(x)
750
return self.parent().new('%s %% matrix{%s}'%(self.name(), y.name()))
751
if not isinstance(x, Macaulay2Element):
752
x = self.parent(x)
753
return self.parent().new('%s %% %s'%(self.name(), x.name()))
754
755
def __nonzero__(self):
756
"""
757
EXAMPLES:
758
sage: a = macaulay2(0) #optional
759
sage: a == 0 #optional
760
True
761
sage: a.__nonzero__() #optional
762
False
763
"""
764
P = self.parent()
765
return P.eval('%s == 0'%self.name()) == 'false'
766
767
def sage_polystring(self):
768
"""
769
If this Macaulay2 element is a polynomial, return a string
770
representation of this polynomial that is suitable for
771
evaluation in Python. Thus * is used for multiplication
772
and ** for exponentiation. This function is primarily
773
used internally.
774
775
EXAMPLES:
776
sage: R = macaulay2.ring('QQ','(x,y)') # optional
777
sage: f = macaulay2('x^3 + 3*y^11 + 5') # optional
778
sage: print f # optional
779
3 11
780
x + 3y + 5
781
sage: f.sage_polystring() # optional
782
'x**3+3*y**11+5'
783
"""
784
return self.external_string().replace('^','**')
785
786
def structure_sheaf(self):
787
"""
788
EXAMPLES:
789
sage: S = macaulay2('QQ[a..d]') # optional
790
sage: R = S/macaulay2('a^3+b^3+c^3+d^3') # optional
791
sage: X = R.Proj() # optional
792
sage: print X.structure_sheaf() # optional
793
OO
794
sage...
795
"""
796
return self.parent()('OO_%s'%self.name())
797
798
def substitute(self, *args, **kwds):
799
"""
800
Note that we have to override the substitute method so that we get
801
the default one from Macaulay2 instead of the one provided by Element.
802
803
EXAMPLES:
804
sage: R = macaulay2("QQ[x]") #optional
805
sage: P = macaulay2("ZZ/7[symbol x]") #optional
806
sage: x, = R.gens() #optional
807
sage: a = x^2 + 1 #optional
808
sage: a = a.substitute(P) #optional
809
sage: a.to_sage().parent() #optional
810
Univariate Polynomial Ring in x over Finite Field of size 7
811
812
"""
813
return self.__getattr__("substitute")(*args, **kwds)
814
815
subs = substitute
816
817
def trait_names(self):
818
"""
819
Return a list of tab completions for `self``.
820
821
:returns: dynamically built sorted list of commands obtained using
822
Macaulay2 "methods" command. All returned functions can take
823
``self`` as their first argument
824
825
:rtype: list of strings
826
827
TEST:
828
sage: a = macaulay2("QQ[x,y]") # optional
829
sage: traits = a.trait_names() # optional
830
sage: "generators" in traits # optional
831
True
832
"""
833
# It is possible, that these are not all possible methods, but
834
# there are still plenty and at least there are no definitely
835
# wrong ones...
836
r = self.parent().eval(
837
"""currentClass = class %s;
838
total = {};
839
while true do (
840
-- Select methods with first argument of the given class
841
r = select(methods currentClass, s -> s_1 === currentClass);
842
-- Get their names as strings
843
r = apply(r, s -> toString s_0);
844
-- Keep only alpha-numeric ones
845
r = select(r, s -> match("^[[:alnum:]]+$", s));
846
-- Add to existing ones
847
total = total | select(r, s -> not any(total, e -> e == s));
848
if parent currentClass === currentClass then break;
849
currentClass = parent currentClass;
850
)
851
toString total""" % self.name())
852
r = r[1:-1].split(", ")
853
r.sort()
854
return r
855
856
def cls(self):
857
"""
858
Since class is a keyword in Python, we have to use cls to call
859
Macaulay2's class. In Macaulay2, class corresponds to Sage's
860
notion of parent.
861
862
EXAMPLES:
863
sage: macaulay2(ZZ).cls() #optional
864
Ring
865
866
"""
867
return self.parent()("class %s"%self.name())
868
869
##########################
870
#Aliases for M2 operators#
871
##########################
872
def dot(self, x):
873
"""
874
EXAMPLES:
875
sage: d = macaulay2.new("MutableHashTable") #optional
876
sage: d["k"] = 4 #optional
877
sage: d.dot("k") #optional
878
4
879
"""
880
parent = self.parent()
881
x = parent(x)
882
return parent("%s.%s"%(self.name(), x))
883
884
def _operator(self, opstr, x):
885
"""
886
Returns the infix binary operation specified by opstr applied
887
to self and x.
888
889
EXAMPLES:
890
sage: a = macaulay2("3") #optional
891
sage: a._operator("+", a) #optional
892
6
893
sage: a._operator("*", a) #optional
894
9
895
"""
896
parent = self.parent()
897
x = parent(x)
898
return parent("%s%s%s"%(self.name(), opstr, x.name()))
899
900
def sharp(self, x):
901
"""
902
EXAMPLES:
903
sage: a = macaulay2([1,2,3]) #optional
904
sage: a.sharp(0) #optional
905
1
906
"""
907
return self._operator("#", x)
908
909
def starstar(self, x):
910
"""
911
The binary operator ** in Macaulay2 is usually used for tensor
912
or Cartesian power.
913
914
EXAMPLES:
915
sage: a = macaulay2([1,2]).set() #optional
916
sage: a.starstar(a) #optional
917
set {(1, 1), (1, 2), (2, 1), (2, 2)}
918
919
"""
920
return self._operator("**", x)
921
922
def underscore(self, x):
923
"""
924
EXAMPLES:
925
sage: a = macaulay2([1,2,3]) #optional
926
sage: a.underscore(0) #optional
927
1
928
"""
929
return self._operator("_", x)
930
931
####################
932
#Conversion to Sage#
933
####################
934
def to_sage(self):
935
"""
936
EXAMPLES:
937
sage: macaulay2(ZZ).to_sage() #optional
938
Integer Ring
939
sage: macaulay2(QQ).to_sage() #optional
940
Rational Field
941
942
sage: macaulay2(2).to_sage() #optional
943
2
944
sage: macaulay2(1/2).to_sage() #optional
945
1/2
946
sage: macaulay2(2/1).to_sage() #optional
947
2
948
sage: _.parent() #optional
949
Rational Field
950
sage: macaulay2([1,2,3]).to_sage() #optional
951
[1, 2, 3]
952
953
sage: m = matrix([[1,2],[3,4]])
954
sage: macaulay2(m).to_sage() #optional
955
[1 2]
956
[3 4]
957
958
sage: macaulay2(QQ['x,y']).to_sage() #optional
959
Multivariate Polynomial Ring in x, y over Rational Field
960
sage: macaulay2(QQ['x']).to_sage() #optional
961
Univariate Polynomial Ring in x over Rational Field
962
sage: macaulay2(GF(7)['x,y']).to_sage() #optional
963
Multivariate Polynomial Ring in x, y over Finite Field of size 7
964
965
sage: macaulay2(GF(7)).to_sage() #optional
966
Finite Field of size 7
967
sage: macaulay2(GF(49, 'a')).to_sage() #optional
968
Finite Field in a of size 7^2
969
970
sage: R.<x,y> = QQ[]
971
sage: macaulay2(x^2+y^2+1).to_sage() #optional
972
x^2 + y^2 + 1
973
974
sage: R = macaulay2("QQ[x,y]") #optional
975
sage: I = macaulay2("ideal (x,y)") #optional
976
sage: I.to_sage() #optional
977
Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field
978
979
sage: X = R/I #optional
980
sage: X.to_sage() #optional
981
Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x, y)
982
983
sage: R = macaulay2("QQ^2") #optional
984
sage: R.to_sage() #optional
985
Vector space of dimension 2 over Rational Field
986
987
sage: m = macaulay2('"hello"') #optional
988
sage: m.to_sage() #optional
989
'hello'
990
991
"""
992
repr_str = str(self)
993
cls_str = str(self.cls())
994
cls_cls_str = str(self.cls().cls())
995
996
if repr_str == "ZZ":
997
from sage.rings.all import ZZ
998
return ZZ
999
elif repr_str == "QQ":
1000
from sage.rings.all import QQ
1001
return QQ
1002
1003
if cls_cls_str == "Type":
1004
if cls_str == "List":
1005
return [entry.to_sage() for entry in self]
1006
elif cls_str == "Matrix":
1007
from sage.matrix.all import matrix
1008
base_ring = self.ring().to_sage()
1009
entries = self.entries().to_sage()
1010
return matrix(base_ring, entries)
1011
elif cls_str == "Ideal":
1012
parent = self.ring().to_sage()
1013
gens = self.gens().entries().flatten().to_sage()
1014
return parent.ideal(*gens)
1015
elif cls_str == "QuotientRing":
1016
#Handle the ZZ/n case
1017
if "ZZ" in repr_str and "--" in repr_str:
1018
from sage.rings.all import ZZ, GF
1019
external_string = self.external_string()
1020
zz, n = external_string.split("/")
1021
1022
#Note that n must be prime since it is
1023
#coming from Macaulay 2
1024
return GF(ZZ(n))
1025
1026
ambient = self.ambient().to_sage()
1027
ideal = self.ideal().to_sage()
1028
return ambient.quotient(ideal)
1029
elif cls_str == "PolynomialRing":
1030
from sage.rings.all import PolynomialRing
1031
from sage.rings.polynomial.term_order import inv_macaulay2_name_mapping
1032
1033
#Get the base ring
1034
base_ring = self.coefficientRing().to_sage()
1035
1036
#Get a string list of generators
1037
gens = str(self.gens())[1:-1]
1038
1039
# Check that we are dealing with default degrees, i.e. 1's.
1040
if self.degrees().any("x -> x != {1}").to_sage():
1041
raise ValueError, "cannot convert Macaulay2 polynomial ring with non-default degrees to Sage"
1042
#Handle the term order
1043
external_string = self.external_string()
1044
order = None
1045
if "MonomialOrder" not in external_string:
1046
order = "degrevlex"
1047
else:
1048
for order_name in inv_macaulay2_name_mapping:
1049
if order_name in external_string:
1050
order = inv_macaulay2_name_mapping[order_name]
1051
if len(gens) > 1 and order is None:
1052
raise ValueError, "cannot convert Macaulay2's term order to a Sage term order"
1053
1054
return PolynomialRing(base_ring, order=order, names=gens)
1055
elif cls_str == "GaloisField":
1056
from sage.rings.all import ZZ, GF
1057
gf, n = repr_str.split(" ")
1058
n = ZZ(n)
1059
if n.is_prime():
1060
return GF(n)
1061
else:
1062
gen = str(self.gens())[1:-1]
1063
return GF(n, gen)
1064
elif cls_str == "Boolean":
1065
if repr_str == "true":
1066
return True
1067
elif repr_str == "false":
1068
return False
1069
elif cls_str == "String":
1070
return str(repr_str)
1071
elif cls_str == "Module":
1072
from sage.modules.all import FreeModule
1073
if self.isFreeModule().to_sage():
1074
ring = self.ring().to_sage()
1075
rank = self.rank().to_sage()
1076
return FreeModule(ring, rank)
1077
else:
1078
#Handle the integers and rationals separately
1079
if cls_str == "ZZ":
1080
from sage.rings.all import ZZ
1081
return ZZ(repr_str)
1082
elif cls_str == "QQ":
1083
from sage.rings.all import QQ
1084
repr_str = self.external_string()
1085
if "/" not in repr_str:
1086
repr_str = repr_str + "/1"
1087
return QQ(repr_str)
1088
1089
m2_parent = self.cls()
1090
parent = m2_parent.to_sage()
1091
1092
if cls_cls_str == "PolynomialRing":
1093
from sage.misc.sage_eval import sage_eval
1094
gens_dict = parent.gens_dict()
1095
return sage_eval(self.external_string(), gens_dict)
1096
1097
from sage.misc.sage_eval import sage_eval
1098
try:
1099
return sage_eval(repr_str)
1100
except:
1101
raise NotImplementedError, "cannot convert %s to a Sage object"%repr_str
1102
1103
1104
class Macaulay2Function(ExpectFunction):
1105
def _sage_doc_(self):
1106
"""
1107
EXAMPLES:
1108
sage: print macaulay2.load._sage_doc_() # optional
1109
load -- read Macaulay2 commands
1110
*******************************
1111
...
1112
* "input" -- read Macaulay2 commands and echo
1113
* "notify" -- whether to notify the user when a file is loaded
1114
"""
1115
return self._parent.help(self._name)
1116
1117
def _sage_src_(self):
1118
"""
1119
EXAMPLES:
1120
sage: print macaulay2.gb._sage_src_() #optional
1121
code(methods gb)
1122
...
1123
"""
1124
if self._parent._expect is None:
1125
self._parent._start()
1126
E = self._parent._expect
1127
E.sendline("code(methods %s)"%self._name)
1128
E.expect(self._parent._prompt)
1129
s = E.before
1130
self._parent.eval("2+2")
1131
return s
1132
1133
def is_Macaulay2Element(x):
1134
"""
1135
EXAMPLES:
1136
sage: from sage.interfaces.macaulay2 import is_Macaulay2Element
1137
sage: is_Macaulay2Element(2) #optional
1138
False
1139
sage: is_Macaulay2Element(macaulay2(2)) #optional
1140
True
1141
"""
1142
return isinstance(x, Macaulay2Element)
1143
1144
# An instance
1145
macaulay2 = Macaulay2(script_subdirectory='user')
1146
1147
import os
1148
1149
def macaulay2_console():
1150
"""
1151
Spawn a new M2 command-line session.
1152
1153
EXAMPLES:
1154
sage: macaulay2_console() # not tested
1155
Macaulay 2, version 1.1
1156
with packages: Classic, Core, Elimination, IntegralClosure, LLLBases, Parsing, PrimaryDecomposition, SchurRings, TangentCone
1157
...
1158
1159
"""
1160
os.system('M2')
1161
1162
1163
1164
def reduce_load_macaulay2():
1165
"""
1166
Used for reconstructing a copy of the Macaulay2 interpreter from a pickle.
1167
1168
EXAMPLES:
1169
sage: from sage.interfaces.macaulay2 import reduce_load_macaulay2
1170
sage: reduce_load_macaulay2()
1171
Macaulay2
1172
"""
1173
return macaulay2
1174
1175
1176