Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/interfaces/frobby.py
8814 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
from sage.misc.misc_c import prod
24
25
class Frobby:
26
def __call__(self, action, input=None, options=[], verbose=False):
27
r"""
28
This function calls Frobby as a command line program using streams
29
for input and output. Strings passed as part of the command get
30
broken up at whitespace. This is not done to the data passed
31
via the streams.
32
33
INPUT:
34
action -- A string telling Frobby what to do.
35
input -- None or a string that is passed to Frobby as standard in.
36
options -- A list of options without the dash in front.
37
verbose -- bool (default: false) Print detailed information.
38
OUTPUT:
39
string -- What Frobby wrote to the standard output stream.
40
41
EXAMPLES:
42
We compute the lcm of an ideal provided in Monos format.
43
sage: frobby("analyze", input="vars x,y,z;[x^2,x*y];", # optional - frobby
44
... options=["lcm", "iformat monos", "oformat 4ti2"]) # optional - frobby
45
' 2 1 0\n\n2 generators\n3 variables\n'
46
47
48
We get an exception if frobby reports an error.
49
sage: frobby("do_dishes") # optional - frobby
50
Traceback (most recent call last):
51
...
52
RuntimeError: Frobby reported an error:
53
ERROR: No action has the prefix "do_dishes".
54
55
AUTHOR:
56
- Bjarke Hammersholt Roune (2008-04-27)
57
"""
58
command = ['frobby'] + action.split()
59
for option in options:
60
command += ('-' + option.strip()).split()
61
62
if verbose:
63
print "Frobby action: ", action
64
print "Frobby options: ", `options`
65
print "Frobby command: ", `command`
66
print "Frobby input:\n", input
67
68
process = Popen(command, stdin = PIPE, stdout = PIPE, stderr = PIPE)
69
output, err = process.communicate(input = input)
70
71
if verbose:
72
print "Frobby output:\n", output
73
print "Frobby error:\n", err
74
if process.poll() != 0:
75
raise RuntimeError("Frobby reported an error:\n" + err)
76
77
return output
78
79
def alexander_dual(self, monomial_ideal):
80
r"""
81
This function computes the Alexander dual of the passed-in
82
monomial ideal. This ideal is the one corresponding to the
83
simplicial complex whose faces are the complements of the
84
nonfaces of the simplicial complex corresponding to the input
85
ideal.
86
87
INPUT:
88
monomial_ideal -- The monomial ideal to decompose.
89
90
OUTPUT:
91
The monomial corresponding to the Alexander dual.
92
93
EXAMPLES:
94
This is a simple example of computing irreducible decomposition.
95
96
sage: (a, b, c, d) = QQ['a,b,c,d'].gens() # optional - frobby
97
sage: id = ideal(a * b, b * c, c * d, d * a) # optional - frobby
98
sage: alexander_dual = frobby.alexander_dual(id) # optional - frobby
99
sage: true_alexander_dual = ideal(b * d, a * c) # optional - frobby
100
sage: alexander_dual == true_alexander_dual # use sets to ignore order # optional - frobby
101
True
102
103
We see how it is much faster to compute this with frobby than the built-in
104
procedure for simplicial complexes.
105
106
sage: t=simplicial_complexes.PoincareHomologyThreeSphere() # optional - frobby
107
sage: R=PolynomialRing(QQ,16,'x') # optional - frobby
108
sage: I=R.ideal([prod([R.gen(i-1) for i in a]) for a in t.facets()]) # optional - frobby
109
sage: len(frobby.alexander_dual(I).gens()) # optional - frobby
110
643
111
112
113
"""
114
frobby_input = self._ideal_to_string(monomial_ideal)
115
frobby_output = self('alexdual', input=frobby_input)
116
return self._parse_ideals(frobby_output, monomial_ideal.ring())[0]
117
118
def hilbert(self, monomial_ideal):
119
r"""
120
Computes the multigraded Hilbert-Poincare series of the input
121
ideal. Use the -univariate option to get the univariate series.
122
123
The Hilbert-Poincare series of a monomial ideal is the sum of all
124
monomials not in the ideal. This sum can be written as a (finite)
125
rational function with $(x_1-1)(x_2-1)...(x_n-1)$ in the denominator,
126
assuming the variables of the ring are $x_1,x2,...,x_n$. This action
127
computes the polynomial in the numerator of this fraction.
128
129
INPUT:
130
131
monomial_ideal -- A monomial ideal.
132
133
OUTPUT:
134
135
A polynomial in the same ring as the ideal.
136
137
EXAMPLES::
138
139
sage: R.<d,b,c>=QQ[] # optional - frobby
140
sage: I=[d*b*c,b^2*c,b^10,d^10]*R # optional - frobby
141
sage: frobby.hilbert(I) # optional - frobby
142
d^10*b^10*c + d^10*b^10 + d^10*b*c + b^10*c + d^10 + b^10 + d*b^2*c + d*b*c + b^2*c + 1
143
144
"""
145
frobby_input = self._ideal_to_string(monomial_ideal)
146
frobby_output = self('hilbert', input=frobby_input)
147
ring=monomial_ideal.ring()
148
lines=frobby_output.split('\n')
149
if lines[-1]=='':
150
lines.pop(-1)
151
if lines[-1]=='(coefficient)':
152
lines.pop(-1)
153
lines.pop(0)
154
resul=0
155
for l in lines:
156
lis=map(int,l.split())
157
resul+=lis[0]+prod([ring.gen(i)**lis[i+1] for i in range(len(lis)-1)])
158
return resul
159
160
def associated_primes(self, monomial_ideal):
161
r"""
162
This function computes the associated primes of the passed-in
163
monomial ideal.
164
165
INPUT:
166
monomial_ideal -- The monomial ideal to decompose.
167
168
OUTPUT:
169
A list of the associated primes of the monomial ideal. These ideals
170
are constructed in the same ring as monomial_ideal is.
171
172
EXAMPLES::
173
174
sage: R.<d,b,c>=QQ[] # optional - frobby
175
sage: I=[d*b*c,b^2*c,b^10,d^10]*R # optional - frobby
176
sage: frobby.associated_primes(I) # optional - frobby
177
[Ideal (d, b) of Multivariate Polynomial Ring in d, b, c over Rational Field,
178
Ideal (d, b, c) of Multivariate Polynomial Ring in d, b, c over Rational Field]
179
180
"""
181
frobby_input = self._ideal_to_string(monomial_ideal)
182
frobby_output = self('assoprimes', input=frobby_input)
183
lines=frobby_output.split('\n')
184
lines.pop(0)
185
if lines[-1]=='':
186
lines.pop(-1)
187
lists=[map(int,a.split()) for a in lines]
188
def to_monomial(exps):
189
return [v ** e for v, e in zip(monomial_ideal.ring().gens(), exps) if e != 0]
190
return [monomial_ideal.ring().ideal(to_monomial(a)) for a in lists]
191
192
def dimension(self, monomial_ideal):
193
r"""
194
This function computes the dimension of the passed-in
195
monomial ideal.
196
197
INPUT:
198
monomial_ideal -- The monomial ideal to decompose.
199
200
OUTPUT:
201
The dimension of the zero set of the ideal.
202
203
EXAMPLES::
204
205
sage: R.<d,b,c>=QQ[] # optional - frobby
206
sage: I=[d*b*c,b^2*c,b^10,d^10]*R # optional - frobby
207
sage: frobby.dimension(I) # optional - frobby
208
1
209
210
"""
211
frobby_input = self._ideal_to_string(monomial_ideal)
212
frobby_output = self('dimension', input=frobby_input)
213
return int(frobby_output)
214
215
def irreducible_decomposition(self, monomial_ideal):
216
r"""
217
This function computes the irreducible decomposition of the passed-in
218
monomial ideal. I.e. it computes the unique minimal list of
219
irreducible monomial ideals whose intersection equals monomial_ideal.
220
221
INPUT:
222
monomial_ideal -- The monomial ideal to decompose.
223
224
OUTPUT:
225
A list of the unique irredundant irreducible components of
226
monomial_ideal. These ideals are constructed in the same ring
227
as monomial_ideal is.
228
229
EXAMPLES::
230
This is a simple example of computing irreducible decomposition.
231
232
sage: (x, y, z) = QQ['x,y,z'].gens() # optional - frobby
233
sage: id = ideal(x ** 2, y ** 2, x * z, y * z) # optional - frobby
234
sage: decom = frobby.irreducible_decomposition(id) # optional - frobby
235
sage: true_decom = [ideal(x, y), ideal(x ** 2, y ** 2, z)] # optional - frobby
236
sage: set(decom) == set(true_decom) # use sets to ignore order # optional - frobby
237
True
238
239
We now try the special case of the zero ideal in different rings.
240
241
We should also try PolynomialRing(QQ, names=[]), but it has a bug
242
which makes that impossible (see trac ticket 3028).
243
sage: rings = [ZZ['x'], CC['x,y']] # optional - frobby
244
sage: allOK = True # optional - frobby
245
sage: for ring in rings: # optional - frobby
246
... id0 = ring.ideal(0) # optional - frobby
247
... decom0 = frobby.irreducible_decomposition(id0) # optional - frobby
248
... allOK = allOK and decom0 == [id0] # optional - frobby
249
sage: allOK # optional - frobby
250
True
251
252
Finally, we try the ideal that is all of the ring in different
253
rings.
254
sage: rings = [ZZ['x'], CC['x,y']] # optional - frobby
255
sage: allOK = True # optional - frobby
256
sage: for ring in rings: # optional - frobby
257
... id1 = ring.ideal(1) # optional - frobby
258
... decom1 = frobby.irreducible_decomposition(id1) # optional - frobby
259
... allOK = allOK and decom1 == [id1] # optional - frobby
260
sage: allOK # optional - frobby
261
True
262
"""
263
frobby_input = self._ideal_to_string(monomial_ideal)
264
frobby_output = self('irrdecom', input=frobby_input)
265
return self._parse_ideals(frobby_output, monomial_ideal.ring())
266
267
def _parse_ideals(self, string, ring):
268
r"""
269
This function parses a list of irreducible monomial ideals in 4ti2
270
format and constructs them within the passed-in ring.
271
272
INPUT:
273
string -- The string to be parsed.
274
ring -- The ring within which to construct the irreducible
275
monomial ideals within.
276
277
OUTPUT:
278
A list of the monomial ideals in the order they are listed
279
in the string.
280
281
EXAMPLES::
282
sage: ring = QQ['x,y,z'] # optional - frobby
283
sage: (x, y, z) = ring.gens() # optional - frobby
284
sage: string = '2 3\n1 2 3\n0 5 0\n2 3\n1 2 3\n4 5 6' # optional - frobby
285
sage: frobby._parse_ideals(string, ring) # optional - frobby
286
[Ideal (x*y^2*z^3, y^5) of Multivariate Polynomial Ring in x, y, z over Rational Field,
287
Ideal (x*y^2*z^3, x^4*y^5*z^6) of Multivariate Polynomial Ring in x, y, z over Rational Field]
288
289
"""
290
lines=string.split('\n')
291
if lines[-1]=='':
292
lines.pop(-1)
293
matrices=[]
294
while len(lines)>0:
295
if lines[0].split()[1]=='ring':
296
lines.pop(0)
297
lines.pop(0)
298
matrices.append('1 '+str(ring.ngens())+'\n'+'0 '*ring.ngens()+'\n')
299
else:
300
nrows=int(lines[0].split()[0])
301
nmatrix=lines.pop(0)+'\n'
302
for i in range(nrows):
303
nmatrix+=lines.pop(0)+'\n'
304
matrices.append(nmatrix)
305
def to_ideal(exps):
306
if len(exps)==0:
307
return ring.zero_ideal()
308
gens = [prod([v ** e for v, e in zip(ring.gens(), expo) if e != 0]) for expo in exps]
309
return ring.ideal(gens or ring(1))
310
return [to_ideal(self._parse_4ti2_matrix(a)) for a in matrices] or [ring.ideal()]
311
312
def _parse_4ti2_matrix(self, string):
313
r"""
314
Parses a string of a matrix in 4ti2 format into a nested list
315
representation.
316
317
INPUT:
318
string - The string to be parsed.
319
320
OUTPUT:
321
A list of rows of the matrix, where each row is represented as
322
a list of integers.
323
324
EXAMPLES::
325
The format is straight-forward, as this example shows.
326
sage: string = '2 3\n1 2 3\n 0 5 0' # optional - frobby
327
sage: parsed_matrix = frobby._parse_4ti2_matrix(string) # optional - frobby
328
sage: reference_matrix = [[1, 2, 3], [0, 5, 0]] # optional - frobby
329
sage: parsed_matrix == reference_matrix # optional - frobby
330
True
331
332
A number of syntax errors lead to exceptions.
333
sage: string = '1 1\n one' # optional - frobby
334
sage: frobby._parse_4ti2_matrix(string) # optional - frobby
335
Traceback (most recent call last):
336
...
337
RuntimeError: Format error: encountered non-number.
338
"""
339
try:
340
ints = map(int, string.split())
341
except ValueError:
342
raise RuntimeError("Format error: encountered non-number.")
343
if len(ints) < 2:
344
raise RuntimeError("Format error: " +
345
"matrix dimensions not specified.")
346
347
term_count = ints[0]
348
var_count = ints[1]
349
ints = ints[2:]
350
351
if term_count * var_count != len(ints):
352
raise RuntimeError("Format error: incorrect matrix dimensions.")
353
354
exponents = []
355
for i in range(term_count):
356
exponents.append(ints[:var_count])
357
ints = ints[var_count:]
358
return exponents;
359
360
def _ideal_to_string(self, monomial_ideal):
361
r"""
362
This function formats the passed-in monomial ideal in 4ti2 format.
363
364
INPUT:
365
monomial_ideal -- The monomial ideal to be formatted as a string.
366
367
OUTPUT:
368
A string in 4ti2 format representing the ideal.
369
370
EXAMPLES::
371
sage: ring = QQ['x,y,z'] # optional - frobby
372
sage: (x, y, z) = ring.gens() # optional - frobby
373
sage: id = ring.ideal(x ** 2, x * y * z) # optional - frobby
374
sage: frobby._ideal_to_string(id) == "2 3\n2 0 0\n1 1 1\n" # optional - frobby
375
True
376
"""
377
# There is no exponent vector that represents zero as a generator, so
378
# we take care that the zero ideal gets represented correctly in the
379
# 4ti2 format; as an ideal with no generators.
380
if monomial_ideal.is_zero():
381
gens = []
382
else:
383
gens = monomial_ideal.gens();
384
var_count = monomial_ideal.ring().ngens();
385
first_row = str(len(gens)) + ' ' + str(var_count) + '\n'
386
rows = map(self._monomial_to_string, gens);
387
return first_row + "".join(rows)
388
389
def _monomial_to_string(self, monomial):
390
r"""
391
This function formats the exponent vector of a monomial as a string
392
containing a space-delimited list of integers.
393
394
INPUT:
395
monomial -- The monomial whose exponent vector is to be formatted.
396
397
OUTPUT:
398
A string representing the exponent vector of monomial.
399
400
EXAMPLES::
401
sage: ring = QQ['x,y,z'] # optional - frobby
402
sage: (x, y, z) = ring.gens() # optional - frobby
403
sage: monomial = x * x * z # optional - frobby
404
sage: frobby._monomial_to_string(monomial) == '2 0 1\n' # optional - frobby
405
True
406
"""
407
exponents = monomial.exponents()
408
if len(exponents) != 1:
409
raise RuntimeError(str(monomial) + " is not a monomial.")
410
exponents = exponents[0]
411
412
# for a multivariate ring exponents will be an ETuple, while
413
# for a univariate ring exponents will be just an int. To get
414
# this to work we make the two cases look alike.
415
if isinstance(exponents, int):
416
exponents = [exponents]
417
strings = [str(exponents[var]) for var in range(len(exponents))]
418
return ' '.join(strings) + '\n'
419
420
# This singleton instance is what should be used outside this file.
421
frobby = Frobby()
422
423