Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/interfaces/frobby.py
4049 views
1
r"""
2
Interface to Frobby for fast computations on monomial ideals.
3
4
The software package Frobby provides a number of computations on
5
monomial ideals. The current main feature is the socle of a monomial
6
ideal, which is largely equivalent to computing the maximal standard
7
monomials, the Alexander dual or the irreducible decomposition.
8
9
Operations on monomial ideals are much faster than algorithms designed
10
for ideals in general, which is what makes a specialized library for
11
these operations on monomial ideals useful.
12
13
AUTHORS:
14
-- Bjarke Hammersholt Roune (2008-04-25): Wrote the Frobby C++
15
program and the initial version of the Python interface.
16
17
NOTES:
18
The official source for Frobby is \url{http://www.broune.com/frobby},
19
which also has documentation and papers describing the algorithms used.
20
"""
21
22
from subprocess import *
23
24
class Frobby:
25
def __call__(self, action, input=None, options=[], verbose=False):
26
r"""
27
This function calls Frobby as a command line program using streams
28
for input and output. Strings passed as part of the command get
29
broken up at whitespace. This is not done to the data passed
30
via the streams.
31
32
INPUT:
33
action -- A string telling Frobby what to do.
34
input -- None or a string that is passed to Frobby as standard in.
35
options -- A list of options without the dash in front.
36
verbose -- bool (default: false) Print detailed information.
37
OUTPUT:
38
string -- What Frobby wrote to the standard output stream.
39
40
EXAMPLES:
41
We compute the lcm of an ideal provided in Monos format.
42
sage: frobby("analyze", input="vars x,y,z;[x^2,x*y];", #optional
43
... options=["lcm", "iformat monos", "oformat 4ti2"]) #optional
44
'x^2*y\n'
45
46
We get an exception if frobby reports an error.
47
sage: frobby("do_dishes") #optional
48
Traceback (most recent call last):
49
...
50
RuntimeError: Frobby reported an error:
51
ERROR: Unknown action "do_dishes".
52
53
AUTHOR:
54
- Bjarke Hammersholt Roune (2008-04-27)
55
"""
56
command = ['frobby'] + action.split()
57
for option in options:
58
command += ('-' + option.strip()).split()
59
60
if verbose:
61
print "Frobby action: ", action
62
print "Frobby options: ", `options`
63
print "Frobby command: ", `command`
64
print "Frobby input:\n", input
65
66
process = Popen(command, stdin = PIPE, stdout = PIPE, stderr = PIPE)
67
output, err = process.communicate(input = input)
68
69
if verbose:
70
print "Frobby output:\n", output
71
print "Frobby error:\n", err
72
if process.poll() != 0:
73
raise RuntimeError("Frobby reported an error:\n" + err)
74
75
return output
76
77
def irreducible_decomposition(self, monomial_ideal):
78
r"""
79
This function computes the irreducible decomposition of the passed-in
80
monomial ideal. I.e. it computes the unique minimal list of
81
irreducible monomial ideals whose intersection equals monomial_ideal.
82
83
INPUT:
84
monomial_ideal -- The monomial ideal to decompose.
85
86
OUTPUT:
87
A list of the unique irredundant irreducible components of
88
monomial_ideal. These ideals are constructed in the same ring
89
as monomial_ideal is.
90
91
EXAMPLES:
92
This is a simple example of computing irreducible decomposition.
93
94
sage: (x, y, z) = QQ['x,y,z'].gens() #optional
95
sage: id = ideal(x ** 2, y ** 2, x * z, y * z) #optional
96
sage: decom = frobby.irreducible_decomposition(id) #optional
97
sage: true_decom = [ideal(x, y), ideal(x ** 2, y ** 2, z)] #optional
98
sage: set(decom) == set(true_decom) # use sets to ignore order #optional
99
True
100
101
We now try the special case of the zero ideal in different rings.
102
103
We should also try PolynomialRing(QQ, names=[]), but it has a bug
104
which makes that impossible (see trac ticket 3028).
105
sage: rings = [ZZ['x'], CC['x,y']] #optional
106
sage: allOK = True #optional
107
sage: for ring in rings: #optional
108
... id0 = ring.ideal(0) #optional
109
... decom0 = frobby.irreducible_decomposition(id0) #optional
110
... allOK = allOK and decom0 == [id0] #optional
111
sage: allOK #optional
112
True
113
114
Finally, we try the ideal that is all of the ring in different
115
rings.
116
sage: allOK = True #optional
117
sage: for ring in rings: #optional
118
... id1 = ring.ideal(1) #optional
119
... decom1 = frobby.irreducible_decomposition(id1) #optional
120
... allOK = allOK and decom1 == [id1] #optional
121
sage: allOK #optional
122
True
123
"""
124
frobby_input = self._ideal_to_string(monomial_ideal)
125
frobby_output = self('irrdecom', input=frobby_input)
126
return self._parse_ideals(frobby_output, monomial_ideal.ring())
127
128
def _parse_ideals(self, string, ring):
129
r"""
130
This function parses a list of irreducible monomial ideals in 4ti2
131
format and constructs them within the passed-in ring.
132
133
INPUT:
134
string -- The string to be parsed.
135
ring -- The ring within which to construct the irreducible
136
monomial ideals within.
137
138
OUTPUT:
139
A list of the monomial ideals in the order they are listed
140
in the string.
141
142
EXAMPLES:
143
sage: ring = QQ['x,y,z'] #optional
144
sage: (x, y, z) = ring.gens() #optional
145
sage: string = '2 3\n1 2 3\n0 5 0' #optional
146
sage: parsed_ideals = frobby._parse_ideals(string, ring) #optional
147
sage: reference_ideals = [ring.ideal(x, y ** 2, z ** 3), #optional
148
... ring.ideal(y ** 5)] #optional #optional
149
sage: parsed_ideals == reference_ideals #optional
150
True
151
"""
152
def to_ideal(exps):
153
gens = [v ** e for v, e in zip(ring.gens(), exps) if e != 0]
154
return ring.ideal(gens or ring(1))
155
return map(to_ideal, self._parse_4ti2_matrix(string)) or [ring.ideal()]
156
157
def _parse_4ti2_matrix(self, string):
158
r"""
159
Parses a string of a matrix in 4ti2 format into a nested list
160
representation.
161
162
INPUT:
163
string - The string to be parsed.
164
165
OUTPUT:
166
A list of rows of the matrix, where each row is represented as
167
a list of integers.
168
169
EXAMPLES:
170
The format is straight-forward, as this example shows.
171
sage: string = '2 3\n1 2 3\n 0 5 0' #optional
172
sage: parsed_matrix = frobby._parse_4ti2_matrix(string) #optional
173
sage: reference_matrix = [[1, 2, 3], [0, 5, 0]] #optional
174
sage: parsed_matrix == reference_matrix #optional
175
True
176
177
A number of syntax errors lead to exceptions.
178
sage: string = '1 1\n one' #optional
179
sage: frobby._parse_4ti2_matrix(string) #optional
180
Traceback (most recent call last):
181
...
182
RuntimeError: Format error: encountered non-number.
183
"""
184
try:
185
ints = map(int, string.split())
186
except ValueError:
187
raise RuntimeError("Format error: encountered non-number.")
188
if len(ints) < 2:
189
raise RuntimeError("Format error: " +
190
"matrix dimensions not specified.")
191
192
term_count = ints[0]
193
var_count = ints[1]
194
ints = ints[2:]
195
196
if term_count * var_count != len(ints):
197
raise RuntimeError("Format error: incorrect matrix dimensions.")
198
199
exponents = []
200
for i in range(term_count):
201
exponents.append(ints[:var_count])
202
ints = ints[var_count:]
203
return exponents;
204
205
def _ideal_to_string(self, monomial_ideal):
206
r"""
207
This function formats the passed-in monomial ideal in 4ti2 format.
208
209
INPUT:
210
monomial_ideal -- The monomial ideal to be formatted as a string.
211
212
OUTPUT:
213
A string in 4ti2 format representing the ideal.
214
215
EXAMPLES:
216
sage: ring = QQ['x,y,z'] #optional
217
sage: (x, y, z) = ring.gens() #optional
218
sage: id = ring.ideal(x ** 2, x * y * z) #optional
219
sage: frobby._ideal_to_string(id) == "2 3\n2 0 0\n1 1 1\n" #optional
220
True
221
"""
222
# There is no exponent vector that represents zero as a generator, so
223
# we take care that the zero ideal gets represented correctly in the
224
# 4ti2 format; as an ideal with no generators.
225
if monomial_ideal.is_zero():
226
gens = []
227
else:
228
gens = monomial_ideal.gens();
229
var_count = monomial_ideal.ring().ngens();
230
first_row = str(len(gens)) + ' ' + str(var_count) + '\n'
231
rows = map(self._monomial_to_string, gens);
232
return first_row + "".join(rows)
233
234
def _monomial_to_string(self, monomial):
235
r"""
236
This function formats the exponent vector of a monomial as a string
237
containing a space-delimited list of integers.
238
239
INPUT:
240
monomial -- The monomial whose exponent vector is to be formatted.
241
242
OUTPUT:
243
A string representing the exponent vector of monomial.
244
245
EXAMPLES:
246
sage: ring = QQ['x,y,z'] #optional
247
sage: (x, y, z) = ring.gens() #optional
248
sage: monomial = x * x * z #optional
249
sage: frobby._monomial_to_string(monomial) == '2 0 1\n' #optional
250
True
251
"""
252
exponents = monomial.exponents()
253
if len(exponents) != 1:
254
raise RuntimeError(str(monomial) + " is not a monomial.")
255
exponents = exponents[0]
256
257
# for a multivariate ring exponents will be an ETuple, while
258
# for a univariate ring exponents will be just an int. To get
259
# this to work we make the two cases look alike.
260
if isinstance(exponents, int):
261
exponents = [exponents]
262
strings = [str(exponents[var]) for var in range(len(exponents))]
263
return ' '.join(strings) + '\n'
264
265
# This singleton instance is what should be used outside this file.
266
frobby = Frobby()
267
268