passagemath-gfan 10.6.32__cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-gfan might be problematic. Click here for more details.
- passagemath_gfan-10.6.32.dist-info/METADATA +92 -0
- passagemath_gfan-10.6.32.dist-info/RECORD +15 -0
- passagemath_gfan-10.6.32.dist-info/WHEEL +6 -0
- passagemath_gfan-10.6.32.dist-info/top_level.txt +2 -0
- passagemath_gfan.libs/libcddgmp-21acf0c6.so.0.1.3 +0 -0
- passagemath_gfan.libs/libgmp-6e109695.so.10.5.0 +0 -0
- sage/all__sagemath_gfan.py +4 -0
- sage/interfaces/all__sagemath_gfan.py +1 -0
- sage/libs/all__sagemath_gfan.py +1 -0
- sage/libs/gfan.cpython-314t-x86_64-linux-gnu.so +0 -0
- sage/libs/gfan.pyx +1 -0
- sage/rings/all__sagemath_gfan.py +1 -0
- sage/rings/polynomial/all__sagemath_gfan.py +1 -0
- sage/rings/polynomial/groebner_fan.py +1899 -0
- sage_wheels/bin/gfan +0 -0
|
@@ -0,0 +1,1899 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-gfan
|
|
2
|
+
# sage.doctest: optional - gfan
|
|
3
|
+
r"""
|
|
4
|
+
Groebner Fans
|
|
5
|
+
|
|
6
|
+
Sage provides much of the functionality of ``gfan``, which is a
|
|
7
|
+
software package whose main function is to enumerate all reduced
|
|
8
|
+
Groebner bases of a polynomial ideal. The reduced Groebner bases
|
|
9
|
+
yield the maximal cones in the Groebner fan of the ideal. Several
|
|
10
|
+
subcomputations can be issued and additional tools are included.
|
|
11
|
+
Among these the highlights are:
|
|
12
|
+
|
|
13
|
+
- Commands for computing tropical varieties.
|
|
14
|
+
|
|
15
|
+
- Interactive walks in the Groebner fan of an ideal.
|
|
16
|
+
|
|
17
|
+
- Commands for graphical renderings of Groebner fans and monomial
|
|
18
|
+
ideals.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
AUTHORS:
|
|
22
|
+
|
|
23
|
+
- Anders Nedergaard Jensen: Wrote the ``gfan`` C++ program, which
|
|
24
|
+
implements algorithms many of which were invented by Jensen, Komei
|
|
25
|
+
Fukuda, and Rekha Thomas. All the underlying hard work of the
|
|
26
|
+
Groebner fans functionality of Sage depends on this C++ program.
|
|
27
|
+
|
|
28
|
+
- William Stein (2006-04-20): Wrote first version of the Sage code
|
|
29
|
+
for working with Groebner fans.
|
|
30
|
+
|
|
31
|
+
- Tristram Bogart: the design of the Sage interface
|
|
32
|
+
to ``gfan`` is joint work with Tristram Bogart, who also supplied
|
|
33
|
+
numerous examples.
|
|
34
|
+
|
|
35
|
+
- Marshall Hampton (2008-03-25): Rewrote various functions to use
|
|
36
|
+
``gfan-0.3``. This is still a work in progress, comments are
|
|
37
|
+
appreciated on sage-devel@googlegroups.com (or personally at
|
|
38
|
+
hamptonio@gmail.com).
|
|
39
|
+
|
|
40
|
+
EXAMPLES::
|
|
41
|
+
|
|
42
|
+
sage: x,y = QQ['x,y'].gens()
|
|
43
|
+
sage: i = ideal(x^2 - y^2 + 1)
|
|
44
|
+
sage: g = i.groebner_fan()
|
|
45
|
+
sage: g.reduced_groebner_bases()
|
|
46
|
+
[[x^2 - y^2 + 1], [-x^2 + y^2 - 1]]
|
|
47
|
+
|
|
48
|
+
TESTS::
|
|
49
|
+
|
|
50
|
+
sage: x,y = QQ['x,y'].gens()
|
|
51
|
+
sage: i = ideal(x^2 - y^2 + 1)
|
|
52
|
+
sage: g = i.groebner_fan()
|
|
53
|
+
sage: g == loads(dumps(g))
|
|
54
|
+
True
|
|
55
|
+
|
|
56
|
+
REFERENCES:
|
|
57
|
+
|
|
58
|
+
- Anders N. Jensen; *Gfan, a software system for Groebner fans*;
|
|
59
|
+
http://home.math.au.dk/jensen/software/gfan/gfan.html
|
|
60
|
+
"""
|
|
61
|
+
from subprocess import PIPE, Popen
|
|
62
|
+
import pexpect
|
|
63
|
+
import re
|
|
64
|
+
import string
|
|
65
|
+
from typing import Iterator
|
|
66
|
+
|
|
67
|
+
from sage.structure.sage_object import SageObject
|
|
68
|
+
from sage.interfaces.gfan import gfan
|
|
69
|
+
from .multi_polynomial_ideal import MPolynomialIdeal
|
|
70
|
+
from .polynomial_ring_constructor import PolynomialRing
|
|
71
|
+
from sage.rings.rational_field import QQ
|
|
72
|
+
from sage.rings.integer import Integer
|
|
73
|
+
from sage.rings.integer_ring import ZZ
|
|
74
|
+
from sage.modules.free_module_element import vector
|
|
75
|
+
from sage.misc.lazy_import import lazy_import
|
|
76
|
+
lazy_import("sage.plot.all", ["line", "Graphics", "polygon"])
|
|
77
|
+
lazy_import("sage.plot.plot3d.shapes2", "line3d")
|
|
78
|
+
from sage.geometry.polyhedron.constructor import Polyhedron
|
|
79
|
+
from sage.geometry.fan import Fan
|
|
80
|
+
from sage.matrix.constructor import matrix
|
|
81
|
+
from sage.misc.cachefunc import cached_method
|
|
82
|
+
from sage.misc.misc_c import prod
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def prefix_check(str_list) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
Check if any strings in a list are prefixes of another string in
|
|
88
|
+
the list.
|
|
89
|
+
|
|
90
|
+
EXAMPLES::
|
|
91
|
+
|
|
92
|
+
sage: from sage.rings.polynomial.groebner_fan import prefix_check
|
|
93
|
+
sage: prefix_check(['z1','z1z1'])
|
|
94
|
+
False
|
|
95
|
+
sage: prefix_check(['z1','zz1'])
|
|
96
|
+
True
|
|
97
|
+
"""
|
|
98
|
+
for index1, string1 in enumerate(str_list):
|
|
99
|
+
for index2, string2 in enumerate(str_list):
|
|
100
|
+
if index1 != index2:
|
|
101
|
+
if string1[:len(string2)] == string2:
|
|
102
|
+
return False
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def max_degree(list_of_polys) -> float:
|
|
107
|
+
"""
|
|
108
|
+
Compute the maximum degree of a list of polynomials.
|
|
109
|
+
|
|
110
|
+
EXAMPLES::
|
|
111
|
+
|
|
112
|
+
sage: from sage.rings.polynomial.groebner_fan import max_degree
|
|
113
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
114
|
+
sage: p_list = [x^2-y,x*y^10-x]
|
|
115
|
+
sage: max_degree(p_list)
|
|
116
|
+
11.0
|
|
117
|
+
"""
|
|
118
|
+
return float(max(qf.degree() for qf in list_of_polys))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _cone_parse(fan_dict_cone) -> dict:
|
|
122
|
+
"""
|
|
123
|
+
Utility function that parses cone information into a dict indexed by
|
|
124
|
+
dimension.
|
|
125
|
+
|
|
126
|
+
INPUT:
|
|
127
|
+
|
|
128
|
+
- ``fan_dict_cone`` -- the value of a ``fan_dict`` with key 'CONES'
|
|
129
|
+
|
|
130
|
+
EXAMPLES::
|
|
131
|
+
|
|
132
|
+
sage: R.<x,y,z,w> = PolynomialRing(QQ,4)
|
|
133
|
+
sage: I = R.ideal([x^2+y^2+z^2-1,x^4-y^2-z-1,x+y+z,w+x+y])
|
|
134
|
+
sage: GF = I.groebner_fan()
|
|
135
|
+
sage: TI = GF.tropical_intersection()
|
|
136
|
+
sage: from sage.rings.polynomial.groebner_fan import _cone_parse
|
|
137
|
+
sage: _cone_parse(TI.fan_dict['CONES'])
|
|
138
|
+
{1: [[0], [1], [2], [3], [4]], 2: [[2, 3]]}
|
|
139
|
+
"""
|
|
140
|
+
cone_dict = {}
|
|
141
|
+
cur_dim = 0
|
|
142
|
+
for item in fan_dict_cone:
|
|
143
|
+
temp = item.split('{')[-1]
|
|
144
|
+
if temp.split('}') == '':
|
|
145
|
+
temp = ['']
|
|
146
|
+
else:
|
|
147
|
+
temp = temp.split('}')[0]
|
|
148
|
+
temp = temp.split(' ')
|
|
149
|
+
if item.find('Dimension') != -1:
|
|
150
|
+
cur_dim = Integer(item.split(' ')[-1])
|
|
151
|
+
if cur_dim > 0:
|
|
152
|
+
cone_dict[cur_dim] = [[Integer(q) for q in temp if q != '']]
|
|
153
|
+
else:
|
|
154
|
+
if cur_dim > 0:
|
|
155
|
+
cone_dict[cur_dim] += [[Integer(q) for q in temp if q != '']]
|
|
156
|
+
return cone_dict
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class PolyhedralCone(SageObject):
|
|
160
|
+
|
|
161
|
+
def __init__(self, gfan_polyhedral_cone, ring=QQ) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Convert polymake/gfan data on a polyhedral cone into a sage class.
|
|
164
|
+
|
|
165
|
+
Currently (18-03-2008) needs a lot of work.
|
|
166
|
+
|
|
167
|
+
EXAMPLES::
|
|
168
|
+
|
|
169
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
170
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
171
|
+
sage: a = gf[0].groebner_cone()
|
|
172
|
+
sage: a.facets()
|
|
173
|
+
[[0, 0, 1], [0, 1, 0], [1, 0, 0]]
|
|
174
|
+
"""
|
|
175
|
+
cone_keys = ['AMBIENT_DIM', 'DIM', 'IMPLIED_EQUATIONS',
|
|
176
|
+
'LINEALITY_DIM', 'LINEALITY_SPACE', 'FACETS',
|
|
177
|
+
'RELATIVE_INTERIOR_POINT']
|
|
178
|
+
poly_lines = gfan_polyhedral_cone.split('\n')
|
|
179
|
+
self.cone_dict = {}
|
|
180
|
+
cur_key = None
|
|
181
|
+
for ting in poly_lines:
|
|
182
|
+
if cone_keys.count(ting):
|
|
183
|
+
cur_key = ting
|
|
184
|
+
self.cone_dict[cur_key] = []
|
|
185
|
+
elif cur_key and ting:
|
|
186
|
+
self.cone_dict[cur_key].append(ting)
|
|
187
|
+
self._facets = []
|
|
188
|
+
for facet in self.cone_dict['FACETS']:
|
|
189
|
+
temp_facet = facet.split('\t')[0]
|
|
190
|
+
temp_facet = temp_facet.split(' ')
|
|
191
|
+
temp_facet = [int(x) for x in temp_facet]
|
|
192
|
+
self._facets.append(temp_facet)
|
|
193
|
+
self._ambient_dim = int(self.cone_dict['AMBIENT_DIM'][0])
|
|
194
|
+
self._dim = int(self.cone_dict['DIM'][0])
|
|
195
|
+
self._lineality_dim = int(self.cone_dict['LINEALITY_DIM'][0])
|
|
196
|
+
rel_int_pt_str = self.cone_dict['RELATIVE_INTERIOR_POINT'][0]
|
|
197
|
+
self._relative_interior_point = [int(q) for q in rel_int_pt_str.split(' ')]
|
|
198
|
+
|
|
199
|
+
def _repr_(self) -> str:
|
|
200
|
+
"""
|
|
201
|
+
Return a basic description of the polyhedral cone.
|
|
202
|
+
|
|
203
|
+
EXAMPLES::
|
|
204
|
+
|
|
205
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
206
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
207
|
+
sage: a = gf[0].groebner_cone()
|
|
208
|
+
sage: a # indirect doctests
|
|
209
|
+
Polyhedral cone in 3 dimensions of dimension 3
|
|
210
|
+
"""
|
|
211
|
+
return "Polyhedral cone in {} dimensions of dimension {}".format(self.ambient_dim(), self.dim())
|
|
212
|
+
|
|
213
|
+
def facets(self) -> list:
|
|
214
|
+
"""
|
|
215
|
+
Return the inward facet normals of the Groebner cone.
|
|
216
|
+
|
|
217
|
+
EXAMPLES::
|
|
218
|
+
|
|
219
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
220
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
221
|
+
sage: a = gf[0].groebner_cone()
|
|
222
|
+
sage: a.facets()
|
|
223
|
+
[[0, 0, 1], [0, 1, 0], [1, 0, 0]]
|
|
224
|
+
"""
|
|
225
|
+
return self._facets
|
|
226
|
+
|
|
227
|
+
def ambient_dim(self):
|
|
228
|
+
"""
|
|
229
|
+
Return the ambient dimension of the Groebner cone.
|
|
230
|
+
|
|
231
|
+
EXAMPLES::
|
|
232
|
+
|
|
233
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
234
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
235
|
+
sage: a = gf[0].groebner_cone()
|
|
236
|
+
sage: a.ambient_dim()
|
|
237
|
+
3
|
|
238
|
+
"""
|
|
239
|
+
return self._ambient_dim
|
|
240
|
+
|
|
241
|
+
def dim(self):
|
|
242
|
+
"""
|
|
243
|
+
Return the dimension of the Groebner cone.
|
|
244
|
+
|
|
245
|
+
EXAMPLES::
|
|
246
|
+
|
|
247
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
248
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
249
|
+
sage: a = gf[0].groebner_cone()
|
|
250
|
+
sage: a.dim()
|
|
251
|
+
3
|
|
252
|
+
"""
|
|
253
|
+
return self._dim
|
|
254
|
+
|
|
255
|
+
def lineality_dim(self):
|
|
256
|
+
"""
|
|
257
|
+
Return the lineality dimension of the Groebner cone. This is
|
|
258
|
+
just the difference between the ambient dimension and the dimension
|
|
259
|
+
of the cone.
|
|
260
|
+
|
|
261
|
+
EXAMPLES::
|
|
262
|
+
|
|
263
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
264
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
265
|
+
sage: a = gf[0].groebner_cone()
|
|
266
|
+
sage: a.lineality_dim()
|
|
267
|
+
0
|
|
268
|
+
"""
|
|
269
|
+
return self._lineality_dim
|
|
270
|
+
|
|
271
|
+
def relative_interior_point(self):
|
|
272
|
+
"""
|
|
273
|
+
Return a point in the relative interior of the Groebner cone.
|
|
274
|
+
|
|
275
|
+
EXAMPLES::
|
|
276
|
+
|
|
277
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
278
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
279
|
+
sage: a = gf[0].groebner_cone()
|
|
280
|
+
sage: a.relative_interior_point()
|
|
281
|
+
[1, 1, 1]
|
|
282
|
+
"""
|
|
283
|
+
return self._relative_interior_point
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class PolyhedralFan(SageObject):
|
|
287
|
+
def __init__(self, gfan_polyhedral_fan, parameter_indices=None) -> None:
|
|
288
|
+
"""
|
|
289
|
+
Convert polymake/gfan data on a polyhedral fan into a sage class.
|
|
290
|
+
|
|
291
|
+
INPUT:
|
|
292
|
+
|
|
293
|
+
- ``gfan_polyhedral_fan`` -- output from gfan of a polyhedral fan
|
|
294
|
+
|
|
295
|
+
EXAMPLES::
|
|
296
|
+
|
|
297
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
298
|
+
sage: i2 = ideal(x*z + 6*y*z - z^2, x*y + 6*x*z + y*z - z^2, y^2 + x*z + y*z)
|
|
299
|
+
sage: gf2 = i2.groebner_fan(verbose=False)
|
|
300
|
+
sage: pf = gf2.polyhedralfan()
|
|
301
|
+
sage: pf.rays()
|
|
302
|
+
[[-1, 0, 1], [-1, 1, 0], [1, -2, 1], [1, 1, -2], [2, -1, -1]]
|
|
303
|
+
"""
|
|
304
|
+
if parameter_indices is None:
|
|
305
|
+
parameter_indices = []
|
|
306
|
+
fan_keys = ['AMBIENT_DIM', 'DIM', 'LINEALITY_DIM', 'RAYS', 'N_RAYS',
|
|
307
|
+
'LINEALITY_SPACE', 'ORTH_LINEALITY_SPACE', 'F_VECTOR',
|
|
308
|
+
'CONES', 'MAXIMAL_CONES', 'PURE', 'SIMPLICIAL',
|
|
309
|
+
'MULTIPLICITIES']
|
|
310
|
+
poly_lines = gfan_polyhedral_fan.split('\n')
|
|
311
|
+
self.fan_dict = {}
|
|
312
|
+
cur_key = None
|
|
313
|
+
for ting in poly_lines:
|
|
314
|
+
if fan_keys.count(ting):
|
|
315
|
+
cur_key = ting
|
|
316
|
+
self.fan_dict[cur_key] = []
|
|
317
|
+
elif cur_key and ting != '':
|
|
318
|
+
self.fan_dict[cur_key].append(ting)
|
|
319
|
+
self._ambient_dim = int(self.fan_dict['AMBIENT_DIM'][0])
|
|
320
|
+
self._dim = int(self.fan_dict['DIM'][0])
|
|
321
|
+
self._lineality_dim = int(self.fan_dict['LINEALITY_DIM'][0])
|
|
322
|
+
self._rays = []
|
|
323
|
+
for ray in self.fan_dict['RAYS']:
|
|
324
|
+
temp_ray = ray.split('\t')[0]
|
|
325
|
+
temp_ray = temp_ray.split(' ')
|
|
326
|
+
temp_ray = [int(x) for x in temp_ray]
|
|
327
|
+
if parameter_indices != []:
|
|
328
|
+
for q in parameter_indices:
|
|
329
|
+
temp_ray = temp_ray[0:q] + [0] + temp_ray[q:]
|
|
330
|
+
self._rays.append(temp_ray)
|
|
331
|
+
self._cone_dict = _cone_parse(self.fan_dict['CONES'])
|
|
332
|
+
self._maximal_cone_dict = _cone_parse(self.fan_dict['MAXIMAL_CONES'])
|
|
333
|
+
self._str = gfan_polyhedral_fan
|
|
334
|
+
|
|
335
|
+
def _repr_(self) -> str:
|
|
336
|
+
"""
|
|
337
|
+
Return a basic description of the polyhedral fan.
|
|
338
|
+
|
|
339
|
+
EXAMPLES::
|
|
340
|
+
|
|
341
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
342
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
343
|
+
sage: pf = gf.polyhedralfan()
|
|
344
|
+
sage: pf # indirect doctest
|
|
345
|
+
Polyhedral fan in 3 dimensions of dimension 3
|
|
346
|
+
"""
|
|
347
|
+
return "Polyhedral fan in {} dimensions of dimension {}".format(self.ambient_dim(), self.dim())
|
|
348
|
+
|
|
349
|
+
def _str_(self) -> str:
|
|
350
|
+
r"""
|
|
351
|
+
Return the raw output of gfan as a string.
|
|
352
|
+
|
|
353
|
+
This should only be needed internally as all relevant output
|
|
354
|
+
is converted to sage objects.
|
|
355
|
+
|
|
356
|
+
EXAMPLES::
|
|
357
|
+
|
|
358
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
359
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
360
|
+
sage: pf = gf.polyhedralfan()
|
|
361
|
+
sage: pf._str_()
|
|
362
|
+
'_application fan\n_version 2.2\n_type SymmetricFan\n\nAMBIENT_DIM\n3\n\nDIM\n3\n\nLINEALITY_DIM\n0\n\nRAYS\n0 0 1\t# 0\n0 1 0\t# 1\n1 0 0\t# 2\n\nN_RAYS\n3\n\nLINEALITY_SPACE\n\nORTH_LINEALITY_SPACE\n1 0 0\n0 1 0\n0 0 1\n\nF_VECTOR\n1 3 3 1\n\nSIMPLICIAL\n1\n\nPURE\n1\n\nCONES\n{}\t# Dimension 0\n{0}\t# Dimension 1\n{1}\n{2}\n{0 1}\t# Dimension 2\n{0 2}\n{1 2}\n{0 1 2}\t# Dimension 3\n\nMAXIMAL_CONES\n{0 1 2}\t# Dimension 3\n'
|
|
363
|
+
"""
|
|
364
|
+
return self._str
|
|
365
|
+
|
|
366
|
+
def ambient_dim(self):
|
|
367
|
+
"""
|
|
368
|
+
Return the ambient dimension of the Groebner fan.
|
|
369
|
+
|
|
370
|
+
EXAMPLES::
|
|
371
|
+
|
|
372
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
373
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
374
|
+
sage: a = gf.polyhedralfan()
|
|
375
|
+
sage: a.ambient_dim()
|
|
376
|
+
3
|
|
377
|
+
"""
|
|
378
|
+
return self._ambient_dim
|
|
379
|
+
|
|
380
|
+
def dim(self):
|
|
381
|
+
"""
|
|
382
|
+
Return the dimension of the Groebner fan.
|
|
383
|
+
|
|
384
|
+
EXAMPLES::
|
|
385
|
+
|
|
386
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
387
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
388
|
+
sage: a = gf.polyhedralfan()
|
|
389
|
+
sage: a.dim()
|
|
390
|
+
3
|
|
391
|
+
"""
|
|
392
|
+
return self._dim
|
|
393
|
+
|
|
394
|
+
def lineality_dim(self):
|
|
395
|
+
"""
|
|
396
|
+
Return the lineality dimension of the fan. This is the
|
|
397
|
+
dimension of the largest subspace contained in the fan.
|
|
398
|
+
|
|
399
|
+
EXAMPLES::
|
|
400
|
+
|
|
401
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
402
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-2]).groebner_fan()
|
|
403
|
+
sage: a = gf.polyhedralfan()
|
|
404
|
+
sage: a.lineality_dim()
|
|
405
|
+
0
|
|
406
|
+
"""
|
|
407
|
+
return self._lineality_dim
|
|
408
|
+
|
|
409
|
+
def rays(self) -> list:
|
|
410
|
+
"""
|
|
411
|
+
A list of rays of the polyhedral fan.
|
|
412
|
+
|
|
413
|
+
EXAMPLES::
|
|
414
|
+
|
|
415
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
416
|
+
sage: i2 = ideal(x*z + 6*y*z - z^2, x*y + 6*x*z + y*z - z^2, y^2 + x*z + y*z)
|
|
417
|
+
sage: gf2 = i2.groebner_fan(verbose=False)
|
|
418
|
+
sage: pf = gf2.polyhedralfan()
|
|
419
|
+
sage: pf.rays()
|
|
420
|
+
[[-1, 0, 1], [-1, 1, 0], [1, -2, 1], [1, 1, -2], [2, -1, -1]]
|
|
421
|
+
"""
|
|
422
|
+
return sorted(self._rays)
|
|
423
|
+
|
|
424
|
+
def cones(self) -> dict:
|
|
425
|
+
"""
|
|
426
|
+
A dictionary of cones in which the keys are the cone dimensions.
|
|
427
|
+
|
|
428
|
+
For each dimension, the value is a list of the cones,
|
|
429
|
+
where each element consists of a list of ray indices.
|
|
430
|
+
|
|
431
|
+
EXAMPLES::
|
|
432
|
+
|
|
433
|
+
sage: R.<x,y,z> = QQ[]
|
|
434
|
+
sage: f = 1+x+y+x*y
|
|
435
|
+
sage: I = R.ideal([f+z*f, 2*f+z*f, 3*f+z^2*f])
|
|
436
|
+
sage: GF = I.groebner_fan()
|
|
437
|
+
sage: PF = GF.tropical_intersection()
|
|
438
|
+
sage: PF.cones()
|
|
439
|
+
{1: [[0], [1], [2], [3], [4], [5]], 2: [[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [4, 5]]}
|
|
440
|
+
"""
|
|
441
|
+
return self._cone_dict
|
|
442
|
+
|
|
443
|
+
def maximal_cones(self) -> dict:
|
|
444
|
+
"""
|
|
445
|
+
A dictionary of the maximal cones in which the keys are the
|
|
446
|
+
cone dimensions.
|
|
447
|
+
|
|
448
|
+
For each dimension, the value is a list of
|
|
449
|
+
the maximal cones, where each element consists of a list of ray indices.
|
|
450
|
+
|
|
451
|
+
EXAMPLES::
|
|
452
|
+
|
|
453
|
+
sage: R.<x,y,z> = QQ[]
|
|
454
|
+
sage: f = 1+x+y+x*y
|
|
455
|
+
sage: I = R.ideal([f+z*f, 2*f+z*f, 3*f+z^2*f])
|
|
456
|
+
sage: GF = I.groebner_fan()
|
|
457
|
+
sage: PF = GF.tropical_intersection()
|
|
458
|
+
sage: PF.maximal_cones()
|
|
459
|
+
{2: [[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [4, 5]]}
|
|
460
|
+
"""
|
|
461
|
+
return self._maximal_cone_dict
|
|
462
|
+
|
|
463
|
+
def f_vector(self) -> list:
|
|
464
|
+
"""
|
|
465
|
+
The f-vector of the fan.
|
|
466
|
+
|
|
467
|
+
EXAMPLES::
|
|
468
|
+
|
|
469
|
+
sage: R.<x,y,z> = QQ[]
|
|
470
|
+
sage: f = 1+x+y+x*y
|
|
471
|
+
sage: I = R.ideal([f+z*f, 2*f+z*f, 3*f+z^2*f])
|
|
472
|
+
sage: GF = I.groebner_fan()
|
|
473
|
+
sage: PF = GF.tropical_intersection()
|
|
474
|
+
sage: PF.f_vector()
|
|
475
|
+
[1, 6, 12]
|
|
476
|
+
"""
|
|
477
|
+
str_data = self.fan_dict['F_VECTOR'][0]
|
|
478
|
+
return [Integer(x) for x in str_data.split(' ')]
|
|
479
|
+
|
|
480
|
+
def is_simplicial(self) -> bool:
|
|
481
|
+
"""
|
|
482
|
+
Whether the fan is simplicial or not.
|
|
483
|
+
|
|
484
|
+
EXAMPLES::
|
|
485
|
+
|
|
486
|
+
sage: R.<x,y,z> = QQ[]
|
|
487
|
+
sage: f = 1+x+y+x*y
|
|
488
|
+
sage: I = R.ideal([f+z*f, 2*f+z*f, 3*f+z^2*f])
|
|
489
|
+
sage: GF = I.groebner_fan()
|
|
490
|
+
sage: PF = GF.tropical_intersection()
|
|
491
|
+
sage: PF.is_simplicial()
|
|
492
|
+
True
|
|
493
|
+
"""
|
|
494
|
+
return bool(int(self.fan_dict['SIMPLICIAL'][0]))
|
|
495
|
+
|
|
496
|
+
def to_RationalPolyhedralFan(self):
|
|
497
|
+
"""
|
|
498
|
+
Convert to the RationalPolyhedralFan class, which is more actively
|
|
499
|
+
maintained.
|
|
500
|
+
|
|
501
|
+
While the information in each class is essentially the
|
|
502
|
+
same, the methods and implementation are different.
|
|
503
|
+
|
|
504
|
+
EXAMPLES::
|
|
505
|
+
|
|
506
|
+
sage: R.<x,y,z> = QQ[]
|
|
507
|
+
sage: f = 1+x+y+x*y
|
|
508
|
+
sage: I = R.ideal([f+z*f, 2*f+z*f, 3*f+z^2*f])
|
|
509
|
+
sage: GF = I.groebner_fan()
|
|
510
|
+
sage: PF = GF.tropical_intersection()
|
|
511
|
+
sage: fan = PF.to_RationalPolyhedralFan()
|
|
512
|
+
sage: [tuple(q.facet_normals()) for q in fan]
|
|
513
|
+
[(M(0, -1, 0), M(-1, 0, 0)), (M(0, 0, -1), M(-1, 0, 0)), (M(0, 0, 1), M(-1, 0, 0)), (M(0, 1, 0), M(-1, 0, 0)), (M(0, 0, -1), M(0, -1, 0)), (M(0, 0, 1), M(0, -1, 0)), (M(0, 1, 0), M(0, 0, -1)), (M(0, 1, 0), M(0, 0, 1)), (M(1, 0, 0), M(0, -1, 0)), (M(1, 0, 0), M(0, 0, -1)), (M(1, 0, 0), M(0, 0, 1)), (M(1, 0, 0), M(0, 1, 0))]
|
|
514
|
+
|
|
515
|
+
Here we use the RationalPolyhedralFan's Gale_transform method on a tropical
|
|
516
|
+
prevariety.
|
|
517
|
+
|
|
518
|
+
.. link
|
|
519
|
+
|
|
520
|
+
::
|
|
521
|
+
|
|
522
|
+
sage: fan.Gale_transform() # needs sage.libs.linbox
|
|
523
|
+
[ 1 0 0 0 0 1 -2]
|
|
524
|
+
[ 0 1 0 0 1 0 -2]
|
|
525
|
+
[ 0 0 1 1 0 0 -2]
|
|
526
|
+
"""
|
|
527
|
+
try:
|
|
528
|
+
return self._fan
|
|
529
|
+
except AttributeError:
|
|
530
|
+
cdnt = []
|
|
531
|
+
cones = self.cones()
|
|
532
|
+
for x in cones:
|
|
533
|
+
if x > 1:
|
|
534
|
+
cdnt += cones[x]
|
|
535
|
+
fan = Fan(cones=cdnt, rays=self.rays(), discard_faces=True)
|
|
536
|
+
self._fan = fan
|
|
537
|
+
return self._fan
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
class InitialForm(SageObject):
|
|
541
|
+
def __init__(self, cone, rays, initial_forms) -> None:
|
|
542
|
+
"""
|
|
543
|
+
A system of initial forms from a polynomial system.
|
|
544
|
+
|
|
545
|
+
To each form is associated a cone and a list of
|
|
546
|
+
polynomials (the initial form system itself).
|
|
547
|
+
|
|
548
|
+
This class is intended for internal use inside of the
|
|
549
|
+
:class:`TropicalPrevariety` class.
|
|
550
|
+
|
|
551
|
+
EXAMPLES::
|
|
552
|
+
|
|
553
|
+
sage: from sage.rings.polynomial.groebner_fan import InitialForm
|
|
554
|
+
sage: R.<x,y> = QQ[]
|
|
555
|
+
sage: inform = InitialForm([0], [[-1, 0]], [y^2 - 1, y^2 - 2, y^2 - 3])
|
|
556
|
+
sage: inform._cone
|
|
557
|
+
[0]
|
|
558
|
+
"""
|
|
559
|
+
self._cone = cone
|
|
560
|
+
self._rays = rays
|
|
561
|
+
self._initial_forms = initial_forms
|
|
562
|
+
|
|
563
|
+
def cone(self):
|
|
564
|
+
"""
|
|
565
|
+
The cone associated with the initial form system.
|
|
566
|
+
|
|
567
|
+
EXAMPLES::
|
|
568
|
+
|
|
569
|
+
sage: R.<x,y> = QQ[]
|
|
570
|
+
sage: I = R.ideal([(x+y)^2-1,(x+y)^2-2,(x+y)^2-3])
|
|
571
|
+
sage: GF = I.groebner_fan()
|
|
572
|
+
sage: PF = GF.tropical_intersection()
|
|
573
|
+
sage: pfi0 = PF.initial_form_systems()[0]
|
|
574
|
+
sage: pfi0.cone()
|
|
575
|
+
[0]
|
|
576
|
+
"""
|
|
577
|
+
return self._cone
|
|
578
|
+
|
|
579
|
+
def rays(self):
|
|
580
|
+
"""
|
|
581
|
+
The rays of the cone associated with the initial form system.
|
|
582
|
+
|
|
583
|
+
EXAMPLES::
|
|
584
|
+
|
|
585
|
+
sage: R.<x,y> = QQ[]
|
|
586
|
+
sage: I = R.ideal([(x+y)^2-1,(x+y)^2-2,(x+y)^2-3])
|
|
587
|
+
sage: GF = I.groebner_fan()
|
|
588
|
+
sage: PF = GF.tropical_intersection()
|
|
589
|
+
sage: pfi0 = PF.initial_form_systems()[0]
|
|
590
|
+
sage: pfi0.rays()
|
|
591
|
+
[[-1, 0]]
|
|
592
|
+
"""
|
|
593
|
+
return self._rays
|
|
594
|
+
|
|
595
|
+
def internal_ray(self):
|
|
596
|
+
"""
|
|
597
|
+
A ray internal to the cone associated with the initial form
|
|
598
|
+
system.
|
|
599
|
+
|
|
600
|
+
EXAMPLES::
|
|
601
|
+
|
|
602
|
+
sage: R.<x,y> = QQ[]
|
|
603
|
+
sage: I = R.ideal([(x+y)^2-1,(x+y)^2-2,(x+y)^2-3])
|
|
604
|
+
sage: GF = I.groebner_fan()
|
|
605
|
+
sage: PF = GF.tropical_intersection()
|
|
606
|
+
sage: pfi0 = PF.initial_form_systems()[0]
|
|
607
|
+
sage: pfi0.internal_ray()
|
|
608
|
+
(-1, 0)
|
|
609
|
+
"""
|
|
610
|
+
return sum([vector(q) for q in self.rays()])
|
|
611
|
+
|
|
612
|
+
def initial_forms(self):
|
|
613
|
+
"""
|
|
614
|
+
The initial forms (polynomials).
|
|
615
|
+
|
|
616
|
+
EXAMPLES::
|
|
617
|
+
|
|
618
|
+
sage: R.<x,y> = QQ[]
|
|
619
|
+
sage: I = R.ideal([(x+y)^2-1,(x+y)^2-2,(x+y)^2-3])
|
|
620
|
+
sage: GF = I.groebner_fan()
|
|
621
|
+
sage: PF = GF.tropical_intersection()
|
|
622
|
+
sage: pfi0 = PF.initial_form_systems()[0]
|
|
623
|
+
sage: pfi0.initial_forms()
|
|
624
|
+
[y^2 - 1, y^2 - 2, y^2 - 3]
|
|
625
|
+
"""
|
|
626
|
+
return self._initial_forms
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def verts_for_normal(normal, poly) -> list:
|
|
630
|
+
"""
|
|
631
|
+
Return the exponents of the vertices of a Newton polytope
|
|
632
|
+
that make up the supporting hyperplane for the given outward
|
|
633
|
+
normal.
|
|
634
|
+
|
|
635
|
+
EXAMPLES::
|
|
636
|
+
|
|
637
|
+
sage: from sage.rings.polynomial.groebner_fan import verts_for_normal
|
|
638
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
639
|
+
sage: f1 = x*y*z - 1
|
|
640
|
+
sage: f2 = f1*(x^2 + y^2 + 1)
|
|
641
|
+
sage: verts_for_normal([1,1,1],f2)
|
|
642
|
+
[(3, 1, 1), (1, 3, 1)]
|
|
643
|
+
"""
|
|
644
|
+
exps = [tuple(x) for x in poly.exponents()]
|
|
645
|
+
expmat = matrix(exps)
|
|
646
|
+
vals = expmat * vector(QQ, normal)
|
|
647
|
+
maxval = max(vals)
|
|
648
|
+
return [exps[i] for i in range(len(exps)) if vals[i] == maxval]
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
class TropicalPrevariety(PolyhedralFan):
|
|
652
|
+
|
|
653
|
+
def __init__(self, gfan_polyhedral_fan, polynomial_system, poly_ring,
|
|
654
|
+
parameters=None) -> None:
|
|
655
|
+
"""
|
|
656
|
+
This class is a subclass of the PolyhedralFan class,
|
|
657
|
+
with some additional methods for tropical prevarieties.
|
|
658
|
+
|
|
659
|
+
INPUT:
|
|
660
|
+
|
|
661
|
+
- ``gfan_polyhedral_fan`` -- output from ``gfan`` of a polyhedral fan
|
|
662
|
+
- ``polynomial_system`` -- list of polynomials
|
|
663
|
+
- ``poly_ring`` -- the polynomial ring of the list of polynomials
|
|
664
|
+
- ``parameters`` -- (optional) list of variables to be considered
|
|
665
|
+
as parameters
|
|
666
|
+
|
|
667
|
+
EXAMPLES::
|
|
668
|
+
|
|
669
|
+
sage: R.<x,y,z> = QQ[]
|
|
670
|
+
sage: I = R.ideal([(x+y+z)^2-1,(x+y+z)-x,(x+y+z)-3])
|
|
671
|
+
sage: GF = I.groebner_fan()
|
|
672
|
+
sage: TI = GF.tropical_intersection()
|
|
673
|
+
sage: TI._polynomial_system
|
|
674
|
+
[x^2 + 2*x*y + y^2 + 2*x*z + 2*y*z + z^2 - 1, y + z, x + y + z - 3]
|
|
675
|
+
"""
|
|
676
|
+
parameter_indices = []
|
|
677
|
+
if parameters is not None:
|
|
678
|
+
allvars = poly_ring.gens()
|
|
679
|
+
parameter_indices = [allvars.index(q) for q in parameters]
|
|
680
|
+
PolyhedralFan.__init__(self, gfan_polyhedral_fan,
|
|
681
|
+
parameter_indices=parameter_indices)
|
|
682
|
+
self._polynomial_system = polynomial_system
|
|
683
|
+
self._parameters = parameters
|
|
684
|
+
|
|
685
|
+
def initial_form_systems(self) -> list:
|
|
686
|
+
"""
|
|
687
|
+
Return a list of systems of initial forms for each cone
|
|
688
|
+
in the tropical prevariety.
|
|
689
|
+
|
|
690
|
+
EXAMPLES::
|
|
691
|
+
|
|
692
|
+
sage: R.<x,y> = QQ[]
|
|
693
|
+
sage: I = R.ideal([(x+y)^2-1,(x+y)^2-2,(x+y)^2-3])
|
|
694
|
+
sage: GF = I.groebner_fan()
|
|
695
|
+
sage: PF = GF.tropical_intersection()
|
|
696
|
+
sage: pfi = PF.initial_form_systems()
|
|
697
|
+
sage: for q in pfi:
|
|
698
|
+
....: print(q.initial_forms())
|
|
699
|
+
[y^2 - 1, y^2 - 2, y^2 - 3]
|
|
700
|
+
[x^2 - 1, x^2 - 2, x^2 - 3]
|
|
701
|
+
[x^2 + 2*x*y + y^2, x^2 + 2*x*y + y^2, x^2 + 2*x*y + y^2]
|
|
702
|
+
"""
|
|
703
|
+
try:
|
|
704
|
+
return self._initial_form_systems
|
|
705
|
+
except AttributeError:
|
|
706
|
+
initial_form_systems = []
|
|
707
|
+
pvars = self._polynomial_system[0].parent().gens()
|
|
708
|
+
nvars = len(pvars)
|
|
709
|
+
for dcone in self.cones():
|
|
710
|
+
for acone in self.cones()[dcone]:
|
|
711
|
+
rays = [self.rays()[i] for i in acone]
|
|
712
|
+
repray = sum([vector(q) for q in rays])
|
|
713
|
+
iforms = []
|
|
714
|
+
for poly in self._polynomial_system:
|
|
715
|
+
verts = verts_for_normal(repray, poly)
|
|
716
|
+
nform = 0
|
|
717
|
+
for x in verts:
|
|
718
|
+
factorlist = [pvars[i]**x[i] for i in range(nvars)]
|
|
719
|
+
temp_monomial = prod(factorlist)
|
|
720
|
+
nform += poly.monomial_coefficient(temp_monomial) * temp_monomial
|
|
721
|
+
iforms.append(nform)
|
|
722
|
+
initial_form_systems.append(InitialForm(acone, rays, iforms))
|
|
723
|
+
self._initial_form_systems = initial_form_systems
|
|
724
|
+
return self._initial_form_systems
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
def ring_to_gfan_format(input_ring) -> str:
|
|
728
|
+
"""
|
|
729
|
+
Convert a ring to gfan's format.
|
|
730
|
+
|
|
731
|
+
EXAMPLES::
|
|
732
|
+
|
|
733
|
+
sage: R.<w,x,y,z> = QQ[]
|
|
734
|
+
sage: from sage.rings.polynomial.groebner_fan import ring_to_gfan_format
|
|
735
|
+
sage: ring_to_gfan_format(R)
|
|
736
|
+
'Q[w, x, y, z]'
|
|
737
|
+
sage: R2.<x,y> = GF(2)[]
|
|
738
|
+
sage: ring_to_gfan_format(R2)
|
|
739
|
+
'Z/2Z[x, y]'
|
|
740
|
+
"""
|
|
741
|
+
gens = str(input_ring.gens()).replace('(', '[').replace(')', ']')
|
|
742
|
+
if input_ring.base_ring() is QQ:
|
|
743
|
+
return 'Q' + gens
|
|
744
|
+
elif input_ring.base_ring() is ZZ:
|
|
745
|
+
return 'Z' + gens
|
|
746
|
+
else:
|
|
747
|
+
return 'Z/{}Z'.format(input_ring.characteristic()) + gens
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def ideal_to_gfan_format(input_ring, polys) -> str:
|
|
751
|
+
"""
|
|
752
|
+
Return the ideal in gfan's notation.
|
|
753
|
+
|
|
754
|
+
EXAMPLES::
|
|
755
|
+
|
|
756
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
757
|
+
sage: polys = [x^2*y - z, y^2*z - x, z^2*x - y]
|
|
758
|
+
sage: from sage.rings.polynomial.groebner_fan import ideal_to_gfan_format
|
|
759
|
+
sage: ideal_to_gfan_format(R, polys)
|
|
760
|
+
'Q[x, y, z]{x^2*y-z,y^2*z-x,x*z^2-y}'
|
|
761
|
+
|
|
762
|
+
TESTS:
|
|
763
|
+
|
|
764
|
+
Test that :issue:`20146` is fixed::
|
|
765
|
+
|
|
766
|
+
sage: P = PolynomialRing(QQ,"x11,x12,x13,x14,x15,x21,x22,x23,x24,x25,x31,x32,x33,x34,x35"); x = P.gens(); M = Matrix(3,x)
|
|
767
|
+
sage: I = P.ideal(M.minors(2))
|
|
768
|
+
sage: ideal_to_gfan_format(P,I.gens())
|
|
769
|
+
'Q[x11, x12, x13, x14, x15, x21, x22, x23, x24, x25, x31, x32, x33, x34, x35]{-x12*x21+x11*x22,-x13*x21+x11*x23,-x14*x21+x11*x24,-x15*x21+x11*x25,-x13*x22+x12*x23,-x14*x22+x12*x24,-x15*x22+x12*x25,-x14*x23+x13*x24,-x15*x23+x13*x25,-x15*x24+x14*x25,-x12*x31+x11*x32,-x13*x31+x11*x33,-x14*x31+x11*x34,-x15*x31+x11*x35,-x13*x32+x12*x33,-x14*x32+x12*x34,-x15*x32+x12*x35,-x14*x33+x13*x34,-x15*x33+x13*x35,-x15*x34+x14*x35,-x22*x31+x21*x32,-x23*x31+x21*x33,-x24*x31+x21*x34,-x25*x31+x21*x35,-x23*x32+x22*x33,-x24*x32+x22*x34,-x25*x32+x22*x35,-x24*x33+x23*x34,-x25*x33+x23*x35,-x25*x34+x24*x35}'
|
|
770
|
+
"""
|
|
771
|
+
ideal_gen_str = "{" + ",".join(str(poly).replace(" ", "").replace("'", "")
|
|
772
|
+
for poly in polys) + "}"
|
|
773
|
+
ring_str = ring_to_gfan_format(input_ring)
|
|
774
|
+
output = ring_str + ideal_gen_str
|
|
775
|
+
return output
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
class GroebnerFan(SageObject):
|
|
779
|
+
|
|
780
|
+
def __init__(self, I, is_groebner_basis=False, symmetry=None, verbose=False) -> None:
|
|
781
|
+
"""
|
|
782
|
+
This class is used to access capabilities of the program ``Gfan``.
|
|
783
|
+
|
|
784
|
+
In addition to computing Groebner fans, ``Gfan`` can compute
|
|
785
|
+
other things in tropical geometry such as tropical prevarieties.
|
|
786
|
+
|
|
787
|
+
INPUT:
|
|
788
|
+
|
|
789
|
+
- ``I`` -- ideal in a multivariate polynomial ring
|
|
790
|
+
|
|
791
|
+
- ``is_groebner_basis`` -- boolean (default: ``False``); if
|
|
792
|
+
``True``, then I.gens() must be a Groebner basis with respect to the
|
|
793
|
+
standard degree lexicographic term order
|
|
794
|
+
|
|
795
|
+
- ``symmetry`` -- (default: ``None``) if not ``None``, describes
|
|
796
|
+
symmetries of the ideal
|
|
797
|
+
|
|
798
|
+
- ``verbose`` -- (default: ``False``) if ``True``, printout
|
|
799
|
+
useful info during computations
|
|
800
|
+
|
|
801
|
+
EXAMPLES::
|
|
802
|
+
|
|
803
|
+
sage: R.<x,y,z> = QQ[]
|
|
804
|
+
sage: I = R.ideal([x^2*y - z, y^2*z - x, z^2*x - y])
|
|
805
|
+
sage: G = I.groebner_fan(); G
|
|
806
|
+
Groebner fan of the ideal:
|
|
807
|
+
Ideal (x^2*y - z, y^2*z - x, x*z^2 - y) of Multivariate Polynomial Ring in x, y, z over Rational Field
|
|
808
|
+
|
|
809
|
+
Here is an example of the use of the tropical_intersection command, and then using the RationalPolyhedralFan
|
|
810
|
+
class to compute the Stanley-Reisner ideal of the tropical prevariety::
|
|
811
|
+
|
|
812
|
+
sage: R.<x,y,z> = QQ[]
|
|
813
|
+
sage: I = R.ideal([(x+y+z)^3-1,(x+y+z)^3-x,(x+y+z)-3])
|
|
814
|
+
sage: GF = I.groebner_fan()
|
|
815
|
+
sage: PF = GF.tropical_intersection()
|
|
816
|
+
sage: PF.rays()
|
|
817
|
+
[[-1, 0, 0], [0, -1, 0], [0, 0, -1], [1, 1, 1]]
|
|
818
|
+
sage: RPF = PF.to_RationalPolyhedralFan()
|
|
819
|
+
sage: RPF.Stanley_Reisner_ideal(PolynomialRing(QQ,4,'A, B, C, D'))
|
|
820
|
+
Ideal (A*B, A*C, B*C*D) of Multivariate Polynomial Ring in A, B, C, D over Rational Field
|
|
821
|
+
"""
|
|
822
|
+
self.__is_groebner_basis = is_groebner_basis
|
|
823
|
+
self.__symmetry = symmetry
|
|
824
|
+
if symmetry:
|
|
825
|
+
print("WARNING! Symmetry option not yet implemented!!")
|
|
826
|
+
self.__verbose = verbose
|
|
827
|
+
if not isinstance(I, MPolynomialIdeal):
|
|
828
|
+
raise TypeError("I must be a multivariate polynomial ideal")
|
|
829
|
+
if not prefix_check([str(R_gen) for R_gen in I.ring().gens()]):
|
|
830
|
+
raise RuntimeError("Ring variables cannot contain each other as prefixes")
|
|
831
|
+
S = I.ring()
|
|
832
|
+
R = S.base_ring()
|
|
833
|
+
# todo: add support for ZZ, which only works for bases computation, not tropical intersections
|
|
834
|
+
if not R.is_field():
|
|
835
|
+
raise NotImplementedError("Groebner fan computation only implemented over fields")
|
|
836
|
+
if not (R is QQ or (R.is_finite() and R.is_prime_field() and R.order() <= 32749)):
|
|
837
|
+
# 32749 is previous_prime(2^15)
|
|
838
|
+
raise NotImplementedError("Groebner fan computation only implemented over Q or GF(p) for p <= 32749.")
|
|
839
|
+
if S.ngens() > 52:
|
|
840
|
+
raise NotImplementedError("Groebner fan computation only implemented for rings in at most 52 variables.")
|
|
841
|
+
|
|
842
|
+
self.__ideal = I
|
|
843
|
+
self.__ring = S
|
|
844
|
+
|
|
845
|
+
def _repr_(self) -> str:
|
|
846
|
+
"""
|
|
847
|
+
Describe the Groebner fan and its corresponding ideal.
|
|
848
|
+
|
|
849
|
+
EXAMPLES::
|
|
850
|
+
|
|
851
|
+
sage: R.<q,u> = PolynomialRing(QQ,2)
|
|
852
|
+
sage: gf = R.ideal([q-u,u^2-1]).groebner_fan()
|
|
853
|
+
sage: gf # indirect doctest
|
|
854
|
+
Groebner fan of the ideal:
|
|
855
|
+
Ideal (q - u, u^2 - 1) of Multivariate Polynomial Ring in q, u over Rational Field
|
|
856
|
+
"""
|
|
857
|
+
return "Groebner fan of the ideal:\n{}".format(self.__ideal)
|
|
858
|
+
|
|
859
|
+
def __eq__(self, right) -> bool:
|
|
860
|
+
"""
|
|
861
|
+
Test equality of Groebner fan objects.
|
|
862
|
+
|
|
863
|
+
EXAMPLES::
|
|
864
|
+
|
|
865
|
+
sage: R.<q,u> = PolynomialRing(QQ,2)
|
|
866
|
+
sage: gf = R.ideal([q^2-u,u^2-q]).groebner_fan()
|
|
867
|
+
sage: gf2 = R.ideal([u^2-q,q^2-u]).groebner_fan()
|
|
868
|
+
sage: gf.__eq__(gf2)
|
|
869
|
+
True
|
|
870
|
+
"""
|
|
871
|
+
return type(self) is type(right) and self.ideal() == right.ideal()
|
|
872
|
+
|
|
873
|
+
def ideal(self):
|
|
874
|
+
"""
|
|
875
|
+
Return the ideal the was used to define this Groebner fan.
|
|
876
|
+
|
|
877
|
+
EXAMPLES::
|
|
878
|
+
|
|
879
|
+
sage: R.<x1,x2> = PolynomialRing(QQ,2)
|
|
880
|
+
sage: gf = R.ideal([x1^3-x2,x2^3-2*x1-2]).groebner_fan()
|
|
881
|
+
sage: gf.ideal()
|
|
882
|
+
Ideal (x1^3 - x2, x2^3 - 2*x1 - 2) of Multivariate Polynomial Ring in x1, x2 over Rational Field
|
|
883
|
+
"""
|
|
884
|
+
return self.__ideal
|
|
885
|
+
|
|
886
|
+
@cached_method
|
|
887
|
+
def _gfan_maps(self) -> tuple:
|
|
888
|
+
"""
|
|
889
|
+
OUTPUT:
|
|
890
|
+
|
|
891
|
+
- map from Sage ring to ``gfan`` ring
|
|
892
|
+
|
|
893
|
+
- map from ``gfan`` ring to Sage ring
|
|
894
|
+
|
|
895
|
+
EXAMPLES::
|
|
896
|
+
|
|
897
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
898
|
+
sage: G = R.ideal([x^2*y - z, y^2*z - x, z^2*x - y]).groebner_fan()
|
|
899
|
+
sage: G._gfan_maps()
|
|
900
|
+
(Ring morphism:
|
|
901
|
+
From: Multivariate Polynomial Ring in x, y, z over Rational Field
|
|
902
|
+
To: Multivariate Polynomial Ring in a, b, c over Rational Field
|
|
903
|
+
Defn: x |--> a
|
|
904
|
+
y |--> b
|
|
905
|
+
z |--> c,
|
|
906
|
+
Ring morphism:
|
|
907
|
+
From: Multivariate Polynomial Ring in a, b, c over Rational Field
|
|
908
|
+
To: Multivariate Polynomial Ring in x, y, z over Rational Field
|
|
909
|
+
Defn: a |--> x
|
|
910
|
+
b |--> y
|
|
911
|
+
c |--> z)
|
|
912
|
+
"""
|
|
913
|
+
S = self.__ring
|
|
914
|
+
n = S.ngens()
|
|
915
|
+
|
|
916
|
+
# Define a polynomial ring in n variables
|
|
917
|
+
# that are named a,b,c,d, ..., z, A, B, C, ...
|
|
918
|
+
R = S.base_ring()
|
|
919
|
+
T = PolynomialRing(R, n, string.ascii_letters[:n])
|
|
920
|
+
|
|
921
|
+
# Define the homomorphism that sends the
|
|
922
|
+
# generators of S to the generators of T.
|
|
923
|
+
phi = S.hom(T.gens())
|
|
924
|
+
|
|
925
|
+
# Define the homomorphism that sends the
|
|
926
|
+
# generators of T to the generators of S.
|
|
927
|
+
psi = T.hom(S.gens())
|
|
928
|
+
return (phi, psi)
|
|
929
|
+
|
|
930
|
+
def _gfan_ring(self) -> str:
|
|
931
|
+
"""
|
|
932
|
+
Return the ring in ``gfan`` notation.
|
|
933
|
+
|
|
934
|
+
EXAMPLES::
|
|
935
|
+
|
|
936
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
937
|
+
sage: G = R.ideal([x^2*y - z, y^2*z - x, z^2*x - y]).groebner_fan()
|
|
938
|
+
sage: G._gfan_ring()
|
|
939
|
+
'Q[x, y, z]'
|
|
940
|
+
"""
|
|
941
|
+
return ring_to_gfan_format(self.ring())
|
|
942
|
+
|
|
943
|
+
@cached_method
|
|
944
|
+
def _gfan_ideal(self) -> str:
|
|
945
|
+
"""
|
|
946
|
+
Return the ideal in ``gfan`` notation.
|
|
947
|
+
|
|
948
|
+
EXAMPLES::
|
|
949
|
+
|
|
950
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
951
|
+
sage: G = R.ideal([x^2*y - z, y^2*z - x, z^2*x - y]).groebner_fan()
|
|
952
|
+
sage: G._gfan_ideal()
|
|
953
|
+
'Q[x, y, z]{x^2*y-z,y^2*z-x,x*z^2-y}'
|
|
954
|
+
"""
|
|
955
|
+
return ideal_to_gfan_format(self.ring(),
|
|
956
|
+
self.__ideal.gens())
|
|
957
|
+
|
|
958
|
+
def weight_vectors(self) -> list:
|
|
959
|
+
"""
|
|
960
|
+
Return the weight vectors corresponding to the reduced Groebner
|
|
961
|
+
bases.
|
|
962
|
+
|
|
963
|
+
EXAMPLES::
|
|
964
|
+
|
|
965
|
+
sage: r3.<x,y,z> = PolynomialRing(QQ,3)
|
|
966
|
+
sage: g = r3.ideal([x^3+y,y^3-z,z^2-x]).groebner_fan()
|
|
967
|
+
sage: g.weight_vectors()
|
|
968
|
+
[(3, 7, 1), (5, 1, 2), (7, 1, 4), (5, 1, 4), (1, 1, 1), (1, 4, 8), (1, 4, 10)]
|
|
969
|
+
sage: r4.<x,y,z,w> = PolynomialRing(QQ,4)
|
|
970
|
+
sage: g4 = r4.ideal([x^3+y,y^3-z,z^2-x,z^3 - w]).groebner_fan()
|
|
971
|
+
sage: len(g4.weight_vectors())
|
|
972
|
+
23
|
|
973
|
+
"""
|
|
974
|
+
gfan_processes = Popen(['gfan', '_weightvector', '-m'],
|
|
975
|
+
stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
|
976
|
+
b_ans, _ = gfan_processes.communicate(input=self.gfan().encode("utf8"))
|
|
977
|
+
s_ans = b_ans.decode()
|
|
978
|
+
vect = re.compile(r"\([0-9,/\s]*\)")
|
|
979
|
+
ans = (tup[1:-1].split(',') for tup in vect.findall(s_ans))
|
|
980
|
+
return [vector(QQ, [QQ(y) for y in x]) for x in ans]
|
|
981
|
+
|
|
982
|
+
def ring(self):
|
|
983
|
+
"""
|
|
984
|
+
Return the multivariate polynomial ring.
|
|
985
|
+
|
|
986
|
+
EXAMPLES::
|
|
987
|
+
|
|
988
|
+
sage: R.<x1,x2> = PolynomialRing(QQ,2)
|
|
989
|
+
sage: gf = R.ideal([x1^3-x2,x2^3-x1-2]).groebner_fan()
|
|
990
|
+
sage: gf.ring()
|
|
991
|
+
Multivariate Polynomial Ring in x1, x2 over Rational Field
|
|
992
|
+
"""
|
|
993
|
+
return self.__ring
|
|
994
|
+
|
|
995
|
+
@cached_method
|
|
996
|
+
def _gfan_reduced_groebner_bases(self) -> str:
|
|
997
|
+
"""
|
|
998
|
+
A string of the reduced Groebner bases of the ideal as output by
|
|
999
|
+
``gfan``.
|
|
1000
|
+
|
|
1001
|
+
EXAMPLES::
|
|
1002
|
+
|
|
1003
|
+
sage: R.<a,b> = PolynomialRing(QQ,2)
|
|
1004
|
+
sage: gf = R.ideal([a^3-b^2,b^2-a-1]).groebner_fan()
|
|
1005
|
+
sage: gf._gfan_reduced_groebner_bases()
|
|
1006
|
+
'Q[a,b]{{b^6-1+2*b^2-3*b^4,a+1-b^2},{a^3-1-a,b^2-1-a}}'
|
|
1007
|
+
"""
|
|
1008
|
+
B = self.gfan(cmd='bases')
|
|
1009
|
+
B = B.replace('\n', '')
|
|
1010
|
+
return B
|
|
1011
|
+
|
|
1012
|
+
def characteristic(self):
|
|
1013
|
+
"""
|
|
1014
|
+
Return the characteristic of the base ring.
|
|
1015
|
+
|
|
1016
|
+
EXAMPLES::
|
|
1017
|
+
|
|
1018
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
1019
|
+
sage: i1 = ideal(x*z + 6*y*z - z^2, x*y + 6*x*z + y*z - z^2, y^2 + x*z + y*z)
|
|
1020
|
+
sage: gf = i1.groebner_fan()
|
|
1021
|
+
sage: gf.characteristic()
|
|
1022
|
+
0
|
|
1023
|
+
"""
|
|
1024
|
+
return self.__ring.characteristic()
|
|
1025
|
+
|
|
1026
|
+
@cached_method
|
|
1027
|
+
def reduced_groebner_bases(self):
|
|
1028
|
+
"""
|
|
1029
|
+
EXAMPLES::
|
|
1030
|
+
|
|
1031
|
+
sage: R.<x,y,z> = PolynomialRing(QQ, 3, order='lex')
|
|
1032
|
+
sage: G = R.ideal([x^2*y - z, y^2*z - x, z^2*x - y]).groebner_fan()
|
|
1033
|
+
sage: X = G.reduced_groebner_bases()
|
|
1034
|
+
sage: len(X)
|
|
1035
|
+
33
|
|
1036
|
+
sage: X[0]
|
|
1037
|
+
[z^15 - z, x - z^9, y - z^11]
|
|
1038
|
+
sage: X[0].ideal()
|
|
1039
|
+
Ideal (z^15 - z, x - z^9, y - z^11) of Multivariate Polynomial Ring in x, y, z over Rational Field
|
|
1040
|
+
sage: X[:5]
|
|
1041
|
+
[[z^15 - z, x - z^9, y - z^11],
|
|
1042
|
+
[y^2 - z^8, x - z^9, y*z^4 - z, -y + z^11],
|
|
1043
|
+
[y^3 - z^5, x - y^2*z, y^2*z^3 - y, y*z^4 - z, -y^2 + z^8],
|
|
1044
|
+
[y^4 - z^2, x - y^2*z, y^2*z^3 - y, y*z^4 - z, -y^3 + z^5],
|
|
1045
|
+
[y^9 - z, y^6*z - y, x - y^2*z, -y^4 + z^2]]
|
|
1046
|
+
|
|
1047
|
+
sage: # needs sage.rings.finite_rings
|
|
1048
|
+
sage: R3.<x,y,z> = PolynomialRing(GF(2477),3)
|
|
1049
|
+
sage: gf = R3.ideal([300*x^3 - y, y^2 - z, z^2 - 12]).groebner_fan()
|
|
1050
|
+
sage: gf.reduced_groebner_bases()
|
|
1051
|
+
[[z^2 - 12, y^2 - z, x^3 + 933*y],
|
|
1052
|
+
[y^4 - 12, x^3 + 933*y, -y^2 + z],
|
|
1053
|
+
[x^6 - 1062*z, z^2 - 12, -300*x^3 + y],
|
|
1054
|
+
[x^12 + 200, -300*x^3 + y, -828*x^6 + z]]
|
|
1055
|
+
"""
|
|
1056
|
+
G = self._gfan_reduced_groebner_bases()
|
|
1057
|
+
if G.find(']') != -1:
|
|
1058
|
+
G = G.split(']')[1]
|
|
1059
|
+
G = G.replace('{{', '').replace('}}', '').split('},{')
|
|
1060
|
+
S = self.__ring
|
|
1061
|
+
return [ReducedGroebnerBasis(self, [S(f) for f in G[i].split(',')],
|
|
1062
|
+
G[i]) for i in range(len(G))]
|
|
1063
|
+
|
|
1064
|
+
@cached_method
|
|
1065
|
+
def _gfan_mod(self) -> str:
|
|
1066
|
+
"""
|
|
1067
|
+
Return the extra options to the ``gfan`` command that are used
|
|
1068
|
+
by this object to account for working modulo a prime or in the
|
|
1069
|
+
presence of extra symmetries.
|
|
1070
|
+
|
|
1071
|
+
EXAMPLES::
|
|
1072
|
+
|
|
1073
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1074
|
+
sage: gf = R.ideal([x^3-y,y^3-x-1]).groebner_fan()
|
|
1075
|
+
sage: gf._gfan_mod()
|
|
1076
|
+
''
|
|
1077
|
+
"""
|
|
1078
|
+
mod = ''
|
|
1079
|
+
# p = self.characteristic()
|
|
1080
|
+
# if p:
|
|
1081
|
+
# mod += ' --mod %s' % p
|
|
1082
|
+
# else:
|
|
1083
|
+
# mod += ''
|
|
1084
|
+
|
|
1085
|
+
if self.__is_groebner_basis:
|
|
1086
|
+
mod += ' -g'
|
|
1087
|
+
|
|
1088
|
+
if self.__symmetry:
|
|
1089
|
+
mod += ' --symmetry'
|
|
1090
|
+
|
|
1091
|
+
return mod
|
|
1092
|
+
|
|
1093
|
+
def gfan(self, cmd='bases', I=None, format=None):
|
|
1094
|
+
r"""
|
|
1095
|
+
Return the ``gfan`` output as a string given an input ``cmd``.
|
|
1096
|
+
|
|
1097
|
+
The default is to produce the list of reduced Groebner bases
|
|
1098
|
+
in ``gfan`` format.
|
|
1099
|
+
|
|
1100
|
+
INPUT:
|
|
1101
|
+
|
|
1102
|
+
- ``cmd`` -- string (default: ``'bases'``); GFan command
|
|
1103
|
+
- ``I`` -- ideal (default: ``None``)
|
|
1104
|
+
- ``format`` -- boolean (default: ``None``); deprecated
|
|
1105
|
+
|
|
1106
|
+
EXAMPLES::
|
|
1107
|
+
|
|
1108
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1109
|
+
sage: gf = R.ideal([x^3-y,y^3-x-1]).groebner_fan()
|
|
1110
|
+
sage: gf.gfan()
|
|
1111
|
+
'Q[x,y]\n{{\ny^9-1-y+3*y^3-3*y^6,\nx+1-y^3}\n,\n{\nx^3-y,\ny^3-1-x}\n,\n{\nx^9-1-x,\ny-x^3}\n}\n'
|
|
1112
|
+
"""
|
|
1113
|
+
if format is not None:
|
|
1114
|
+
from sage.misc.superseded import deprecation
|
|
1115
|
+
deprecation(33468, 'argument `format` is ignored in the code: '
|
|
1116
|
+
'it is now deprecated. Please update your code '
|
|
1117
|
+
'without this argument as it will be removed in a later '
|
|
1118
|
+
'version of SageMath.')
|
|
1119
|
+
|
|
1120
|
+
if I is None:
|
|
1121
|
+
I = self._gfan_ideal()
|
|
1122
|
+
# todo -- put something in here (?) when self.__symmetry isn't None...
|
|
1123
|
+
cmd += self._gfan_mod()
|
|
1124
|
+
s = gfan(I, cmd, verbose=self.__verbose)
|
|
1125
|
+
if s.strip() == '{':
|
|
1126
|
+
raise RuntimeError("Error running gfan command %s on %s" % (cmd, self))
|
|
1127
|
+
return s
|
|
1128
|
+
|
|
1129
|
+
def __iter__(self) -> Iterator:
|
|
1130
|
+
"""
|
|
1131
|
+
Return an iterator for the reduced Groebner bases.
|
|
1132
|
+
|
|
1133
|
+
EXAMPLES::
|
|
1134
|
+
|
|
1135
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1136
|
+
sage: gf = R.ideal([x^3-y,y^3-x-1]).groebner_fan()
|
|
1137
|
+
sage: a = gf.__iter__()
|
|
1138
|
+
sage: next(a)
|
|
1139
|
+
[y^9 - 3*y^6 + 3*y^3 - y - 1, -y^3 + x + 1]
|
|
1140
|
+
"""
|
|
1141
|
+
yield from self.reduced_groebner_bases()
|
|
1142
|
+
|
|
1143
|
+
def __getitem__(self, i):
|
|
1144
|
+
"""
|
|
1145
|
+
Get a reduced groebner basis.
|
|
1146
|
+
|
|
1147
|
+
EXAMPLES::
|
|
1148
|
+
|
|
1149
|
+
sage: R4.<w1,w2,w3,w4> = PolynomialRing(QQ,4)
|
|
1150
|
+
sage: gf = R4.ideal([w1^2-w2,w2^3-1,2*w3-w4^2,w4^2-w1]).groebner_fan()
|
|
1151
|
+
sage: gf[0]
|
|
1152
|
+
[w4^12 - 1, -w4^4 + w2, -w4^2 + w1, -1/2*w4^2 + w3]
|
|
1153
|
+
"""
|
|
1154
|
+
return self.reduced_groebner_bases()[i]
|
|
1155
|
+
|
|
1156
|
+
@cached_method
|
|
1157
|
+
def buchberger(self):
|
|
1158
|
+
"""
|
|
1159
|
+
Return a lexicographic reduced Groebner basis for the ideal.
|
|
1160
|
+
|
|
1161
|
+
EXAMPLES::
|
|
1162
|
+
|
|
1163
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
1164
|
+
sage: G = R.ideal([x - z^3, y^2 - x + x^2 - z^3*x]).groebner_fan()
|
|
1165
|
+
sage: G.buchberger()
|
|
1166
|
+
[-z^3 + y^2, -z^3 + x]
|
|
1167
|
+
"""
|
|
1168
|
+
B = self.gfan(cmd='buchberger')
|
|
1169
|
+
if B.find(']') != -1:
|
|
1170
|
+
B = B.split(']')[1]
|
|
1171
|
+
B = B.replace('}', '').replace('{', '')
|
|
1172
|
+
S = self.__ring
|
|
1173
|
+
return [S(f) for f in B.split(',')]
|
|
1174
|
+
|
|
1175
|
+
@cached_method
|
|
1176
|
+
def polyhedralfan(self):
|
|
1177
|
+
"""
|
|
1178
|
+
Return a polyhedral fan object corresponding to the reduced
|
|
1179
|
+
Groebner bases.
|
|
1180
|
+
|
|
1181
|
+
EXAMPLES::
|
|
1182
|
+
|
|
1183
|
+
sage: R3.<x,y,z> = PolynomialRing(QQ,3)
|
|
1184
|
+
sage: gf = R3.ideal([x^8-y^4,y^4-z^2,z^2-1]).groebner_fan()
|
|
1185
|
+
sage: pf = gf.polyhedralfan()
|
|
1186
|
+
sage: pf.rays()
|
|
1187
|
+
[[0, 0, 1], [0, 1, 0], [1, 0, 0]]
|
|
1188
|
+
"""
|
|
1189
|
+
f = self.gfan(cmd='topolyhedralfan', I=self._gfan_reduced_groebner_bases())
|
|
1190
|
+
return PolyhedralFan(f)
|
|
1191
|
+
|
|
1192
|
+
@cached_method
|
|
1193
|
+
def homogeneity_space(self):
|
|
1194
|
+
"""
|
|
1195
|
+
Return the homogeneity space of a the list of polynomials that
|
|
1196
|
+
define this Groebner fan.
|
|
1197
|
+
|
|
1198
|
+
EXAMPLES::
|
|
1199
|
+
|
|
1200
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1201
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1202
|
+
sage: H = G.homogeneity_space()
|
|
1203
|
+
"""
|
|
1204
|
+
return self.gfan(cmd='homogeneityspace')
|
|
1205
|
+
|
|
1206
|
+
def render(self, file=None, larger=False, shift=0, rgbcolor=(0, 0, 0),
|
|
1207
|
+
polyfill=True, scale_colors=True):
|
|
1208
|
+
"""
|
|
1209
|
+
Render a Groebner fan as sage graphics or save as an xfig file.
|
|
1210
|
+
|
|
1211
|
+
More precisely, the output is a drawing of the Groebner fan
|
|
1212
|
+
intersected with a triangle. The corners of the triangle are
|
|
1213
|
+
(1,0,0) to the right, (0,1,0) to the left and (0,0,1) at the top.
|
|
1214
|
+
If there are more than three variables in the ring we extend these
|
|
1215
|
+
coordinates with zeros.
|
|
1216
|
+
|
|
1217
|
+
INPUT:
|
|
1218
|
+
|
|
1219
|
+
- ``file`` -- a filename if you prefer the output
|
|
1220
|
+
saved to a file; this will be in xfig format
|
|
1221
|
+
|
|
1222
|
+
- ``shift`` -- shift the positions of the variables in
|
|
1223
|
+
the drawing. For example, with shift=1, the corners will be b
|
|
1224
|
+
(right), c (left), and d (top). The shifting is done modulo the
|
|
1225
|
+
number of variables in the polynomial ring. The default is 0.
|
|
1226
|
+
|
|
1227
|
+
- ``larger`` -- boolean (default: ``False``); if ``True``, make
|
|
1228
|
+
the triangle larger so that the shape of the Groebner region
|
|
1229
|
+
appears. Affects the xfig file but probably not the sage graphics (?).
|
|
1230
|
+
|
|
1231
|
+
- ``rgbcolor`` -- this will not affect the saved xfig
|
|
1232
|
+
file, only the sage graphics produced
|
|
1233
|
+
|
|
1234
|
+
- ``polyfill`` -- whether or not to fill the cones with
|
|
1235
|
+
a color determined by the highest degree in each reduced Groebner
|
|
1236
|
+
basis for that cone
|
|
1237
|
+
|
|
1238
|
+
- ``scale_colors`` -- if ``True``, this will normalize
|
|
1239
|
+
color values to try to maximize the range
|
|
1240
|
+
|
|
1241
|
+
EXAMPLES::
|
|
1242
|
+
|
|
1243
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
1244
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x,z]).groebner_fan()
|
|
1245
|
+
sage: test_render = G.render() # needs sage.plot
|
|
1246
|
+
|
|
1247
|
+
::
|
|
1248
|
+
|
|
1249
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
1250
|
+
sage: G = R.ideal([x^2*y - z, y^2*z - x, z^2*x - y]).groebner_fan()
|
|
1251
|
+
sage: test_render = G.render(larger=True) # needs sage.plot
|
|
1252
|
+
|
|
1253
|
+
TESTS:
|
|
1254
|
+
|
|
1255
|
+
Testing the case where the number of generators is < 3. Currently,
|
|
1256
|
+
this should raise a :exc:`NotImplementedError`.
|
|
1257
|
+
|
|
1258
|
+
::
|
|
1259
|
+
|
|
1260
|
+
sage: R.<x,y> = PolynomialRing(QQ, 2)
|
|
1261
|
+
sage: R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan().render() # needs sage.plot
|
|
1262
|
+
Traceback (most recent call last):
|
|
1263
|
+
...
|
|
1264
|
+
NotImplementedError
|
|
1265
|
+
"""
|
|
1266
|
+
if polyfill is True:
|
|
1267
|
+
polyfill = max_degree
|
|
1268
|
+
S = self.__ring
|
|
1269
|
+
if S.ngens() < 3:
|
|
1270
|
+
print("For 2-D fan rendering the polynomial ring must have 3 variables (or more, which are ignored).")
|
|
1271
|
+
raise NotImplementedError
|
|
1272
|
+
cmd = 'render'
|
|
1273
|
+
if shift:
|
|
1274
|
+
cmd += ' --shiftVariables %s' % shift
|
|
1275
|
+
if larger:
|
|
1276
|
+
cmd += ' -L'
|
|
1277
|
+
s = self.gfan(cmd, I=self._gfan_reduced_groebner_bases().replace(' ', ','))
|
|
1278
|
+
if file is not None:
|
|
1279
|
+
with open(file, 'w') as f:
|
|
1280
|
+
f.write(s)
|
|
1281
|
+
sp = s.split('\n')
|
|
1282
|
+
sp2 = []
|
|
1283
|
+
for x in sp[9:]:
|
|
1284
|
+
xs = x.split(' ')
|
|
1285
|
+
y = []
|
|
1286
|
+
if x[0:3] != '2 3' and len(xs) > 1:
|
|
1287
|
+
y.extend(q for q in xs if q)
|
|
1288
|
+
sp2.append(y)
|
|
1289
|
+
sp3 = []
|
|
1290
|
+
for j in range(len(sp2)):
|
|
1291
|
+
temp = [[float(sp2[j][i]) / 1200.0,
|
|
1292
|
+
float(sp2[j][i + 1]) / 1200.0]
|
|
1293
|
+
for i in range(0, len(sp2[j]) - 1, 2)]
|
|
1294
|
+
sp3.append(temp)
|
|
1295
|
+
r_lines = Graphics()
|
|
1296
|
+
for x in sp3:
|
|
1297
|
+
r_lines = r_lines + line(x, rgbcolor=rgbcolor)
|
|
1298
|
+
if polyfill:
|
|
1299
|
+
vals = [polyfill(q) for q in self.reduced_groebner_bases()]
|
|
1300
|
+
if isinstance(vals[0], (list, tuple)):
|
|
1301
|
+
if scale_colors:
|
|
1302
|
+
vmins = [min([q[i] for q in vals]) for i in (0, 1, 2)]
|
|
1303
|
+
vmaxs = [max([q[i] for q in vals]) for i in (0, 1, 2)]
|
|
1304
|
+
for i in (0, 1, 2):
|
|
1305
|
+
if vmaxs[i] == vmins[i]:
|
|
1306
|
+
vmaxs[i] = vmins[i] + .01
|
|
1307
|
+
for index, sp in enumerate(sp3):
|
|
1308
|
+
col = [1 - (vals[index][i] - vmins[i]) / (vmaxs[i] - vmins[i]) for i in (0, 1, 2)]
|
|
1309
|
+
r_lines += polygon(sp, rgbcolor=col)
|
|
1310
|
+
else:
|
|
1311
|
+
for index, sp in enumerate(sp3):
|
|
1312
|
+
r_lines += polygon(sp, rgbcolor=vals[index])
|
|
1313
|
+
elif scale_colors:
|
|
1314
|
+
vmin = min(vals)
|
|
1315
|
+
vmax = max(vals)
|
|
1316
|
+
if vmin == vmax:
|
|
1317
|
+
vmax = vmin + .01
|
|
1318
|
+
for index, sp in enumerate(sp3):
|
|
1319
|
+
r_lines += polygon(sp, hue=.1 + .6 * (vals[index] - vmin) / (vmax - vmin))
|
|
1320
|
+
else:
|
|
1321
|
+
for index, sp in enumerate(sp3):
|
|
1322
|
+
r_lines += polygon(sp, hue=vals[index])
|
|
1323
|
+
return r_lines
|
|
1324
|
+
|
|
1325
|
+
def _cone_to_ieq(self, facet_list):
|
|
1326
|
+
"""
|
|
1327
|
+
A simple utility function for converting a facet normal to an
|
|
1328
|
+
inequality form.
|
|
1329
|
+
|
|
1330
|
+
EXAMPLES::
|
|
1331
|
+
|
|
1332
|
+
sage: R.<x,y> = PolynomialRing(QQ,2) # dummy stuff to get a gfan object
|
|
1333
|
+
sage: gf = R.ideal([x^2+y,y^2]).groebner_fan()
|
|
1334
|
+
sage: gf._cone_to_ieq([[1,2,3,4]])
|
|
1335
|
+
[[0, 1, 2, 3, 4]]
|
|
1336
|
+
"""
|
|
1337
|
+
return [[0] + q for q in facet_list]
|
|
1338
|
+
|
|
1339
|
+
def _embed_tetra(self, fpoint):
|
|
1340
|
+
"""
|
|
1341
|
+
Take a 4-d vector and projects it onto the plane perpendicular to
|
|
1342
|
+
(1,1,1,1). Stretches by a factor of 2 as well, since this is only
|
|
1343
|
+
for graphical display purposes.
|
|
1344
|
+
|
|
1345
|
+
INPUT:
|
|
1346
|
+
|
|
1347
|
+
- ``fpoint`` -- list of four numbers
|
|
1348
|
+
|
|
1349
|
+
EXAMPLES::
|
|
1350
|
+
|
|
1351
|
+
sage: R.<x> = PolynomialRing(QQ,1) # dummy stuff to get a gfan object
|
|
1352
|
+
sage: gf = R.ideal([x^2]).groebner_fan()
|
|
1353
|
+
sage: gf._embed_tetra([1/2,1/2,1/2,1/2])
|
|
1354
|
+
[0, 0, 0]
|
|
1355
|
+
"""
|
|
1356
|
+
v1 = [1, 1, -1, -1]
|
|
1357
|
+
v2 = [1, -1, 1, -1]
|
|
1358
|
+
v3 = [-1, 1, 1, -1]
|
|
1359
|
+
x1 = sum([fpoint[ind] * v1[ind] for ind in range(4)])
|
|
1360
|
+
x2 = sum([fpoint[ind] * v2[ind] for ind in range(4)])
|
|
1361
|
+
x3 = sum([fpoint[ind] * v3[ind] for ind in range(4)])
|
|
1362
|
+
return [x1, x2, x3]
|
|
1363
|
+
|
|
1364
|
+
def _4d_to_3d(self, polyhedral_data):
|
|
1365
|
+
"""
|
|
1366
|
+
A utility function that takes a list of 4d polytopes, projects them
|
|
1367
|
+
to 3d, and returns a list of edges.
|
|
1368
|
+
|
|
1369
|
+
INPUT:
|
|
1370
|
+
|
|
1371
|
+
- ``polyhedral_data`` -- an object with 4d vertex and adjacency
|
|
1372
|
+
information
|
|
1373
|
+
|
|
1374
|
+
OUTPUT:
|
|
1375
|
+
|
|
1376
|
+
- ``edges`` -- list of edges in 3d; each list item is a pair of
|
|
1377
|
+
points
|
|
1378
|
+
|
|
1379
|
+
EXAMPLES::
|
|
1380
|
+
|
|
1381
|
+
sage: R4.<w,x,y,z> = PolynomialRing(QQ,4)
|
|
1382
|
+
sage: gf = R4.ideal([w^2-x,x^2-y,y^2-z,z^2-1]).groebner_fan()
|
|
1383
|
+
sage: g_cone = gf[0].groebner_cone()
|
|
1384
|
+
sage: g_cone_facets = g_cone.facets()
|
|
1385
|
+
sage: g_cone_ieqs = gf._cone_to_ieq(g_cone_facets)
|
|
1386
|
+
sage: cone_data = Polyhedron(ieqs = g_cone_ieqs, eqns = [[1,-1,-1,-1,-1]])
|
|
1387
|
+
sage: cone_lines = gf._4d_to_3d(cone_data)
|
|
1388
|
+
sage: cone_lines
|
|
1389
|
+
[[[1, 1, -1], [1, -1/3, 1/3]],
|
|
1390
|
+
[[1, 1, -1], [-1/7, 3/7, 5/7]],
|
|
1391
|
+
[[1, 1, -1], [-3/5, -1/3, -1/5]],
|
|
1392
|
+
[[1, -1/3, 1/3], [-1/7, 3/7, 5/7]],
|
|
1393
|
+
[[1, -1/3, 1/3], [-3/5, -1/3, -1/5]],
|
|
1394
|
+
[[-1/7, 3/7, 5/7], [-3/5, -1/3, -1/5]]]
|
|
1395
|
+
"""
|
|
1396
|
+
fpoints = polyhedral_data.vertices()
|
|
1397
|
+
tpoints = [self._embed_tetra(q) for q in fpoints]
|
|
1398
|
+
edges = []
|
|
1399
|
+
for vertex in polyhedral_data.vertices():
|
|
1400
|
+
i = vertex.index()
|
|
1401
|
+
for adjacent_vertex in vertex.adjacent():
|
|
1402
|
+
j = adjacent_vertex.index()
|
|
1403
|
+
if j > i:
|
|
1404
|
+
try:
|
|
1405
|
+
edges.append([tpoints[i], tpoints[j]])
|
|
1406
|
+
except Exception:
|
|
1407
|
+
print('tpoints: ' + str(tpoints))
|
|
1408
|
+
print('fpoints: ' + str(fpoints))
|
|
1409
|
+
print(adjacent_vertex)
|
|
1410
|
+
print(polyhedral_data.ieqs())
|
|
1411
|
+
raise RuntimeError
|
|
1412
|
+
return edges
|
|
1413
|
+
|
|
1414
|
+
def render3d(self, verbose=False):
|
|
1415
|
+
"""
|
|
1416
|
+
For a Groebner fan of an ideal in a ring with four variables, this
|
|
1417
|
+
function intersects the fan with the standard simplex perpendicular
|
|
1418
|
+
to (1,1,1,1), creating a 3d polytope, which is then projected into
|
|
1419
|
+
3 dimensions. The edges of this projected polytope are returned as
|
|
1420
|
+
lines.
|
|
1421
|
+
|
|
1422
|
+
EXAMPLES::
|
|
1423
|
+
|
|
1424
|
+
sage: R4.<w,x,y,z> = PolynomialRing(QQ,4)
|
|
1425
|
+
sage: gf = R4.ideal([w^2-x,x^2-y,y^2-z,z^2-x]).groebner_fan()
|
|
1426
|
+
sage: three_d = gf.render3d() # needs sage.plot
|
|
1427
|
+
|
|
1428
|
+
TESTS:
|
|
1429
|
+
|
|
1430
|
+
Now test the case where the number of generators is not 4. Currently,
|
|
1431
|
+
this should raise a :exc:`NotImplementedError` error.
|
|
1432
|
+
|
|
1433
|
+
::
|
|
1434
|
+
|
|
1435
|
+
sage: P.<a,b,c> = PolynomialRing(QQ, 3, order='lex')
|
|
1436
|
+
sage: sage.rings.ideal.Katsura(P, 3).groebner_fan().render3d() # needs sage.plot
|
|
1437
|
+
Traceback (most recent call last):
|
|
1438
|
+
...
|
|
1439
|
+
NotImplementedError
|
|
1440
|
+
"""
|
|
1441
|
+
S = self.__ring
|
|
1442
|
+
if S.ngens() != 4:
|
|
1443
|
+
print("For 3-D fan rendering the polynomial ring must have 4 variables")
|
|
1444
|
+
raise NotImplementedError
|
|
1445
|
+
g_cones = [q.groebner_cone() for q in self.reduced_groebner_bases()]
|
|
1446
|
+
g_cones_facets = [q.facets() for q in g_cones]
|
|
1447
|
+
g_cones_ieqs = [self._cone_to_ieq(q) for q in g_cones_facets]
|
|
1448
|
+
# Now the cones are intersected with a plane:
|
|
1449
|
+
cone_info = [Polyhedron(ieqs=q, eqns=[[1, -1, -1, -1, -1]])
|
|
1450
|
+
for q in g_cones_ieqs]
|
|
1451
|
+
# This is really just for debugging
|
|
1452
|
+
if verbose:
|
|
1453
|
+
for x in cone_info:
|
|
1454
|
+
print(x.inequalities() + ([1, 1, 0, 0, 0], [1, 0, 1, 0, 0],
|
|
1455
|
+
[1, 0, 0, 1, 0], [1, 0, 0, 0, 1]))
|
|
1456
|
+
print(x.equations())
|
|
1457
|
+
print()
|
|
1458
|
+
cone_info = [Polyhedron(ieqs=x.inequalities() +
|
|
1459
|
+
([1, 1, 0, 0, 0], [1, 0, 1, 0, 0],
|
|
1460
|
+
[1, 0, 0, 1, 0], [1, 0, 0, 0, 1]),
|
|
1461
|
+
eqns=x.equations()) for x in cone_info]
|
|
1462
|
+
all_lines = []
|
|
1463
|
+
for cone_data in cone_info:
|
|
1464
|
+
try:
|
|
1465
|
+
cone_lines = self._4d_to_3d(cone_data)
|
|
1466
|
+
except Exception:
|
|
1467
|
+
print(cone_data._rays)
|
|
1468
|
+
raise RuntimeError
|
|
1469
|
+
all_lines.extend(a_line for a_line in cone_lines)
|
|
1470
|
+
return sum([line3d(a_line) for a_line in all_lines])
|
|
1471
|
+
|
|
1472
|
+
@cached_method
|
|
1473
|
+
def _gfan_stats(self) -> dict:
|
|
1474
|
+
"""
|
|
1475
|
+
Return various statistics about this Groebner fan.
|
|
1476
|
+
|
|
1477
|
+
EXAMPLES::
|
|
1478
|
+
|
|
1479
|
+
sage: R.<x,y> = PolynomialRing(QQ)
|
|
1480
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1481
|
+
sage: G._gfan_stats()
|
|
1482
|
+
{'Dimension of homogeneity space': 0,
|
|
1483
|
+
'Maximal number of polynomials in Groebner basis': 3,
|
|
1484
|
+
'Maximal number of terms in Groebner basis': 6,
|
|
1485
|
+
'Maximal total degree of a Groebner basis': 4,
|
|
1486
|
+
'Minimal total degree of a Groebner basis': 2,
|
|
1487
|
+
'Number of reduced Groebner bases': 3,
|
|
1488
|
+
'Number of variables': 2}
|
|
1489
|
+
"""
|
|
1490
|
+
s = self.gfan(cmd='stats',
|
|
1491
|
+
I=self._gfan_reduced_groebner_bases().replace(' ', ','))
|
|
1492
|
+
d = {}
|
|
1493
|
+
for v in s.split('\n'):
|
|
1494
|
+
if v:
|
|
1495
|
+
a, b = v.split(':')
|
|
1496
|
+
d[a] = ZZ(b)
|
|
1497
|
+
return d
|
|
1498
|
+
|
|
1499
|
+
def dimension_of_homogeneity_space(self):
|
|
1500
|
+
"""
|
|
1501
|
+
Return the dimension of the homogeneity space.
|
|
1502
|
+
|
|
1503
|
+
EXAMPLES::
|
|
1504
|
+
|
|
1505
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1506
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1507
|
+
sage: G.dimension_of_homogeneity_space()
|
|
1508
|
+
0
|
|
1509
|
+
"""
|
|
1510
|
+
return self._gfan_stats()['Dimension of homogeneity space']
|
|
1511
|
+
|
|
1512
|
+
def maximal_total_degree_of_a_groebner_basis(self):
|
|
1513
|
+
"""
|
|
1514
|
+
Return the maximal total degree of any Groebner basis.
|
|
1515
|
+
|
|
1516
|
+
EXAMPLES::
|
|
1517
|
+
|
|
1518
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1519
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1520
|
+
sage: G.maximal_total_degree_of_a_groebner_basis()
|
|
1521
|
+
4
|
|
1522
|
+
"""
|
|
1523
|
+
return self._gfan_stats()['Maximal total degree of a Groebner basis']
|
|
1524
|
+
|
|
1525
|
+
def minimal_total_degree_of_a_groebner_basis(self):
|
|
1526
|
+
"""
|
|
1527
|
+
Return the minimal total degree of any Groebner basis.
|
|
1528
|
+
|
|
1529
|
+
EXAMPLES::
|
|
1530
|
+
|
|
1531
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1532
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1533
|
+
sage: G.minimal_total_degree_of_a_groebner_basis()
|
|
1534
|
+
2
|
|
1535
|
+
"""
|
|
1536
|
+
return self._gfan_stats()['Minimal total degree of a Groebner basis']
|
|
1537
|
+
|
|
1538
|
+
def number_of_reduced_groebner_bases(self):
|
|
1539
|
+
"""
|
|
1540
|
+
Return the number of reduced Groebner bases.
|
|
1541
|
+
|
|
1542
|
+
EXAMPLES::
|
|
1543
|
+
|
|
1544
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1545
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1546
|
+
sage: G.number_of_reduced_groebner_bases()
|
|
1547
|
+
3
|
|
1548
|
+
"""
|
|
1549
|
+
return self._gfan_stats()['Number of reduced Groebner bases']
|
|
1550
|
+
|
|
1551
|
+
def number_of_variables(self):
|
|
1552
|
+
"""
|
|
1553
|
+
Return the number of variables.
|
|
1554
|
+
|
|
1555
|
+
EXAMPLES::
|
|
1556
|
+
|
|
1557
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1558
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1559
|
+
sage: G.number_of_variables()
|
|
1560
|
+
2
|
|
1561
|
+
|
|
1562
|
+
::
|
|
1563
|
+
|
|
1564
|
+
sage: R = PolynomialRing(QQ,'x',10)
|
|
1565
|
+
sage: R.inject_variables(globals())
|
|
1566
|
+
Defining x0, x1, x2, x3, x4, x5, x6, x7, x8, x9
|
|
1567
|
+
sage: G = ideal([x0 - x9, sum(R.gens())]).groebner_fan()
|
|
1568
|
+
sage: G.number_of_variables()
|
|
1569
|
+
10
|
|
1570
|
+
"""
|
|
1571
|
+
return self.__ring.ngens()
|
|
1572
|
+
|
|
1573
|
+
@cached_method
|
|
1574
|
+
def tropical_basis(self, check=True, verbose=False):
|
|
1575
|
+
"""
|
|
1576
|
+
Return a tropical basis for the tropical curve associated to this
|
|
1577
|
+
ideal.
|
|
1578
|
+
|
|
1579
|
+
INPUT:
|
|
1580
|
+
|
|
1581
|
+
- ``check`` -- boolean (default: ``True``); if ``True`` raises a
|
|
1582
|
+
:exc:`ValueError` exception if this ideal does not define a tropical
|
|
1583
|
+
curve (i.e., the condition that R/I has dimension equal to 1 + the
|
|
1584
|
+
dimension of the homogeneity space is not satisfied)
|
|
1585
|
+
|
|
1586
|
+
EXAMPLES::
|
|
1587
|
+
|
|
1588
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3, order='lex')
|
|
1589
|
+
sage: G = R.ideal([y^3-3*x^2, z^3-x-y-2*y^3+2*x^2]).groebner_fan()
|
|
1590
|
+
sage: G
|
|
1591
|
+
Groebner fan of the ideal:
|
|
1592
|
+
Ideal (-3*x^2 + y^3, 2*x^2 - x - 2*y^3 - y + z^3) of Multivariate Polynomial Ring in x, y, z over Rational Field
|
|
1593
|
+
sage: G.tropical_basis() # needs sage.libs.singular
|
|
1594
|
+
[-3*x^2 + y^3, 2*x^2 - x - 2*y^3 - y + z^3, 3/4*x + y^3 + 3/4*y - 3/4*z^3]
|
|
1595
|
+
"""
|
|
1596
|
+
cmd = 'tropicalbasis'
|
|
1597
|
+
|
|
1598
|
+
I = self.ideal()
|
|
1599
|
+
if not I.is_homogeneous():
|
|
1600
|
+
cmd += ' -h'
|
|
1601
|
+
if check:
|
|
1602
|
+
if I.dimension() != 1 + self.dimension_of_homogeneity_space():
|
|
1603
|
+
raise ValueError("The ideal does not define a tropical curve.")
|
|
1604
|
+
|
|
1605
|
+
B = self.gfan(cmd)
|
|
1606
|
+
if B.find(']') != -1:
|
|
1607
|
+
B = B.split(']')[1]
|
|
1608
|
+
S = self.__ring
|
|
1609
|
+
B = B.replace('\n', '')
|
|
1610
|
+
B = B.replace('{', '').replace('}', '').split(',')
|
|
1611
|
+
if verbose:
|
|
1612
|
+
print(S, B)
|
|
1613
|
+
return [S(f) for f in B]
|
|
1614
|
+
|
|
1615
|
+
def interactive(self, *args, **kwds):
|
|
1616
|
+
"""
|
|
1617
|
+
See the documentation for self[0].interactive().
|
|
1618
|
+
|
|
1619
|
+
This does not work with the notebook.
|
|
1620
|
+
|
|
1621
|
+
EXAMPLES::
|
|
1622
|
+
|
|
1623
|
+
sage: print("This is not easily doc-testable; please write a good one!")
|
|
1624
|
+
This is not easily doc-testable; please write a good one!
|
|
1625
|
+
"""
|
|
1626
|
+
self[0].interactive(*args, **kwds)
|
|
1627
|
+
|
|
1628
|
+
@cached_method
|
|
1629
|
+
def tropical_intersection(self, parameters=None, symmetry_generators=None,
|
|
1630
|
+
*args, **kwds):
|
|
1631
|
+
"""
|
|
1632
|
+
Return information about the tropical intersection of the
|
|
1633
|
+
polynomials defining the ideal.
|
|
1634
|
+
|
|
1635
|
+
This is the common refinement of the outward-pointing normal
|
|
1636
|
+
fans of the Newton polytopes of the generators of the
|
|
1637
|
+
ideal. Note that some people use the inward-pointing normal
|
|
1638
|
+
fans.
|
|
1639
|
+
|
|
1640
|
+
INPUT:
|
|
1641
|
+
|
|
1642
|
+
- ``parameters`` -- (optional) tuple of variables to be
|
|
1643
|
+
considered as parameters
|
|
1644
|
+
- ``symmetry_generators`` -- (optional) generators of the symmetry group
|
|
1645
|
+
|
|
1646
|
+
OUTPUT: a TropicalPrevariety object
|
|
1647
|
+
|
|
1648
|
+
EXAMPLES::
|
|
1649
|
+
|
|
1650
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
1651
|
+
sage: I = R.ideal(x*z + 6*y*z - z^2, x*y + 6*x*z + y*z - z^2, y^2 + x*z + y*z)
|
|
1652
|
+
sage: gf = I.groebner_fan()
|
|
1653
|
+
sage: pf = gf.tropical_intersection()
|
|
1654
|
+
sage: pf.rays()
|
|
1655
|
+
[[-2, 1, 1]]
|
|
1656
|
+
|
|
1657
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
1658
|
+
sage: f1 = x*y*z - 1
|
|
1659
|
+
sage: f2 = f1*(x^2 + y^2 + z^2)
|
|
1660
|
+
sage: f3 = f2*(x + y + z - 1)
|
|
1661
|
+
sage: I = R.ideal([f1,f2,f3])
|
|
1662
|
+
sage: gf = I.groebner_fan()
|
|
1663
|
+
sage: pf = gf.tropical_intersection(symmetry_generators = '(1,2,0),(1,0,2)')
|
|
1664
|
+
sage: pf.rays()
|
|
1665
|
+
[[-2, 1, 1], [1, -2, 1], [1, 1, -2]]
|
|
1666
|
+
|
|
1667
|
+
sage: R.<x,y,z> = QQ[]
|
|
1668
|
+
sage: I = R.ideal([(x+y+z)^2-1,(x+y+z)-x,(x+y+z)-3])
|
|
1669
|
+
sage: GF = I.groebner_fan()
|
|
1670
|
+
sage: TI = GF.tropical_intersection()
|
|
1671
|
+
sage: TI.rays()
|
|
1672
|
+
[[-1, 0, 0], [0, -1, -1], [1, 1, 1]]
|
|
1673
|
+
sage: GF = I.groebner_fan()
|
|
1674
|
+
sage: TI = GF.tropical_intersection(parameters=(y,))
|
|
1675
|
+
sage: TI.rays()
|
|
1676
|
+
[[-1, 0, 0]]
|
|
1677
|
+
"""
|
|
1678
|
+
if parameters is None:
|
|
1679
|
+
parameters = []
|
|
1680
|
+
if symmetry_generators is None:
|
|
1681
|
+
symmetry_generators = []
|
|
1682
|
+
cmd = 'tropicalintersection'
|
|
1683
|
+
id_str = self._gfan_ideal()
|
|
1684
|
+
if parameters:
|
|
1685
|
+
allvars = self.ring().gens()
|
|
1686
|
+
truevars = [q for q in allvars if q not in parameters]
|
|
1687
|
+
base_ring = self.ring().base_ring()
|
|
1688
|
+
new_ring = PolynomialRing(base_ring, len(truevars),
|
|
1689
|
+
",".join(str(q) for q in truevars))
|
|
1690
|
+
old_polys = self.ideal().gens()
|
|
1691
|
+
new_polys = []
|
|
1692
|
+
sub = {v: 1 for v in parameters}
|
|
1693
|
+
for apoly in old_polys:
|
|
1694
|
+
mons = apoly.monomials()
|
|
1695
|
+
mons = [m.subs(sub) for m in mons]
|
|
1696
|
+
new_polys.append(sum(mons))
|
|
1697
|
+
id_str = ideal_to_gfan_format(new_ring, new_polys)
|
|
1698
|
+
if symmetry_generators:
|
|
1699
|
+
cmd = cmd + ' --symmetryExploit'
|
|
1700
|
+
id_str = id_str + '{' + symmetry_generators + '}'
|
|
1701
|
+
f = self.gfan(cmd=cmd, I=id_str)
|
|
1702
|
+
pf = TropicalPrevariety(f, self.ideal().gens(), self.ring(),
|
|
1703
|
+
parameters=parameters)
|
|
1704
|
+
pf._gfan_output = f
|
|
1705
|
+
return pf
|
|
1706
|
+
|
|
1707
|
+
def mixed_volume(self):
|
|
1708
|
+
"""
|
|
1709
|
+
Return the mixed volume of the generators of this ideal.
|
|
1710
|
+
|
|
1711
|
+
This is not really an ideal property, it can depend on the
|
|
1712
|
+
generators used.
|
|
1713
|
+
|
|
1714
|
+
The generators must give a square system (as many polynomials
|
|
1715
|
+
as variables).
|
|
1716
|
+
|
|
1717
|
+
EXAMPLES::
|
|
1718
|
+
|
|
1719
|
+
sage: R.<x,y,z> = QQ[]
|
|
1720
|
+
sage: example_ideal = R.ideal([x^2-y-1,y^2-z-1,z^2-x-1])
|
|
1721
|
+
sage: gf = example_ideal.groebner_fan()
|
|
1722
|
+
sage: mv = gf.mixed_volume()
|
|
1723
|
+
sage: mv
|
|
1724
|
+
8
|
|
1725
|
+
|
|
1726
|
+
sage: R2.<x,y> = QQ[]
|
|
1727
|
+
sage: g1 = 1 - x + x^7*y^3 + 2*x^8*y^4
|
|
1728
|
+
sage: g2 = 2 + y + 3*x^7*y^3 + x^8*y^4
|
|
1729
|
+
sage: example2 = R2.ideal([g1,g2])
|
|
1730
|
+
sage: example2.groebner_fan().mixed_volume()
|
|
1731
|
+
15
|
|
1732
|
+
"""
|
|
1733
|
+
if len(self.ring().variable_names()) != self.ideal().ngens():
|
|
1734
|
+
raise ValueError('not a square system')
|
|
1735
|
+
return Integer(self.gfan(cmd='mixedvolume'))
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
class ReducedGroebnerBasis(SageObject, list):
|
|
1739
|
+
def __init__(self, groebner_fan, gens, gfan_gens) -> None:
|
|
1740
|
+
"""
|
|
1741
|
+
A class for representing reduced Groebner bases as produced by
|
|
1742
|
+
``gfan``.
|
|
1743
|
+
|
|
1744
|
+
INPUT:
|
|
1745
|
+
|
|
1746
|
+
- ``groebner_fan`` -- a GroebnerFan object from an ideal
|
|
1747
|
+
|
|
1748
|
+
- ``gens`` -- the generators of the ideal
|
|
1749
|
+
|
|
1750
|
+
- ``gfan_gens`` -- the generators as a gfan string
|
|
1751
|
+
|
|
1752
|
+
EXAMPLES::
|
|
1753
|
+
|
|
1754
|
+
sage: R.<a,b> = PolynomialRing(QQ,2)
|
|
1755
|
+
sage: gf = R.ideal([a^2-b^2,b-a-1]).groebner_fan()
|
|
1756
|
+
sage: from sage.rings.polynomial.groebner_fan import ReducedGroebnerBasis
|
|
1757
|
+
sage: ReducedGroebnerBasis(gf,gf[0],gf[0]._gfan_gens())
|
|
1758
|
+
[b - 1/2, a + 1/2]
|
|
1759
|
+
"""
|
|
1760
|
+
self.__groebner_fan = groebner_fan
|
|
1761
|
+
list.__init__(self, gens)
|
|
1762
|
+
self.__gfan_gens = '{' + gfan_gens.replace(' ', ',') + '}'
|
|
1763
|
+
self.__ring = groebner_fan._gfan_ring()
|
|
1764
|
+
|
|
1765
|
+
def _repr_(self) -> str:
|
|
1766
|
+
"""
|
|
1767
|
+
Return the reduced Groebner basis as a string.
|
|
1768
|
+
|
|
1769
|
+
EXAMPLES::
|
|
1770
|
+
|
|
1771
|
+
sage: R.<z1,zz1> = PolynomialRing(QQ,2)
|
|
1772
|
+
sage: gf = R.ideal([z1^2*zz1-1,zz1-2]).groebner_fan()
|
|
1773
|
+
sage: rgb1 = gf.reduced_groebner_bases()[0]
|
|
1774
|
+
sage: rgb1 # indirect doctest
|
|
1775
|
+
[zz1 - 2, z1^2 - 1/2]
|
|
1776
|
+
"""
|
|
1777
|
+
return list.__repr__(self)
|
|
1778
|
+
|
|
1779
|
+
def _gfan_gens(self):
|
|
1780
|
+
"""
|
|
1781
|
+
Return the reduced Groebner basis as a string in ``gfan`` format.
|
|
1782
|
+
|
|
1783
|
+
EXAMPLES::
|
|
1784
|
+
|
|
1785
|
+
sage: R.<z1,zz1> = PolynomialRing(QQ,2)
|
|
1786
|
+
sage: gf = R.ideal([z1^2*zz1-1,zz1-2]).groebner_fan()
|
|
1787
|
+
sage: rgb1 = gf.reduced_groebner_bases()[0]
|
|
1788
|
+
sage: rgb1._gfan_gens()
|
|
1789
|
+
'{zz1-2,z1^2-1/2}'
|
|
1790
|
+
"""
|
|
1791
|
+
return self.__gfan_gens
|
|
1792
|
+
|
|
1793
|
+
def _gfan(self):
|
|
1794
|
+
"""
|
|
1795
|
+
Return a description of the Groebner fan this basis was derived
|
|
1796
|
+
from.
|
|
1797
|
+
|
|
1798
|
+
EXAMPLES::
|
|
1799
|
+
|
|
1800
|
+
sage: R.<z1,zz1> = PolynomialRing(QQ,2)
|
|
1801
|
+
sage: gf = R.ideal([z1^2*zz1-1,zz1-2]).groebner_fan()
|
|
1802
|
+
sage: rgb1 = gf.reduced_groebner_bases()[0]
|
|
1803
|
+
sage: rgb1._gfan()
|
|
1804
|
+
Groebner fan of the ideal:
|
|
1805
|
+
Ideal (z1^2*zz1 - 1, zz1 - 2) of Multivariate Polynomial Ring in z1, zz1 over Rational Field
|
|
1806
|
+
"""
|
|
1807
|
+
return self.__groebner_fan
|
|
1808
|
+
|
|
1809
|
+
def interactive(self, latex=False, flippable=False, wall=False,
|
|
1810
|
+
inequalities=False, weight=False):
|
|
1811
|
+
"""
|
|
1812
|
+
Do an interactive walk of the Groebner fan starting at this reduced
|
|
1813
|
+
Groebner basis.
|
|
1814
|
+
|
|
1815
|
+
EXAMPLES::
|
|
1816
|
+
|
|
1817
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1818
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1819
|
+
sage: G[0].interactive() # not tested
|
|
1820
|
+
Initializing gfan interactive mode
|
|
1821
|
+
*********************************************
|
|
1822
|
+
* Press control-C to return to Sage *
|
|
1823
|
+
*********************************************
|
|
1824
|
+
....
|
|
1825
|
+
"""
|
|
1826
|
+
cmd = 'gfan_interactive'
|
|
1827
|
+
if latex:
|
|
1828
|
+
cmd += ' -L'
|
|
1829
|
+
if flippable:
|
|
1830
|
+
cmd += ' -f'
|
|
1831
|
+
if wall:
|
|
1832
|
+
cmd += ' -w'
|
|
1833
|
+
if inequalities:
|
|
1834
|
+
cmd += ' -i'
|
|
1835
|
+
if weight:
|
|
1836
|
+
cmd += ' -W'
|
|
1837
|
+
cmd += self.__groebner_fan._gfan_mod()
|
|
1838
|
+
E = pexpect.spawn(cmd)
|
|
1839
|
+
print("Initializing gfan interactive mode")
|
|
1840
|
+
# E.sendline(self._gfan_ideal())
|
|
1841
|
+
E.sendline(self.__gfan_gens)
|
|
1842
|
+
print("*" * 45)
|
|
1843
|
+
print("* Press control-C to return to Sage *")
|
|
1844
|
+
print("*" * 45)
|
|
1845
|
+
try:
|
|
1846
|
+
E.interact()
|
|
1847
|
+
except OSError:
|
|
1848
|
+
print("Returning to Sage.")
|
|
1849
|
+
|
|
1850
|
+
def groebner_cone(self, restrict=False):
|
|
1851
|
+
"""
|
|
1852
|
+
Return defining inequalities for the full-dimensional Groebner cone
|
|
1853
|
+
associated to this marked minimal reduced Groebner basis.
|
|
1854
|
+
|
|
1855
|
+
INPUT:
|
|
1856
|
+
|
|
1857
|
+
- ``restrict`` -- boolean (default: ``False``); if ``True``, add
|
|
1858
|
+
an inequality for each coordinate, so that the cone is restricted
|
|
1859
|
+
to the positive orthant
|
|
1860
|
+
|
|
1861
|
+
OUTPUT: tuple of integer vectors
|
|
1862
|
+
|
|
1863
|
+
EXAMPLES::
|
|
1864
|
+
|
|
1865
|
+
sage: R.<x,y> = PolynomialRing(QQ,2)
|
|
1866
|
+
sage: G = R.ideal([y^3 - x^2, y^2 - 13*x]).groebner_fan()
|
|
1867
|
+
sage: poly_cone = G[1].groebner_cone()
|
|
1868
|
+
sage: poly_cone.facets()
|
|
1869
|
+
[[-1, 2], [1, -1]]
|
|
1870
|
+
sage: [g.groebner_cone().facets() for g in G]
|
|
1871
|
+
[[[0, 1], [1, -2]], [[-1, 2], [1, -1]], [[-1, 1], [1, 0]]]
|
|
1872
|
+
sage: G[1].groebner_cone(restrict=True).facets()
|
|
1873
|
+
[[-1, 2], [1, -1]]
|
|
1874
|
+
"""
|
|
1875
|
+
try:
|
|
1876
|
+
return self.__groebner_cone[restrict]
|
|
1877
|
+
except AttributeError:
|
|
1878
|
+
self.__groebner_cone = {}
|
|
1879
|
+
except KeyError:
|
|
1880
|
+
pass
|
|
1881
|
+
cmd = 'groebnercone'
|
|
1882
|
+
if restrict:
|
|
1883
|
+
cmd += ' --restrict'
|
|
1884
|
+
gf = self.__groebner_fan
|
|
1885
|
+
c = gf.gfan(cmd=cmd, I=self.__ring + self.__gfan_gens)
|
|
1886
|
+
return PolyhedralCone(c)
|
|
1887
|
+
|
|
1888
|
+
def ideal(self):
|
|
1889
|
+
"""
|
|
1890
|
+
Return the ideal generated by this basis.
|
|
1891
|
+
|
|
1892
|
+
EXAMPLES::
|
|
1893
|
+
|
|
1894
|
+
sage: R.<x,y,z> = PolynomialRing(QQ,3)
|
|
1895
|
+
sage: G = R.ideal([x - z^3, y^2 - 13*x]).groebner_fan()
|
|
1896
|
+
sage: G[0].ideal()
|
|
1897
|
+
Ideal (-13*z^3 + y^2, -z^3 + x) of Multivariate Polynomial Ring in x, y, z over Rational Field
|
|
1898
|
+
"""
|
|
1899
|
+
return self.__groebner_fan.ring().ideal(self)
|