From 06841b5c730adc7c1f0ed98094f328e20a32dfe5 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Tue, 20 Aug 2024 20:42:26 +0700 Subject: [PATCH 01/24] Add ``dual_subdivision`` --- src/sage/rings/semirings/tropical_variety.py | 65 ++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index a8328ee3e48..de6a60b579c 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -531,6 +531,71 @@ def update_result(result): update_result(result) return result + def dual_subdivision(self): + """ + Return the dual subdivision of ``self``. + + Dual subdivision refers to a specific decomposition of the Newton + polygon associated with a tropical polynomial. The term "dual" + is used in the sense that the combinatorial structure of the + tropical variety is reflected in the dual subdivision of the + Newton polygon. Vertices of the dual subdivision correspond to + the intersection of multiple components. Edges of the dual + subdivision correspond to the individual components. + + EXAMPLES: + + Dual subdivision of a tropical curve:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x^2 + y^2 + sage: tv = p1.tropical_variety() + sage: G = tv.dual_subdivision() + sage: G.plot(vertex_labels=False) + Graphics object consisting of 10 graphics primitives + + Dual subdivision of a tropical surface:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x + y + z + x^2 + R(1) + sage: tv = p1.tropical_variety() + sage: G = tv.dual_subdivision() + sage: G.plot3d() + Graphics3d Object + + Dual subdivision of a tropical hypersurface:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = a^2 + b^2 + c^2 + d^2 + a*b*c*d + sage: tv = p1.tropical_variety() + sage: G = tv.dual_subdivision() + sage: G.plot(vertex_labels=False) + Graphics object consisting of 11 graphics primitives + + """ + from sage.graphs.graph import Graph + + G = Graph() + edges = [e for e in self._keys] + # for edge in self._keys: + # edges.append(edge) + G.add_edges(edges) + pos = {} + for vertex in G.vertices(): + pos[vertex] = list(vertex) + + if self._poly.parent().ngens() == 2: + G.layout(pos=pos, save_pos=True) + elif self._poly.parent().ngens() == 3: + G.layout(dim=3, save_pos=True) + G._pos3d = pos + else: + G.layout("spring", save_pos=True) + return G + class TropicalSurface(TropicalVariety): r""" From 67343198836d2e2382ed096a269583eeee93e82e Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Tue, 20 Aug 2024 21:04:05 +0700 Subject: [PATCH 02/24] Add ``_components_of_vertices`` and ``weight_vectors`` --- src/sage/rings/semirings/tropical_variety.py | 120 ++++++++++++++++++- 1 file changed, 117 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index de6a60b579c..9dbade7e52f 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -530,7 +530,7 @@ def update_result(result): points[i] = new_eq update_result(result) return result - + def dual_subdivision(self): """ Return the dual subdivision of ``self``. @@ -580,8 +580,6 @@ def dual_subdivision(self): G = Graph() edges = [e for e in self._keys] - # for edge in self._keys: - # edges.append(edge) G.add_edges(edges) pos = {} for vertex in G.vertices(): @@ -1074,6 +1072,122 @@ def vertices(self): vertices.add((x,y)) return vertices + def _components_of_vertices(self): + """ + Return the index of components adjacent to each vertex of ``self``. + + OUTPUT: + + A dictionary where the keys represent the vertices, and the values + are lists of tuples. Each tuple consists of the index of an + adjacent edge (component) `e_i` and a string indicating the + directionality of `e_i` relative to the vertex. The string is + either "pos" or "neg", specifying whether it is positive or + negative. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(0) + x + y + x*y + x^2*y + x*y^2 + sage: p1.tropical_variety()._components_of_vertices() + {(0, 0): [(0, 'pos'), (1, 'pos'), (2, 'pos'), (3, 'neg'), (4, 'neg')]} + sage: p2 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: p2.tropical_variety()._components_of_vertices() + {(-2, 0): [(0, 'neg'), (1, 'pos'), (3, 'pos')], + (-1, -3): [(2, 'neg'), (4, 'pos'), (5, 'pos')], + (-1, 0): [(3, 'neg'), (4, 'neg'), (6, 'pos')], + (3, 4): [(6, 'neg'), (7, 'pos'), (8, 'pos')]} + """ + comp_vert = {} + if len(self._hypersurface) >= 3: + for i, component in enumerate(self._hypersurface): + parametric_function = component[0] + v = component[1][0].variables()[0] + interval = self._parameter_intervals()[i] + lower = interval[0].lower() + upper = interval[0].upper() + if lower != -infinity: + x = parametric_function[0].subs(v==lower) + y = parametric_function[1].subs(v==lower) + if (x,y) not in comp_vert: + comp_vert[(x,y)] = [(i, 'pos')] + else: + comp_vert[(x,y)].append((i, 'pos')) + if upper != infinity: + x = parametric_function[0].subs(v==upper) + y = parametric_function[1].subs(v==upper) + if (x,y) not in comp_vert: + comp_vert[(x,y)] = [(i, 'neg')] + else: + comp_vert[(x,y)].append((i, 'neg')) + return comp_vert + + def weight_vectors(self): + r""" + Return the weight vectors for all vertices of ``self``. + + Suppose `v` is a vertex adjacent to the edges `e_1, ldots, e_k` + with respective weights `w_1, ldots, w_k`. Every edge `e_i` is + contained in a line (component) defined by an equation with + integer coefficients. Because of this there exists a unique + integer vector `v_i=(\alpha, \beta)` in the direction of `e_i` + such that `\gcd(\alpha, \beta)=1`. Then each vertex `v` yield + the vectors `w_1v_1,ldots,w_kv_k`. These vectors will satisfy + the following balancing condition: + `\sum_{i=1}^k w_i v_i = 0`. + + OUTPUT: + + A dictionary where the keys represent the vertices, and the values + are lists of vectors. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(-2)*x^2 + R(-1)*x + R(1/2)*y + R(1/6) + sage: p1.tropical_variety().weight_vectors() + {(1, -1/2): [(0, 1), (-1, -2), (1, 1)], + (7/6, -1/3): [(-1, -1), (0, 1), (1, 0)]} + sage: p3 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: p3.tropical_variety().weight_vectors() + {(-2, 0): [(-1, -1), (0, 1), (1, 0)], + (-1, -3): [(-1, -1), (0, 1), (1, 0)], + (-1, 0): [(-1, 0), (0, -1), (1, 1)], + (3, 4): [(-1, -1), (0, 1), (1, 0)]} + """ + from sage.calculus.functional import diff + from sage.arith.misc import gcd + from sage.rings.rational_field import QQ + from sage.modules.free_module_element import vector + + if not self._components_of_vertices(): + return {} + + # finding the base vector in the direction of each edges + temp_vectors = [] + par = self._hypersurface[0][1][0].variables()[0] + for comp in self._hypersurface: + dx = diff(comp[0][0], par) + dy = diff(comp[0][1], par) + multiplier = gcd(QQ(dx), QQ(dy)) + temp_vectors.append(vector([dx/multiplier, dy/multiplier])) + + # calculate the weight vectors of each vertex + cov = self._components_of_vertices() + result = {} + for vertex in cov: + vectors = [] + for comp in cov[vertex]: + weight = self._hypersurface[comp[0]][2] + if comp[1] == 'pos': + vectors.append(weight*temp_vectors[comp[0]]) + else: + vectors.append(weight*(-temp_vectors[comp[0]])) + result[vertex] = vectors + return result + def _parameter_intervals(self): r""" Return the intervals of each component's parameter of ``self``. From 8b8c5cc6110865c4391a8b741d71561df1688677 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Thu, 22 Aug 2024 02:42:41 +0700 Subject: [PATCH 03/24] Add ``is_smooth``, ``is_simple``, ``genus``, ``contribution`` --- src/sage/rings/semirings/tropical_variety.py | 128 +++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 9dbade7e52f..df507b828cd 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -1188,6 +1188,134 @@ def weight_vectors(self): result[vertex] = vectors return result + def is_smooth(self): + r""" + Return ``True`` if ``self`` is smooth and ``False`` otherwise. + + Suppose `C` is a tropical curve of degree `d`. A tropical curve + `C` is smooth if the dual subdivision of `C` consists of `d^2` + triangles each having unit area `1/2`. This is equivalent with + `C` having `d^2` vertices. These vertices are necessarily + trivalent (has three adjacent edges). + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x^2 + x + R(1) + sage: p1.tropical_variety().is_smooth() + False + sage: p2 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: p2.tropical_variety().is_smooth() + True + """ + if len(self.vertices()) == self._poly.degree()**2: + return True + return False + + def is_simple(self): + r""" + Return ``True`` if ``self`` is simple and ``False`` otherwise. + + A tropical curve `C` is called simple if each vertex is either + trivalent or is locally the intersection of two line segments. + Equivalently, `C` is simple if the corresponding subdivision + consists only of triangles and parallelograms. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(0) + x + y + x*y + x^2*y + x*y^2 + sage: p1.tropical_variety().is_simple() + False + sage: p2 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: p2.tropical_variety().is_simple() + True + """ + vov = self.weight_vectors() + for vertex in self.vertices(): + if len(vov[vertex]) > 4: + return False + elif len(vov[vertex]) == 4: + for v in vov[vertex]: + if -v not in vov[vertex]: + return False + return True + + def genus(self): + r""" + Return the genus of ``self``. + + Let `t(C)` be the number of trivalent vertices, and let `r(C)` be + the number of unbounded edges of `C`. The genus of simple tropical + curve `C` is defined by the formula: + `g(C) = \frac{1}{2}t(C) - \frac{1}{2}r(C) + 1`. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x^2 + y^2 + x*y + sage: p1.tropical_variety().genus() + 1 + sage: p2 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: p2.tropical_variety().genus() + 0 + """ + if not self.is_simple(): + raise ValueError("tropical curve is not simple") + trivalent = 0 # number of trivalent vertices + for vectors in self.weight_vectors().values(): + if len(vectors) == 3: + trivalent += 1 + unbounded = 0 # number of unbounded edges + for component in self._hypersurface: + if len(component[1]) == 1: + unbounded += 1 + return trivalent//2 - unbounded//2 + 1 + + def contribution(self): + r""" + Return the contribution of ``self``. + + The contribution of a simple curve `C` is defined as the product + of the normalized areas of all triangles in the corresponding + dual subdivision. We just multiply positive integers attached to + the trivalent vertices. The contribution of a trivalent vertex + equals `w_1w_2|\det(v_1,v_2)|`, with `w_i` are the weights of + the adjacent edges and `v_i` are their weight vectors. That + formula is independent of the choice made because of the + balancing condition `w_1v_1+w_2v_2+w_3v_3=0`. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: p1.tropical_variety().contribution() + 1 + sage: p2 = R(-1/3)*x^2 + R(1)*x*y + R(1)*y^2 + R(-1/3)*x + R(1/3) + sage: p2.tropical_variety().contribution() + 16 + """ + if not self.is_simple(): + raise ValueError("tropical curve is not simple") + result = 1 + voc = self._components_of_vertices() + vov = self.weight_vectors() + for vertex in vov: + if len(vov[vertex]) == 3: + u1 = vov[vertex][0] + u2 = vov[vertex][1] + index1 = voc[vertex][0][0] + index2 = voc[vertex][1][0] + w1 = self._hypersurface[index1][2] + w2 = self._hypersurface[index2][2] + det = u1[0]*u2[1] - u1[1]*u2[0] + result *= w1 * w2 * abs(det) + return result + def _parameter_intervals(self): r""" Return the intervals of each component's parameter of ``self``. From 5c9e8d1b7ae9c0bf9786336a36c92b332cbc97fa Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Thu, 22 Aug 2024 03:10:17 +0700 Subject: [PATCH 04/24] Add ``weight_vectors`` in tropical surface --- src/sage/rings/semirings/tropical_variety.py | 137 +++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index df507b828cd..0a6c00349c6 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -952,6 +952,143 @@ def _repr_(self): Tropical surface of 0*x^4 + 0*z^2 """ return f"Tropical surface of {self._poly}" + + def weight_vectors(self): + r""" + Return the weight vectors for each edge of ``self``. + + Suppose `L` is an edge adjacent to the surface `S_1, ldots, S_k` + with respective weights `w_1, ldots, w_k`. This edge `L` has + a direction vector `d=[d_1,d_2,d_3]`. Each surface + `S_1, ldots, S_k` has a normal vector `n_1, \ldots, n_k`. + Make sure that the normal vector is scale to an integer vector + `n_k=(\alpha, \beta, \gamma)` such that + `\gcd(\alpha, \beta, \gamma)=1`. The weight vector of a surface + with respect to line `L` can be calculated with + `v_k=w_k \cdot (d\times n_k). These vectors will satisfy + the following balancing condition: + `\sum_{i=1}^k v_k = 0`. + + OUTPUT: + + A dictionary where the keys represent the vertices, and the values + are lists of vectors. + + EXAMPLES:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x^2 + y^2 + z^3 + sage: tv1 = p1.tropical_variety() + sage: tv1.weight_vectors() + [[((3/2*t2, 3/2*t2, t2), {t2 < +Infinity}), + [(-2, -2, 6), (-9/2, 13/2, -3), (13/2, -9/2, -3)]]] + + sage: p2 = x + y + z + x^2 + R(1) + sage: tv2 = p2.tropical_variety() + sage: tv2.weight_vectors() + [[((t2, t2, t2), {0 <= t2, t2 <= 1}), [(-1, -1, 2), (-1, 2, -1), (2, -1, -1)]], + [((0, 0, t2), {0 <= t2}), [(-1, -1, 0), (0, -1, 0), (1, 2, 0)]], + [((1, 1, t2), {1 <= t2}), [(1, 1, 0), (0, -1, 0), (-1, 0, 0)]], + [((0, t2, 0), {0 <= t2}), [(1, 0, 1), (0, 0, 1), (-1, 0, -2)]], + [((1, t2, 1), {1 <= t2}), [(-1, 0, -1), (0, 0, 1), (1, 0, 0)]], + [((t1, 1, 1), {1 <= t1}), [(0, -1, -1), (0, 0, 1), (0, 1, 0)]], + [((t1, 2*t1, 2*t1), {t1 <= 0}), [(4, -1, -1), (-2, -4, 5), (-2, 5, -4)]]] + """ + from sage.symbolic.ring import SR + from sage.symbolic.relation import solve + from sage.calculus.functional import diff + from sage.arith.misc import gcd + from sage.rings.rational_field import QQ + from sage.modules.free_module_element import vector + + t = SR.var('t') + CI = self._components_intersection() + unique_line = set() + index_line = {} + line_comps = {} + index = 0 + + # Identify the distinct line of intersection and determine which + # components contain this line. + for i, lines in CI.items(): + for line in lines: + v1 = next(iter(line[1])).variables()[0] + eqn = line[0] + is_unique = True + for uniq in unique_line: + subs_index = -1 + for j in range(3): + if eqn[j] != uniq[j]: + subs_index = j + break + if subs_index == -1: + new_line = eqn + is_unique = False + break + eq1 = eqn[subs_index].subs(v1 == t) + eq2 = uniq[subs_index] + temp_sol = solve(eq1 == eq2, t) + if temp_sol: + temp_sol = temp_sol[0].rhs() + if not temp_sol.is_numeric(): + new_line = [] + for l in eqn: + new_line.append(l.subs(v1 == temp_sol)) + if tuple(new_line) in unique_line: + is_unique = False + break + if is_unique: + unique_line.add(eqn) + index_line[index] = line + line_comps[index] = [i] + index += 1 + else: + match_key = [k for k, v in index_line.items() if v[0] == tuple(new_line)][0] + line_comps[match_key].append(i) + + WV = {i: [] for i in range(len(line_comps))} + for k, v in line_comps.items(): + + # Calculate direction vector of the line + dir_vec = [] + line = index_line[k][0] + for l in line: + if l.variables(): + vpar = l.variables()[0] + break + for l in line: + dir_vec.append(QQ(diff(l, vpar))) + dir_vec = vector(dir_vec) + + # Calculate the outgoing normal vector of each surface in the + # direction of the line + for i in v: + surface = self._hypersurface[i][0] + vec1, vec2 = [], [] + for s in surface: + vec1.append(QQ(diff(s, self._vars[0]))) + vec2.append(QQ(diff(s, self._vars[1]))) + vector1 = vector(vec1) + vector2 = vector(vec2) + nor_vec = vector1.cross_product(vector2) + nor_vec *= 1/gcd(nor_vec) + weight_vec = nor_vec.cross_product(dir_vec) + weight_vec = vector([QQ(w) for w in weight_vec]) + order = self._hypersurface[i][2] + weight_vec *= order + WV[k].append(weight_vec) + for i in range(len(WV[k])): + test_vectors = [v for v in WV[k]] + test_vectors[i] = -test_vectors[i] + if sum(test_vectors) == vector([0,0,0]): + WV[k] = test_vectors + break + + result = [] + for k, v in WV.items(): + result.append([index_line[k], v]) + return result class TropicalCurve(TropicalVariety): From 010149c3e2c97d0df868787cb1b66c7ecd72549b Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Thu, 22 Aug 2024 22:43:58 +0700 Subject: [PATCH 05/24] Add ``PLOT`` section in dual subdvision and add one reference --- src/doc/en/reference/references/index.rst | 3 ++ src/sage/rings/semirings/tropical_variety.py | 43 +++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index d7fe85422ba..88702eecdc7 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4648,6 +4648,9 @@ REFERENCES: University Press, New York, 1995, With contributions by A. Zelevinsky, Oxford Science Publications. +.. [Mac2015] Diane Maclagan and Bernd Sturmfels, *Introduction to + Tropical Geometry*, American Mathematical Society, 2015. + .. [MagmaHGM] *Hypergeometric motives* in Magma, http://magma.maths.usyd.edu.au/~watkins/papers/HGM-chapter.pdf diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 0a6c00349c6..25df68ce78d 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -12,6 +12,7 @@ REFERENCES: - [Bru2014]_ +- [Mac2015]_ - [Fil2017]_ """ @@ -555,6 +556,17 @@ def dual_subdivision(self): sage: G.plot(vertex_labels=False) Graphics object consisting of 10 graphics primitives + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x**2 + y**2 + tv = p1.tropical_variety() + G = tv.dual_subdivision() + sphinx_plot(G.plot(vertex_labels=False)) + Dual subdivision of a tropical surface:: sage: T = TropicalSemiring(QQ) @@ -565,6 +577,17 @@ def dual_subdivision(self): sage: G.plot3d() Graphics3d Object + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y,z')) + x, y, z = R.gen(), R.gen(1), R.gen(2) + p1 = x + y + z + x**2 + R(1) + tv = p1.tropical_variety() + G = tv.dual_subdivision() + sphinx_plot(G.plot3d()) + Dual subdivision of a tropical hypersurface:: sage: T = TropicalSemiring(QQ) @@ -575,6 +598,16 @@ def dual_subdivision(self): sage: G.plot(vertex_labels=False) Graphics object consisting of 11 graphics primitives + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('a,b,c,d')) + a, b, c, d = R.gen(), R.gen(1), R.gen(2), R.gen(3) + p1 = a**2 + b**2 + c**2 + d**2 + a*b*c*d + tv = p1.tropical_variety() + G = tv.dual_subdivision() + sphinx_plot(G.plot(vertex_labels=False)) """ from sage.graphs.graph import Graph @@ -965,9 +998,8 @@ def weight_vectors(self): `n_k=(\alpha, \beta, \gamma)` such that `\gcd(\alpha, \beta, \gamma)=1`. The weight vector of a surface with respect to line `L` can be calculated with - `v_k=w_k \cdot (d\times n_k). These vectors will satisfy - the following balancing condition: - `\sum_{i=1}^k v_k = 0`. + `v_k=d\times n_k. These vectors will satisfy the following + balancing condition: `\sum_{i=1}^k w_k v_k = 0`. OUTPUT: @@ -1287,8 +1319,9 @@ def weight_vectors(self): sage: p1.tropical_variety().weight_vectors() {(1, -1/2): [(0, 1), (-1, -2), (1, 1)], (7/6, -1/3): [(-1, -1), (0, 1), (1, 0)]} - sage: p3 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) - sage: p3.tropical_variety().weight_vectors() + + sage: p2 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) + sage: p2.tropical_variety().weight_vectors() {(-2, 0): [(-1, -1), (0, 1), (1, 0)], (-1, -3): [(-1, -1), (0, 1), (1, 0)], (-1, 0): [(-1, 0), (0, -1), (1, 1)], From 3f1641af2a3baeeaeb8c27348a9df02043d7918b Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Fri, 23 Aug 2024 16:41:21 +0700 Subject: [PATCH 06/24] Add ``TESTS`` to genus and contribution --- src/sage/rings/semirings/tropical_variety.py | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 25df68ce78d..f31496ee1fe 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -1432,9 +1432,19 @@ def genus(self): sage: p2 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) sage: p2.tropical_variety().genus() 0 + + TESTS:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(0) + y + x^2*y + x*y^2 + sage: p1.tropical_variety().genus() + Traceback (most recent call last): + ... + ValueError: Tropical curve of 0*x^2*y + 0*x*y^2 + 0*y + 0 is not simple """ if not self.is_simple(): - raise ValueError("tropical curve is not simple") + raise ValueError(f"{self} is not simple") trivalent = 0 # number of trivalent vertices for vectors in self.weight_vectors().values(): if len(vectors) == 3: @@ -1468,9 +1478,19 @@ def contribution(self): sage: p2 = R(-1/3)*x^2 + R(1)*x*y + R(1)*y^2 + R(-1/3)*x + R(1/3) sage: p2.tropical_variety().contribution() 16 + + TESTS:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(0) + x + x^2*y + x*y^2 + sage: p1.tropical_variety().contribution() + Traceback (most recent call last): + ... + ValueError: Tropical curve of 0*x^2*y + 0*x*y^2 + 0*x + 0 is not simple """ if not self.is_simple(): - raise ValueError("tropical curve is not simple") + raise ValueError(f"{self} is not simple") result = 1 voc = self._components_of_vertices() vov = self.weight_vectors() From 4795639815688cfc0200da52c14f80d269471a10 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Fri, 23 Aug 2024 21:55:56 +0700 Subject: [PATCH 07/24] Small fix on style and white spaces --- src/sage/rings/semirings/tropical_variety.py | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index f31496ee1fe..560b8b76a33 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -531,7 +531,7 @@ def update_result(result): points[i] = new_eq update_result(result) return result - + def dual_subdivision(self): """ Return the dual subdivision of ``self``. @@ -935,7 +935,7 @@ def plot(self, color='random'): p1 = x + z sphinx_plot(p1.tropical_variety().plot()) - A tropical surface with multiple cell that exhibit complex and + A tropical surface with multiple cells that exhibit complex and intriguing geometric structures:: sage: p2 = x^2 + x + y + z + R(1) @@ -985,7 +985,7 @@ def _repr_(self): Tropical surface of 0*x^4 + 0*z^2 """ return f"Tropical surface of {self._poly}" - + def weight_vectors(self): r""" Return the weight vectors for each edge of ``self``. @@ -1058,7 +1058,7 @@ def weight_vectors(self): new_line = eqn is_unique = False break - eq1 = eqn[subs_index].subs(v1 == t) + eq1 = eqn[subs_index].subs(**{str(v1): t}) eq2 = uniq[subs_index] temp_sol = solve(eq1 == eq2, t) if temp_sol: @@ -1066,7 +1066,7 @@ def weight_vectors(self): if not temp_sol.is_numeric(): new_line = [] for l in eqn: - new_line.append(l.subs(v1 == temp_sol)) + new_line.append(l.subs(**{str(v1): temp_sol})) if tuple(new_line) in unique_line: is_unique = False break @@ -1247,11 +1247,11 @@ def _components_of_vertices(self): OUTPUT: - A dictionary where the keys represent the vertices, and the values - are lists of tuples. Each tuple consists of the index of an - adjacent edge (component) `e_i` and a string indicating the - directionality of `e_i` relative to the vertex. The string is - either "pos" or "neg", specifying whether it is positive or + A dictionary where the keys represent the vertices, and the + values are lists of tuples. Each tuple consists of the index + of an adjacent edge (component) `e_i` and a string indicating + the directionality of `e_i` relative to the vertex. The string + is either "pos" or "neg", specifying whether it is positive or negative. EXAMPLES:: @@ -1277,15 +1277,15 @@ def _components_of_vertices(self): lower = interval[0].lower() upper = interval[0].upper() if lower != -infinity: - x = parametric_function[0].subs(v==lower) - y = parametric_function[1].subs(v==lower) + x = parametric_function[0].subs(**{str(v): lower}) + y = parametric_function[1].subs(**{str(v): lower}) if (x,y) not in comp_vert: comp_vert[(x,y)] = [(i, 'pos')] else: comp_vert[(x,y)].append((i, 'pos')) if upper != infinity: - x = parametric_function[0].subs(v==upper) - y = parametric_function[1].subs(v==upper) + x = parametric_function[0].subs(**{str(v): upper}) + y = parametric_function[1].subs(**{str(v): upper}) if (x,y) not in comp_vert: comp_vert[(x,y)] = [(i, 'neg')] else: From a601cbdd44670ab8c3de1d08d044ab7a9b4717cb Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Sun, 25 Aug 2024 13:47:40 +0700 Subject: [PATCH 08/24] Refactor ``vertices`` in tropical curve --- src/sage/rings/semirings/tropical_variety.py | 37 ++++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 560b8b76a33..03e961623d4 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -265,7 +265,7 @@ def __init__(self, poly): temp_order.append(order) temp_keys.append(keys) - # Changing all the operator symbol to be <= or >= + # Changing all the operator's symbol to <= or >= self._keys = [] components = [] dim_param = 0 @@ -1223,25 +1223,9 @@ def vertices(self): """ if len(self._hypersurface) < 3: return set() + return set(self._vertices_components().keys()) - vertices = set() - for i, component in enumerate(self._hypersurface): - parametric_function = component[0] - var = component[1][0].variables()[0] - interval = self._parameter_intervals()[i] - lower = interval[0].lower() - upper = interval[0].upper() - if lower != -infinity: - x = parametric_function[0].subs(**{str(var): lower}) - y = parametric_function[1].subs(**{str(var): lower}) - vertices.add((x,y)) - if upper != infinity: - x = parametric_function[0].subs(**{str(var): upper}) - y = parametric_function[1].subs(**{str(var): upper}) - vertices.add((x,y)) - return vertices - - def _components_of_vertices(self): + def _vertices_components(self): """ Return the index of components adjacent to each vertex of ``self``. @@ -1259,10 +1243,10 @@ def _components_of_vertices(self): sage: T = TropicalSemiring(QQ) sage: R. = PolynomialRing(T) sage: p1 = R(0) + x + y + x*y + x^2*y + x*y^2 - sage: p1.tropical_variety()._components_of_vertices() + sage: p1.tropical_variety()._vertices_components() {(0, 0): [(0, 'pos'), (1, 'pos'), (2, 'pos'), (3, 'neg'), (4, 'neg')]} sage: p2 = R(2)*x^2 + x*y + R(2)*y^2 + x + R(-1)*y + R(3) - sage: p2.tropical_variety()._components_of_vertices() + sage: p2.tropical_variety()._vertices_components() {(-2, 0): [(0, 'neg'), (1, 'pos'), (3, 'pos')], (-1, -3): [(2, 'neg'), (4, 'pos'), (5, 'pos')], (-1, 0): [(3, 'neg'), (4, 'neg'), (6, 'pos')], @@ -1329,13 +1313,12 @@ def weight_vectors(self): """ from sage.calculus.functional import diff from sage.arith.misc import gcd - from sage.rings.rational_field import QQ from sage.modules.free_module_element import vector - if not self._components_of_vertices(): + if not self._vertices_components(): return {} - # finding the base vector in the direction of each edges + # Finding the base vector in the direction of each edge temp_vectors = [] par = self._hypersurface[0][1][0].variables()[0] for comp in self._hypersurface: @@ -1344,8 +1327,8 @@ def weight_vectors(self): multiplier = gcd(QQ(dx), QQ(dy)) temp_vectors.append(vector([dx/multiplier, dy/multiplier])) - # calculate the weight vectors of each vertex - cov = self._components_of_vertices() + # Calculate the weight vectors of each vertex + cov = self._vertices_components() result = {} for vertex in cov: vectors = [] @@ -1492,7 +1475,7 @@ def contribution(self): if not self.is_simple(): raise ValueError(f"{self} is not simple") result = 1 - voc = self._components_of_vertices() + voc = self._vertices_components() vov = self.weight_vectors() for vertex in vov: if len(vov[vertex]) == 3: From 2e8592c1e6c487867567797bc17828debb3cd230 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Sun, 25 Aug 2024 18:49:19 +0700 Subject: [PATCH 09/24] Move ``weight_vectors`` from ``TropicalSurface`` to ``TropicalVariety`` --- .../rings/semirings/tropical_mpolynomial.py | 2 +- src/sage/rings/semirings/tropical_variety.py | 354 +++++++++++------- 2 files changed, 211 insertions(+), 145 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index a482e097f89..50b15f45776 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -284,7 +284,7 @@ def plot3d(self, color='random'): T = self.parent().base() R = self.base_ring().base_ring() - # Finding the point of curve that touch the edge of the axes + # Find the point of curve that touch the edge of the axes for comp in tv.components(): if len(comp[1]) == 1: valid_int = RealSet(comp[1][0]) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 03e961623d4..fad657ee871 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -27,9 +27,11 @@ # **************************************************************************** from sage.structure.sage_object import SageObject -from sage.rings.infinity import infinity from sage.structure.unique_representation import UniqueRepresentation +from sage.rings.rational_field import QQ +from sage.rings.infinity import infinity + class TropicalVariety(UniqueRepresentation, SageObject): r""" A tropical variety in `\RR^n`. @@ -478,6 +480,7 @@ def update_result(result): sol_param_sim.add(eqn.lhs() >= eqn.rhs()) else: sol_param_sim.add(sol) + # Checking there are no conditions with the same variables # that use the <= and >= operators simultaneously unique_sol_param = set() @@ -627,6 +630,205 @@ def dual_subdivision(self): G.layout("spring", save_pos=True) return G + def weight_vectors(self): + r""" + Return the weight vectors for each unique intesection of + components of ``self``. + + Assume ``self`` is a `n`-dimensional tropical variety. + Suppose `L` is an intersection adjacent to the components + `S_1, ldots, S_k` with respective weights `w_1, ldots, w_k`. + This `L` is a linear structure in `\RR^{n-1}` and has `n-1` + direction vectors `d_1,d_2,\dots, d_{n-1}`. Each component + `S_1, ldots, S_k` has a normal vector `n_1, \ldots, n_k`. + Make sure that the normal vector is scale to an integer vector + such that the greatest common divisor of its elements is 1. + + The weight vector of a component `S_i` with respect to `L` + can be found by calculating the cross product between direction + vectors of `L` and normal vector `n_i`.These vectors will + satisfy the following balancing condition: + `\sum_{i=1}^k w_k v_k = 0`. + + OUTPUT: + + A tuple of two dictionaries: + - The first dictionary contains equations representing the + intersections. + - The second dictionary contains lists of vectors. + + EXAMPLES: + + Weight vectors of tropical surface:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p = x^2 + R(-1)*y + z + R(1) + sage: tv = p.tropical_variety() + sage: tv.weight_vectors() + ({0: ((1/2*u2, u2 + 1, u2), {u2 <= 1}), + 1: ((1/2, 2, u2), {1 <= u2}), + 2: ((1/2, u2, 1), {2 <= u2}), + 3: ((u1, 2, 1), {(1/2) <= u1})}, + {0: [(1, 2, -5/2), (1, -5/2, 2), (-2, 1/2, 1/2)], + 1: [(-1, -2, 0), (0, 2, 0), (1, 0, 0)], + 2: [(1, 0, 2), (0, 0, -2), (-1, 0, 0)], + 3: [(0, 1, 1), (0, 0, -1), (0, -1, 0)]}) + + Weight vectors of tropical hypersurface:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = R(2)*a*b + R(3)*a*c + R(-1)*c^2 + R(-1/3)*a*d + sage: tv = p1.tropical_variety() + sage: tv.weight_vectors() + ({0: ((u1, u3 - 7/3, u3 - 10/3, u3), {u1 <= u3 - 22/3}), + 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), + 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), + 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, + {0: [(0, 1, 1, -2), (0, 1, -2, 1), (0, -2, 1, 1)], + 1: [(-2, 1, 1, 0), (3, -3, 0, 0), (-1, 2, -1, 0)], + 2: [(-1, 5, 2, -6), (2, 1, -4, 1), (-1, -6, 2, 5)], + 3: [(-1, 0, -1, 2), (-2, 0, 1, 1), (3, 0, 0, -3)]}) + """ + from sage.symbolic.ring import SR + from sage.symbolic.relation import solve + from sage.calculus.functional import diff + from sage.arith.misc import gcd + from sage.matrix.constructor import matrix + from sage.modules.free_module_element import vector, zero_vector + + dim = self.dimension() + t = SR.var('t') + t_vars = [SR.var('t{}'.format(i)) for i in range(dim)] + u_vars = [SR.var('u{}'.format(i)) for i in range(dim)] + convert_tu = {ti: ui for ti, ui in zip(t_vars, u_vars)} + CI = self._components_intersection() + unique_line = set() + index_line = {} + line_comps = {} + index = 0 + + # Find the unique intersection between multiple components and + # the indices of the components containing this intersection. + for i, lines in CI.items(): + for line in lines: + eqn = line[0] + is_unique = True + for uniq in unique_line: + subs_index = -1 + for j in range(dim): + if eqn[j] != uniq[j]: + subs_index = j + break + if subs_index == -1: + new_line = eqn + is_unique = False + break + subs_dict = {} + while len(subs_dict) != dim-2 and subs_index < dim: + eq1 = eqn[subs_index].subs(subs_dict) + vib = None + for unk in eq1.variables(): + if unk not in subs_dict: + if unk in t_vars: + vib = unk + break + if vib: + eq1 = eq1.subs(**{str(vib): t}) + eq2 = uniq[subs_index] + temp_sol = solve(eq1 == eq2, t) + if temp_sol: + temp_sol = temp_sol[0].rhs() + if not temp_sol.is_numeric(): + subs_dict[vib] = temp_sol + subs_index += 1 + if subs_dict: + new_line = [] + for l in eqn: + for key, value in subs_dict.items(): + l = l.subs(key == value) + new_line.append(l) + if tuple(new_line) in unique_line: + is_unique = False + break + if is_unique: + new_eqn = [eq.subs(convert_tu) for eq in eqn] + new_eqn = tuple(new_eqn) + cdns = line[1] + new_cdn = [cdn.subs(convert_tu) for cdn in cdns] + new_cdn = set(new_cdn) + unique_line.add(new_eqn) + index_line[index] = tuple([new_eqn, new_cdn]) + line_comps[index] = [i] + index += 1 + else: + match_key = [k for k, v in index_line.items() if v[0] == tuple(new_line)][0] + line_comps[match_key].append(i) + + WV = {i: [] for i in range(len(line_comps))} + for k, index in line_comps.items(): + + # Calculate direction vector of the line + dir_vecs = [] + line = index_line[k][0] + all_var = set() + for l in line: + for v in l.variables(): + all_var.add(v) + for vpar in all_var: + par_drv = [] + for l in line: + par_drv.append(QQ(diff(l, vpar))) + par_drv = vector(par_drv) + dir_vecs.append(par_drv) + + # Calculate the outgoing normal vector of each surface in the + # direction of the line + for i in index: + surface = self._hypersurface[i][0] + drv_vectors = [] + for vpar in self._vars: + temp_vec = [] + for s in surface: + temp_vec.append(QQ(diff(s, vpar))) + temp_vec = vector(temp_vec) + drv_vectors.append(temp_vec) + temp = [t_vars] + for vec in drv_vectors: + temp.append(vec) + vec_matrix = matrix(SR, temp) + normal_vec = vec_matrix.det() + temp_nor = [] + for tvar in t_vars: + temp_nor.append(QQ(diff(normal_vec, tvar))) + normal_vec = vector(temp_nor) + normal_vec *= 1/gcd(normal_vec) + + # Calculate the weight vector + temp_final = [t_vars] + for v in dir_vecs: + temp_final.append(v) + temp_final.append(normal_vec) + vec_matrix = matrix(SR, temp_final) + weight_vec = vec_matrix.det() + temp_weight = [] + for tvar in t_vars: + temp_weight.append(QQ(diff(weight_vec, tvar))) + weight_vec = vector(temp_weight) + order = self._hypersurface[i][2] + weight_vec *= order + WV[k].append(weight_vec) + + for i in range(len(WV[k])): + test_vectors = [v for v in WV[k]] + test_vectors[i] = -test_vectors[i] + if sum(test_vectors) == zero_vector(QQ, dim): + WV[k] = test_vectors + break + + return index_line, WV + class TropicalSurface(TropicalVariety): r""" @@ -742,7 +944,7 @@ def _axes(self): v_set = v_set.union(temp_v) axes = [[min(u_set)-1, max(u_set)+1], [min(v_set)-1, max(v_set)+1]] - # Finding the z-axis + # Calculate the z-axis step = 10 du = (axes[0][1]-axes[0][0]) / step dv = (axes[1][1]-axes[1][0]) / step @@ -807,9 +1009,8 @@ def _polygon_vertices(self): 7: {(-1/2, -1, -1), (-1/2, 2, -1), (0, 0, 0), (0, 2, 0)}, 8: {(1, 1, 1), (1, 2, 1), (2, 1, 1), (2, 2, 1)}} """ - from sage.sets.real_set import RealSet from sage.symbolic.relation import solve - from sage.rings.rational_field import QQ + from sage.sets.real_set import RealSet poly_verts = {i: set() for i in range(self.number_of_components())} axes = self._axes() @@ -817,7 +1018,7 @@ def _polygon_vertices(self): vars = self._vars comps_int = self._components_intersection() - # Finding the inside vertices + # Find the inside vertices (intersection of components) for index, lines in comps_int.items(): for line in lines: v = list(line[1])[0].variables()[0] @@ -896,7 +1097,8 @@ def find_edge_vertices(i): sol1 = solve(point >= axes[i][0], pv) sol2 = solve(point <= axes[i][1], pv) is_doublevar = True - # Finding the edge vertices (those that touch the axes) + + # Find the edge vertices (those that touch the axes) find_edge_vertices(0) # t1 fixed find_edge_vertices(1) # t2 fixed return poly_verts @@ -986,142 +1188,6 @@ def _repr_(self): """ return f"Tropical surface of {self._poly}" - def weight_vectors(self): - r""" - Return the weight vectors for each edge of ``self``. - - Suppose `L` is an edge adjacent to the surface `S_1, ldots, S_k` - with respective weights `w_1, ldots, w_k`. This edge `L` has - a direction vector `d=[d_1,d_2,d_3]`. Each surface - `S_1, ldots, S_k` has a normal vector `n_1, \ldots, n_k`. - Make sure that the normal vector is scale to an integer vector - `n_k=(\alpha, \beta, \gamma)` such that - `\gcd(\alpha, \beta, \gamma)=1`. The weight vector of a surface - with respect to line `L` can be calculated with - `v_k=d\times n_k. These vectors will satisfy the following - balancing condition: `\sum_{i=1}^k w_k v_k = 0`. - - OUTPUT: - - A dictionary where the keys represent the vertices, and the values - are lists of vectors. - - EXAMPLES:: - - sage: T = TropicalSemiring(QQ) - sage: R. = PolynomialRing(T) - sage: p1 = x^2 + y^2 + z^3 - sage: tv1 = p1.tropical_variety() - sage: tv1.weight_vectors() - [[((3/2*t2, 3/2*t2, t2), {t2 < +Infinity}), - [(-2, -2, 6), (-9/2, 13/2, -3), (13/2, -9/2, -3)]]] - - sage: p2 = x + y + z + x^2 + R(1) - sage: tv2 = p2.tropical_variety() - sage: tv2.weight_vectors() - [[((t2, t2, t2), {0 <= t2, t2 <= 1}), [(-1, -1, 2), (-1, 2, -1), (2, -1, -1)]], - [((0, 0, t2), {0 <= t2}), [(-1, -1, 0), (0, -1, 0), (1, 2, 0)]], - [((1, 1, t2), {1 <= t2}), [(1, 1, 0), (0, -1, 0), (-1, 0, 0)]], - [((0, t2, 0), {0 <= t2}), [(1, 0, 1), (0, 0, 1), (-1, 0, -2)]], - [((1, t2, 1), {1 <= t2}), [(-1, 0, -1), (0, 0, 1), (1, 0, 0)]], - [((t1, 1, 1), {1 <= t1}), [(0, -1, -1), (0, 0, 1), (0, 1, 0)]], - [((t1, 2*t1, 2*t1), {t1 <= 0}), [(4, -1, -1), (-2, -4, 5), (-2, 5, -4)]]] - """ - from sage.symbolic.ring import SR - from sage.symbolic.relation import solve - from sage.calculus.functional import diff - from sage.arith.misc import gcd - from sage.rings.rational_field import QQ - from sage.modules.free_module_element import vector - - t = SR.var('t') - CI = self._components_intersection() - unique_line = set() - index_line = {} - line_comps = {} - index = 0 - - # Identify the distinct line of intersection and determine which - # components contain this line. - for i, lines in CI.items(): - for line in lines: - v1 = next(iter(line[1])).variables()[0] - eqn = line[0] - is_unique = True - for uniq in unique_line: - subs_index = -1 - for j in range(3): - if eqn[j] != uniq[j]: - subs_index = j - break - if subs_index == -1: - new_line = eqn - is_unique = False - break - eq1 = eqn[subs_index].subs(**{str(v1): t}) - eq2 = uniq[subs_index] - temp_sol = solve(eq1 == eq2, t) - if temp_sol: - temp_sol = temp_sol[0].rhs() - if not temp_sol.is_numeric(): - new_line = [] - for l in eqn: - new_line.append(l.subs(**{str(v1): temp_sol})) - if tuple(new_line) in unique_line: - is_unique = False - break - if is_unique: - unique_line.add(eqn) - index_line[index] = line - line_comps[index] = [i] - index += 1 - else: - match_key = [k for k, v in index_line.items() if v[0] == tuple(new_line)][0] - line_comps[match_key].append(i) - - WV = {i: [] for i in range(len(line_comps))} - for k, v in line_comps.items(): - - # Calculate direction vector of the line - dir_vec = [] - line = index_line[k][0] - for l in line: - if l.variables(): - vpar = l.variables()[0] - break - for l in line: - dir_vec.append(QQ(diff(l, vpar))) - dir_vec = vector(dir_vec) - - # Calculate the outgoing normal vector of each surface in the - # direction of the line - for i in v: - surface = self._hypersurface[i][0] - vec1, vec2 = [], [] - for s in surface: - vec1.append(QQ(diff(s, self._vars[0]))) - vec2.append(QQ(diff(s, self._vars[1]))) - vector1 = vector(vec1) - vector2 = vector(vec2) - nor_vec = vector1.cross_product(vector2) - nor_vec *= 1/gcd(nor_vec) - weight_vec = nor_vec.cross_product(dir_vec) - weight_vec = vector([QQ(w) for w in weight_vec]) - order = self._hypersurface[i][2] - weight_vec *= order - WV[k].append(weight_vec) - for i in range(len(WV[k])): - test_vectors = [v for v in WV[k]] - test_vectors[i] = -test_vectors[i] - if sum(test_vectors) == vector([0,0,0]): - WV[k] = test_vectors - break - - result = [] - for k, v in WV.items(): - result.append([index_line[k], v]) - return result - class TropicalCurve(TropicalVariety): r""" @@ -1312,13 +1378,13 @@ def weight_vectors(self): (3, 4): [(-1, -1), (0, 1), (1, 0)]} """ from sage.calculus.functional import diff - from sage.arith.misc import gcd from sage.modules.free_module_element import vector + from sage.arith.misc import gcd if not self._vertices_components(): return {} - # Finding the base vector in the direction of each edge + # Calculate the base vector in the direction of each edge temp_vectors = [] par = self._hypersurface[0][1][0].variables()[0] for comp in self._hypersurface: From 96a0f4cdffb401933a00a498101573be9bf8994f Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Sun, 25 Aug 2024 19:17:03 +0700 Subject: [PATCH 10/24] Small fix on example --- src/sage/rings/semirings/tropical_variety.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index fad657ee871..4b11043bebe 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -686,10 +686,10 @@ def weight_vectors(self): 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, - {0: [(0, 1, 1, -2), (0, 1, -2, 1), (0, -2, 1, 1)], - 1: [(-2, 1, 1, 0), (3, -3, 0, 0), (-1, 2, -1, 0)], - 2: [(-1, 5, 2, -6), (2, 1, -4, 1), (-1, -6, 2, 5)], - 3: [(-1, 0, -1, 2), (-2, 0, 1, 1), (3, 0, 0, -3)]}) + {0: [(0, -1, -1, 2), (0, -1, 2, -1), (0, 2, -1, -1)], + 1: [(2, -1, -1, 0), (-3, 3, 0, 0), (1, -2, 1, 0)], + 2: [(1, -5, -2, 6), (-2, -1, 4, -1), (1, 6, -2, -5)], + 3: [(1, 0, 1, -2), (2, 0, -1, -1), (-3, 0, 0, 3)]}) """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve From ec4307da18e3973e81d9ff98fa1e4ab7a088c43e Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Sun, 25 Aug 2024 19:56:47 +0700 Subject: [PATCH 11/24] Another fix --- src/sage/rings/semirings/tropical_variety.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 4b11043bebe..cd2d9bc84b5 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -683,13 +683,13 @@ def weight_vectors(self): sage: tv = p1.tropical_variety() sage: tv.weight_vectors() ({0: ((u1, u3 - 7/3, u3 - 10/3, u3), {u1 <= u3 - 22/3}), - 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), - 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), - 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, - {0: [(0, -1, -1, 2), (0, -1, 2, -1), (0, 2, -1, -1)], - 1: [(2, -1, -1, 0), (-3, 3, 0, 0), (1, -2, 1, 0)], - 2: [(1, -5, -2, 6), (-2, -1, 4, -1), (1, 6, -2, -5)], - 3: [(1, 0, 1, -2), (2, 0, -1, -1), (-3, 0, 0, 3)]}) + 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), + 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), + 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, + {0: [(0, 1, 1, -2), (0, 1, -2, 1), (0, -2, 1, 1)], + 1: [(-2, 1, 1, 0), (3, -3, 0, 0), (-1, 2, -1, 0)], + 2: [(-1, 5, 2, -6), (2, 1, -4, 1), (-1, -6, 2, 5)], + 3: [(-1, 0, -1, 2), (-2, 0, 1, 1), (3, 0, 0, -3)]}) """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve From 401ad51e6b1f15684419960778f35870f1e72aa9 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Mon, 26 Aug 2024 12:17:02 +0700 Subject: [PATCH 12/24] Fix docstring in ``weight_vectors`` --- src/sage/rings/semirings/tropical_variety.py | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index cd2d9bc84b5..d77cde29d47 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -643,19 +643,17 @@ def weight_vectors(self): `S_1, ldots, S_k` has a normal vector `n_1, \ldots, n_k`. Make sure that the normal vector is scale to an integer vector such that the greatest common divisor of its elements is 1. - + The weight vector of a component `S_i` with respect to `L` can be found by calculating the cross product between direction vectors of `L` and normal vector `n_i`.These vectors will - satisfy the following balancing condition: - `\sum_{i=1}^k w_k v_k = 0`. + satisfy the balancing condition `\sum_{i=1}^k w_k v_k = 0`. OUTPUT: - A tuple of two dictionaries: - - The first dictionary contains equations representing the - intersections. - - The second dictionary contains lists of vectors. + A tuple of two dictionaries. The first dictionary contains + equations representing the intersections. The second dictionary + contains lists of vectors. EXAMPLES: @@ -683,13 +681,13 @@ def weight_vectors(self): sage: tv = p1.tropical_variety() sage: tv.weight_vectors() ({0: ((u1, u3 - 7/3, u3 - 10/3, u3), {u1 <= u3 - 22/3}), - 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), - 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), - 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, + 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), + 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), + 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, {0: [(0, 1, 1, -2), (0, 1, -2, 1), (0, -2, 1, 1)], - 1: [(-2, 1, 1, 0), (3, -3, 0, 0), (-1, 2, -1, 0)], - 2: [(-1, 5, 2, -6), (2, 1, -4, 1), (-1, -6, 2, 5)], - 3: [(-1, 0, -1, 2), (-2, 0, 1, 1), (3, 0, 0, -3)]}) + 1: [(-2, 1, 1, 0), (3, -3, 0, 0), (-1, 2, -1, 0)], + 2: [(-1, 5, 2, -6), (2, 1, -4, 1), (-1, -6, 2, 5)], + 3: [(-1, 0, -1, 2), (-2, 0, 1, 1), (3, 0, 0, -3)]}) """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve From 792b8298667c72ff6a7e5d52374afd6c16ce7d3e Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Fri, 30 Aug 2024 17:08:52 +0700 Subject: [PATCH 13/24] Change output of dual subdivision from Graph to PolyhedralComplex --- .../rings/semirings/tropical_mpolynomial.py | 2 +- src/sage/rings/semirings/tropical_variety.py | 63 ++++++++----------- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 50b15f45776..65354b18142 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -359,7 +359,7 @@ def tropical_variety(self): curve. For dimensions higher than two, it is referred to as a tropical hypersurface. - OUTPUT: a :class:`sage.rings.semirings.tropical_variety.TropicalVariety` + OUTPUT: :class:`sage.rings.semirings.tropical_variety.TropicalVariety` EXAMPLES: diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index d77cde29d47..4438a04588d 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -152,10 +152,10 @@ class TropicalVariety(UniqueRepresentation, SageObject): a tropical hypersurface embedded in a real space `\RR^n`:: sage: T = TropicalSemiring(QQ) - sage: R. = PolynomialRing(T) - sage: p1 = x*y + R(-1/2)*x*z + R(4)*z^2 + a*x + sage: R. = PolynomialRing(T) + sage: p1 = x*y + R(-1/2)*x*z + R(4)*z^2 + w*x sage: tv = p1.tropical_variety(); tv - Tropical hypersurface of 0*a*x + 0*x*y + (-1/2)*x*z + 4*z^2 + Tropical hypersurface of 0*w*x + 0*x*y + (-1/2)*x*z + 4*z^2 sage: tv.components() [[(t1, t2, t3 - 1/2, t3), [t2 - 9/2 <= t3, t3 <= t1 + 1/2, t2 - 5 <= t1], 1], [(t1, 2*t2 - t3 + 4, t3, t2), [t3 + 1/2 <= t2, t3 <= t1], 1], @@ -547,6 +547,8 @@ def dual_subdivision(self): the intersection of multiple components. Edges of the dual subdivision correspond to the individual components. + OUTPUT: :class:`sage.geometry.polyhedral_complex.PolyhedralComplex` + EXAMPLES: Dual subdivision of a tropical curve:: @@ -555,9 +557,9 @@ def dual_subdivision(self): sage: R. = PolynomialRing(T) sage: p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x^2 + y^2 sage: tv = p1.tropical_variety() - sage: G = tv.dual_subdivision() - sage: G.plot(vertex_labels=False) - Graphics object consisting of 10 graphics primitives + sage: pc = tv.dual_subdivision() + sage: pc.plot() + Graphics object consisting of 20 graphics primitives .. PLOT:: :width: 300 px @@ -567,8 +569,8 @@ def dual_subdivision(self): x, y = R.gen(), R.gen(1) p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x**2 + y**2 tv = p1.tropical_variety() - G = tv.dual_subdivision() - sphinx_plot(G.plot(vertex_labels=False)) + pc = tv.dual_subdivision() + sphinx_plot(pc.plot()) Dual subdivision of a tropical surface:: @@ -576,8 +578,8 @@ def dual_subdivision(self): sage: R. = PolynomialRing(T) sage: p1 = x + y + z + x^2 + R(1) sage: tv = p1.tropical_variety() - sage: G = tv.dual_subdivision() - sage: G.plot3d() + sage: pc = tv.dual_subdivision() + sage: pc.plot() Graphics3d Object .. PLOT:: @@ -588,8 +590,8 @@ def dual_subdivision(self): x, y, z = R.gen(), R.gen(1), R.gen(2) p1 = x + y + z + x**2 + R(1) tv = p1.tropical_variety() - G = tv.dual_subdivision() - sphinx_plot(G.plot3d()) + pc = tv.dual_subdivision() + sphinx_plot(pc.plot()) Dual subdivision of a tropical hypersurface:: @@ -597,38 +599,23 @@ def dual_subdivision(self): sage: R. = PolynomialRing(T) sage: p1 = a^2 + b^2 + c^2 + d^2 + a*b*c*d sage: tv = p1.tropical_variety() - sage: G = tv.dual_subdivision() - sage: G.plot(vertex_labels=False) - Graphics object consisting of 11 graphics primitives - - .. PLOT:: - :width: 300 px - - T = TropicalSemiring(QQ, use_min=False) - R = PolynomialRing(T, ('a,b,c,d')) - a, b, c, d = R.gen(), R.gen(1), R.gen(2), R.gen(3) - p1 = a**2 + b**2 + c**2 + d**2 + a*b*c*d - tv = p1.tropical_variety() - G = tv.dual_subdivision() - sphinx_plot(G.plot(vertex_labels=False)) + sage: pc = tv.dual_subdivision(); pc + Polyhedral complex with 6 maximal cells """ from sage.graphs.graph import Graph + from sage.geometry.polyhedron.constructor import Polyhedron + from sage.geometry.polyhedral_complex import PolyhedralComplex G = Graph() edges = [e for e in self._keys] G.add_edges(edges) - pos = {} - for vertex in G.vertices(): - pos[vertex] = list(vertex) - - if self._poly.parent().ngens() == 2: - G.layout(pos=pos, save_pos=True) - elif self._poly.parent().ngens() == 3: - G.layout(dim=3, save_pos=True) - G._pos3d = pos - else: - G.layout("spring", save_pos=True) - return G + + polyhedron_lst = [] + for cycle in G.cycle_basis(): + polyhedron = Polyhedron(vertices=cycle) + polyhedron_lst.append(polyhedron) + pc = PolyhedralComplex(polyhedron_lst) + return pc def weight_vectors(self): r""" From dd2d14d7184450be2a8e105593cae9893192a624 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Fri, 30 Aug 2024 22:18:35 +0700 Subject: [PATCH 14/24] Refine docstring --- src/sage/rings/semirings/tropical_mpolynomial.py | 2 -- src/sage/rings/semirings/tropical_polynomial.py | 6 ++---- src/sage/rings/semirings/tropical_variety.py | 12 +++++++----- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 65354b18142..e6744baf7ef 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -11,8 +11,6 @@ sage: R. = PolynomialRing(T) sage: z.parent() Multivariate Tropical Polynomial Semiring in x, y, z over Rational Field - sage: R(2)*x + R(-1)*x + R(5)*y + R(-3) - (-1)*x + 5*y + (-3) sage: (x+y+z)^2 0*x^2 + 0*x*y + 0*y^2 + 0*x*z + 0*y*z + 0*z^2 diff --git a/src/sage/rings/semirings/tropical_polynomial.py b/src/sage/rings/semirings/tropical_polynomial.py index f1d41c17ee0..cefe97c533a 100644 --- a/src/sage/rings/semirings/tropical_polynomial.py +++ b/src/sage/rings/semirings/tropical_polynomial.py @@ -11,10 +11,8 @@ sage: R. = PolynomialRing(T) sage: x.parent() Univariate Tropical Polynomial Semiring in x over Rational Field - sage: (x + R(3)*x) * (x^2 + x) - 3*x^3 + 3*x^2 - sage: (x^2 + R(1)*x + R(-1))^2 - 0*x^4 + 1*x^3 + 2*x^2 + 0*x + (-2) + sage: (x^2 + x + R(0))^2 + 0*x^4 + 0*x^3 + 0*x^2 + 0*x + 0 REFERENCES: diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 4438a04588d..23149e970dd 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -1574,7 +1574,7 @@ def _parameter_intervals(self): intervals.append(interval) return intervals - def plot(self): + def plot(self, dominant_term=False): """ Return the plot of ``self``. @@ -1631,8 +1631,9 @@ def plot(self): Another tropical polynomial with numerous components, resulting in a more intricate structure:: - sage: p2 = (x^6 + R(4)*x^4*y^2 + R(2)*x^3*y^3 + R(3)*x^2*y^4 + x*y^5 - ....: + R(7)*x^2 + R(5)*x*y + R(3)*y^2 + R(2)*x + y + R(10)) + sage: p2 = (R(8) + R(4)*x + R(2)*y + R(1)*x^2 + x*y + R(1)*y^2 + ....: + R(2)*x^3 + x^2*y + x*y^2 + R(4)*y^3 + R(8)*x^4 + ....: + R(4)*x^3*y + x^2*y^2 + R(2)*x*y^3 + y^4) sage: p2.tropical_variety().plot() Graphics object consisting of 11 graphics primitives @@ -1642,8 +1643,9 @@ def plot(self): T = TropicalSemiring(QQ) R = PolynomialRing(T, ('x,y')) x, y = R.gen(), R.gen(1) - p2 = x**6 + R(4)*x**4*y**2 + R(2)*x**3*y**3 + R(3)*x**2*y**4 + \ - x*y**5 + R(7)*x**2 + R(5)*x*y + R(3)*y**2 + R(2)*x + y + R(10) + p2 = R(8) + R(4)*x + R(2)*y + R(1)*x**2 + x*y + R(1)*y**2 \ + + R(2)*x**3 + x**2*y + x*y**2 + R(4)*y**3 + R(8)*x**4 \ + + R(4)*x**3*y + x**2*y**2 + R(2)*x*y**3 + y**4 sphinx_plot(p2.tropical_variety().plot()) """ from sage.plot.plot import plot From f20838a8ae0d9375523f007a0f63c6fd74843b2c Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Fri, 30 Aug 2024 22:21:25 +0700 Subject: [PATCH 15/24] Small fix on example --- src/sage/rings/semirings/tropical_variety.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 23149e970dd..c2f92405fd7 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -1635,7 +1635,7 @@ def plot(self, dominant_term=False): ....: + R(2)*x^3 + x^2*y + x*y^2 + R(4)*y^3 + R(8)*x^4 ....: + R(4)*x^3*y + x^2*y^2 + R(2)*x*y^3 + y^4) sage: p2.tropical_variety().plot() - Graphics object consisting of 11 graphics primitives + Graphics object consisting of 23 graphics primitives .. PLOT:: :width: 300 px From f425552d05b93842109734a2d8b86e5238755927 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Fri, 30 Aug 2024 23:09:23 +0700 Subject: [PATCH 16/24] Refine the docstring more --- .../rings/semirings/tropical_mpolynomial.py | 2 +- .../rings/semirings/tropical_polynomial.py | 8 ++-- src/sage/rings/semirings/tropical_variety.py | 44 ++++++++++++------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index e6744baf7ef..3d06fa1a7d8 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -593,7 +593,7 @@ def random_element(self, degree=2, terms=None, choose_degree=False, r""" Return a random multivariate tropical polynomial from ``self``. - OUTPUT: a :class:`TropicalMPolynomial` + OUTPUT: :class:`TropicalMPolynomial` .. SEEALSO:: diff --git a/src/sage/rings/semirings/tropical_polynomial.py b/src/sage/rings/semirings/tropical_polynomial.py index cefe97c533a..780a44c4758 100644 --- a/src/sage/rings/semirings/tropical_polynomial.py +++ b/src/sage/rings/semirings/tropical_polynomial.py @@ -165,7 +165,7 @@ def roots(self): Return the list of all tropical roots of ``self``, counted with multiplicity. - OUTPUT: a list of tropical numbers + OUTPUT: A list of tropical numbers ALGORITHM: @@ -291,7 +291,7 @@ def factor(self): `x + x_0` is `x_0` and not `-x_0`. However, not every tropical polynomial can be factored. - OUTPUT: a :class:'Factorization' + OUTPUT: :class:'Factorization' EXAMPLES:: @@ -798,7 +798,7 @@ def random_element(self, degree=(-1, 2), monic=False, *args, **kwds): r""" Return a random tropical polynomial of given degrees (bounds). - OUTPUT: a :class:`TropicalPolynomial` + OUTPUT: :class:`TropicalPolynomial` .. SEEALSO:: @@ -873,7 +873,7 @@ def interpolation(self, points): - ``points`` -- a list of tuples ``(x, y)`` - OUTPUT: a :class:`TropicalPolynomial` + OUTPUT: :class:`TropicalPolynomial` EXAMPLES:: diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index c2f92405fd7..6a9f4aa2314 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -539,13 +539,15 @@ def dual_subdivision(self): """ Return the dual subdivision of ``self``. - Dual subdivision refers to a specific decomposition of the Newton - polygon associated with a tropical polynomial. The term "dual" - is used in the sense that the combinatorial structure of the - tropical variety is reflected in the dual subdivision of the - Newton polygon. Vertices of the dual subdivision correspond to - the intersection of multiple components. Edges of the dual - subdivision correspond to the individual components. + Dual subdivision refers to a specific decomposition of the + Newton polygon of a tropical polynomial. This Newton polygon + is the convex hull of all the points corresponding to the + exponents of the terms of the tropical polynomial. The term + "dual" is used in the sense that the combinatorial structure + of the tropical variety is reflected in the dual subdivision. + Vertices of the dual subdivision correspond to the intersection + of multiple components. Edges of the dual subdivision correspond + to the individual components. OUTPUT: :class:`sage.geometry.polyhedral_complex.PolyhedralComplex` @@ -622,14 +624,19 @@ def weight_vectors(self): Return the weight vectors for each unique intesection of components of ``self``. + Weight vectors are a list of vectors associated with each + unique intersection of the components of tropical variety. + Each vector is a normal vector to a component with respect + to the unique intersection lying within that component. + Assume ``self`` is a `n`-dimensional tropical variety. - Suppose `L` is an intersection adjacent to the components + Suppose `L` is an intersection lying within the components `S_1, ldots, S_k` with respective weights `w_1, ldots, w_k`. This `L` is a linear structure in `\RR^{n-1}` and has `n-1` direction vectors `d_1,d_2,\dots, d_{n-1}`. Each component `S_1, ldots, S_k` has a normal vector `n_1, \ldots, n_k`. - Make sure that the normal vector is scale to an integer vector - such that the greatest common divisor of its elements is 1. + Then, we scale each normal vector to an integer vector such + that the greatest common divisor of its elements is 1. The weight vector of a component `S_i` with respect to `L` can be found by calculating the cross product between direction @@ -1331,14 +1338,17 @@ def weight_vectors(self): r""" Return the weight vectors for all vertices of ``self``. + Weight vectors are a list of vectors associated with each vertex + of the curve. Each vector corresponds to an edge emanating from + that vertex and points in the direction of the edge. + Suppose `v` is a vertex adjacent to the edges `e_1, ldots, e_k` with respective weights `w_1, ldots, w_k`. Every edge `e_i` is - contained in a line (component) defined by an equation with - integer coefficients. Because of this there exists a unique - integer vector `v_i=(\alpha, \beta)` in the direction of `e_i` - such that `\gcd(\alpha, \beta)=1`. Then each vertex `v` yield - the vectors `w_1v_1,ldots,w_kv_k`. These vectors will satisfy - the following balancing condition: + contained in a line (component) defined by an equation. Therefore, + there exists a unique integer vector `v_i=(\alpha, \beta)` in + the direction of `e_i` such that `\gcd(\alpha, \beta)=1`. Then, + each vertex `v` yield the vectors `w_1v_1,ldots,w_kv_k`. + These vectors will satisfy the following balancing condition: `\sum_{i=1}^k w_i v_i = 0`. OUTPUT: @@ -1574,7 +1584,7 @@ def _parameter_intervals(self): intervals.append(interval) return intervals - def plot(self, dominant_term=False): + def plot(self): """ Return the plot of ``self``. From 4306750c0b6ce223898c4a491e52cbaff83da61c Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Mon, 2 Sep 2024 21:34:29 +0700 Subject: [PATCH 17/24] Add more examples to ``TropicalCurve`` --- .../rings/semirings/tropical_polynomial.py | 2 +- src/sage/rings/semirings/tropical_variety.py | 60 ++++++++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/semirings/tropical_polynomial.py b/src/sage/rings/semirings/tropical_polynomial.py index 780a44c4758..18c744ea8eb 100644 --- a/src/sage/rings/semirings/tropical_polynomial.py +++ b/src/sage/rings/semirings/tropical_polynomial.py @@ -53,7 +53,7 @@ class TropicalPolynomial(Polynomial_generic_sparse): EXAMPLES: - First, we construct a tropical polynomial semiring by defining a base + We construct a tropical polynomial semiring by defining a base tropical semiring and then inputting it to :class:`PolynomialRing`:: sage: T = TropicalSemiring(QQ, use_min=False) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 6a9f4aa2314..59b6279cd5a 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -1192,15 +1192,69 @@ class TropicalCurve(TropicalVariety): condition ensures that the sum of the outgoing slopes at each vertex is zero, reflecting the equilibrium. - EXAMPLES:: + EXAMPLES: + + We define some tropical curves:: sage: T = TropicalSemiring(QQ, use_min=False) sage: R. = PolynomialRing(T) sage: p1 = x + y + R(0) - sage: tv = p1.tropical_variety(); tv + sage: tv1 = p1.tropical_variety(); tv1 Tropical curve of 0*x + 0*y + 0 - sage: tv.components() + sage: tv1.components() [[(t1, t1), [t1 >= 0], 1], [(0, t1), [t1 <= 0], 1], [(t1, 0), [t1 <= 0], 1]] + sage: tv1.plot() + Graphics object consisting of 3 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = x + y + R(0) + sphinx_plot(p1.tropical_variety().plot()) + + :: + + sage: p2 = R(-2)*x^2 + R(-1)*x + R(1/2)*y + R(1/6) + sage: tv2 = p2.tropical_variety() + sage: tv2.components() + [[(1/2*t1 + 5/4, t1), [(-1/3) <= t1], 1], + [(13/12, t1), [t1 <= (-1/3)], 2], + [(t1, -1/3), [t1 <= (13/12)], 1]] + sage: tv2.plot() + Graphics object consisting of 4 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p2 = R(-2)*x**2 + R(-1)*x + R(1/2)*y + R(1/6) + sphinx_plot(p2.tropical_variety().plot()) + + When two tropical polynomials are multiplied, the tropical curve of + the resulting polynomial is the union of the tropical curves of the + original polynomials:: + + sage: p3 = p1 * p2; p3 + (-2)*x^3 + (-2)*x^2*y + (-1)*x^2 + 1/2*x*y + 1/2*y^2 + 1/6*x + 1/2*y + 1/6 + sage: tv3 = p3.tropical_variety() + sage: tv3.plot() + Graphics object consisting of 11 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = x + y + R(0) + p2 = R(-2)*x**2 + R(-1)*x + R(1/2)*y + R(1/6) + p3 = p1 * p2 + sphinx_plot(p3.tropical_variety().plot()) """ def _axes(self): """ From 3d0525752e9b5b15a8832de80e98be2ffd9ece4c Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Sun, 8 Sep 2024 12:59:40 +0700 Subject: [PATCH 18/24] Move dual subdivision to ``TropicalMPolynomial`` --- .../rings/semirings/tropical_mpolynomial.py | 80 ++++++++++++++++++ src/sage/rings/semirings/tropical_variety.py | 84 ------------------- 2 files changed, 80 insertions(+), 84 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 3d06fa1a7d8..8b391911b6a 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -387,6 +387,86 @@ def tropical_variety(self): if self.parent().ngens() == 3: return TropicalSurface(self) return TropicalVariety(self) + + def dual_subdivision(self): + """ + Return the dual subdivision of ``self``. + + Dual subdivision refers to a specific decomposition of the + Newton polygon of a tropical polynomial. This Newton polygon + is the convex hull of all the points corresponding to the + exponents of the terms of the tropical polynomial. The term + "dual" is used in the sense that the combinatorial structure + of the tropical variety is reflected in the dual subdivision. + Vertices of the dual subdivision correspond to the intersection + of multiple components. Edges of the dual subdivision correspond + to the individual components. + + OUTPUT: :class:`sage.geometry.polyhedral_complex.PolyhedralComplex` + + EXAMPLES: + + Dual subdivision of a tropical curve:: + + sage: T = TropicalSemiring(QQ, use_min=False) + sage: R. = PolynomialRing(T) + sage: p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x^2 + y^2 + sage: p1.dual_subdivision() + Polyhedral complex with 4 maximal cells + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x**2 + y**2 + tv = p1.tropical_variety() + pc = tv.dual_subdivision() + sphinx_plot(pc.plot()) + + Dual subdivision of a tropical surface:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x + y + z + x^2 + R(1) + sage: p1.dual_subdivision() + Polyhedral complex with 5 maximal cells + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y,z')) + x, y, z = R.gen(), R.gen(1), R.gen(2) + p1 = x + y + z + x**2 + R(1) + tv = p1.tropical_variety() + pc = tv.dual_subdivision() + sphinx_plot(pc.plot()) + + Dual subdivision of a tropical hypersurface:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = a^2 + b^2 + c^2 + d^2 + a*b*c*d + sage: p1.dual_subdivision() + Polyhedral complex with 6 maximal cells + """ + from sage.graphs.graph import Graph + from sage.geometry.polyhedron.constructor import Polyhedron + from sage.geometry.polyhedral_complex import PolyhedralComplex + + TV = self.tropical_variety() + G = Graph() + edges = [e for e in TV._keys] + G.add_edges(edges) + + polyhedron_lst = [] + for cycle in G.cycle_basis(): + polyhedron = Polyhedron(vertices=cycle) + polyhedron_lst.append(polyhedron) + pc = PolyhedralComplex(polyhedron_lst) + return pc def _repr_(self): r""" diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 59b6279cd5a..7c3bf06dbf8 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -535,90 +535,6 @@ def update_result(result): update_result(result) return result - def dual_subdivision(self): - """ - Return the dual subdivision of ``self``. - - Dual subdivision refers to a specific decomposition of the - Newton polygon of a tropical polynomial. This Newton polygon - is the convex hull of all the points corresponding to the - exponents of the terms of the tropical polynomial. The term - "dual" is used in the sense that the combinatorial structure - of the tropical variety is reflected in the dual subdivision. - Vertices of the dual subdivision correspond to the intersection - of multiple components. Edges of the dual subdivision correspond - to the individual components. - - OUTPUT: :class:`sage.geometry.polyhedral_complex.PolyhedralComplex` - - EXAMPLES: - - Dual subdivision of a tropical curve:: - - sage: T = TropicalSemiring(QQ, use_min=False) - sage: R. = PolynomialRing(T) - sage: p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x^2 + y^2 - sage: tv = p1.tropical_variety() - sage: pc = tv.dual_subdivision() - sage: pc.plot() - Graphics object consisting of 20 graphics primitives - - .. PLOT:: - :width: 300 px - - T = TropicalSemiring(QQ, use_min=False) - R = PolynomialRing(T, ('x,y')) - x, y = R.gen(), R.gen(1) - p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x**2 + y**2 - tv = p1.tropical_variety() - pc = tv.dual_subdivision() - sphinx_plot(pc.plot()) - - Dual subdivision of a tropical surface:: - - sage: T = TropicalSemiring(QQ) - sage: R. = PolynomialRing(T) - sage: p1 = x + y + z + x^2 + R(1) - sage: tv = p1.tropical_variety() - sage: pc = tv.dual_subdivision() - sage: pc.plot() - Graphics3d Object - - .. PLOT:: - :width: 300 px - - T = TropicalSemiring(QQ, use_min=False) - R = PolynomialRing(T, ('x,y,z')) - x, y, z = R.gen(), R.gen(1), R.gen(2) - p1 = x + y + z + x**2 + R(1) - tv = p1.tropical_variety() - pc = tv.dual_subdivision() - sphinx_plot(pc.plot()) - - Dual subdivision of a tropical hypersurface:: - - sage: T = TropicalSemiring(QQ) - sage: R. = PolynomialRing(T) - sage: p1 = a^2 + b^2 + c^2 + d^2 + a*b*c*d - sage: tv = p1.tropical_variety() - sage: pc = tv.dual_subdivision(); pc - Polyhedral complex with 6 maximal cells - """ - from sage.graphs.graph import Graph - from sage.geometry.polyhedron.constructor import Polyhedron - from sage.geometry.polyhedral_complex import PolyhedralComplex - - G = Graph() - edges = [e for e in self._keys] - G.add_edges(edges) - - polyhedron_lst = [] - for cycle in G.cycle_basis(): - polyhedron = Polyhedron(vertices=cycle) - polyhedron_lst.append(polyhedron) - pc = PolyhedralComplex(polyhedron_lst) - return pc - def weight_vectors(self): r""" Return the weight vectors for each unique intesection of From d2d211b4169899bcb371d26504d647f2e343c115 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Sun, 8 Sep 2024 13:20:07 +0700 Subject: [PATCH 19/24] Add ``polytope`` to ``TropicalMPolynomial`` --- .../rings/semirings/tropical_mpolynomial.py | 67 ++++++++++++++++--- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 8b391911b6a..a506317ccc3 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -387,20 +387,69 @@ def tropical_variety(self): if self.parent().ngens() == 3: return TropicalSurface(self) return TropicalVariety(self) - + + def polytope(self): + """ + Return the Newton polytope of ``self``. + + The Newton polytope is the convex hull of all the points + corresponding to the exponents of the monomials of tropical + polynomial. + + OUTPUT: :class:`sage.geometry.polyhedron.constructor.Polyhedron` + + EXAMPLES: + + Newton polytope for two variable tropical polynomial:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x + y + sage: p1.polytope() + A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p1 = x + y + sphinx_plot(p1.polytope().plot()) + + Newton polytope in three dimension:: + + sage: T = TropicalSemiring(QQ) + sage: R. = PolynomialRing(T) + sage: p1 = x^2 + x*y*z + x + y + z + R(0) + sage: p1.polytope() + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 5 vertices + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ) + R = PolynomialRing(T, ('x,y,z')) + x, y, z = R.gen(), R.gen(1), R.gen(2) + p1 = x**2 + x*y*z + x + y + z + R(0) + sphinx_plot(p1.polytope().plot()) + """ + from sage.geometry.polyhedron.constructor import Polyhedron + + exponents = self.exponents() + return Polyhedron(exponents) + def dual_subdivision(self): """ Return the dual subdivision of ``self``. Dual subdivision refers to a specific decomposition of the - Newton polygon of a tropical polynomial. This Newton polygon - is the convex hull of all the points corresponding to the - exponents of the terms of the tropical polynomial. The term - "dual" is used in the sense that the combinatorial structure - of the tropical variety is reflected in the dual subdivision. - Vertices of the dual subdivision correspond to the intersection - of multiple components. Edges of the dual subdivision correspond - to the individual components. + Newton polytope of a tropical polynomial. The term "dual" is + used in the sense that the combinatorial structure of the + tropical variety is reflected in the dual subdivision. + Specifically, vertices of the dual subdivision correspond to + the intersection of multiple components. Edges of the dual + subdivision correspond to the individual components. OUTPUT: :class:`sage.geometry.polyhedral_complex.PolyhedralComplex` From 53f495e6309267705d0cc37d610b7e9554476580 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Sun, 8 Sep 2024 15:05:51 +0700 Subject: [PATCH 20/24] Fix ``PLOT`` section in dual subdivision --- src/sage/rings/semirings/tropical_mpolynomial.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index a506317ccc3..887b21cddff 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -470,9 +470,7 @@ def dual_subdivision(self): R = PolynomialRing(T, ('x,y')) x, y = R.gen(), R.gen(1) p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x**2 + y**2 - tv = p1.tropical_variety() - pc = tv.dual_subdivision() - sphinx_plot(pc.plot()) + sphinx_plot(p1.dual_subdivision().plot()) Dual subdivision of a tropical surface:: @@ -489,9 +487,7 @@ def dual_subdivision(self): R = PolynomialRing(T, ('x,y,z')) x, y, z = R.gen(), R.gen(1), R.gen(2) p1 = x + y + z + x**2 + R(1) - tv = p1.tropical_variety() - pc = tv.dual_subdivision() - sphinx_plot(pc.plot()) + sphinx_plot(p1.dual_subdivision().plot()) Dual subdivision of a tropical hypersurface:: From 9f663272c0b440ff1de31ca08e6c4375dc7c32aa Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Tue, 10 Sep 2024 03:46:02 +0700 Subject: [PATCH 21/24] Fix dual subdivision --- .../rings/semirings/tropical_mpolynomial.py | 31 +++++++++++++++++-- src/sage/rings/semirings/tropical_variety.py | 20 ++++++------ 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 887b21cddff..4e103112bdc 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -478,7 +478,7 @@ def dual_subdivision(self): sage: R. = PolynomialRing(T) sage: p1 = x + y + z + x^2 + R(1) sage: p1.dual_subdivision() - Polyhedral complex with 5 maximal cells + Polyhedral complex with 7 maximal cells .. PLOT:: :width: 300 px @@ -495,7 +495,7 @@ def dual_subdivision(self): sage: R. = PolynomialRing(T) sage: p1 = a^2 + b^2 + c^2 + d^2 + a*b*c*d sage: p1.dual_subdivision() - Polyhedral complex with 6 maximal cells + Polyhedral complex with 10 maximal cells """ from sage.graphs.graph import Graph from sage.geometry.polyhedron.constructor import Polyhedron @@ -506,8 +506,33 @@ def dual_subdivision(self): edges = [e for e in TV._keys] G.add_edges(edges) + # Recursive function to find all cycles that partition the graph + # to its dual subdivision + def search(cycle, next, adj): + min_cycle = False + for n in next: + new_cycle = [i for i in cycle] + new_cycle.append(n) + if vertex in G.neighbors(n): + all_cycles.append(new_cycle) + min_cycle = True + if not min_cycle: + for n in next: + new_cycle = [i for i in cycle] + new_cycle.append(n) + new_next = [v for v in G.neighbors(n) if v != adj] + search(new_cycle, new_next, n) + + all_cycles = [] + for vertex in G.vertices(): + for adj in G.neighbors(vertex): + cycle = [vertex] + cycle.append(adj) + next = [v for v in G.neighbors(adj) if v != vertex] + search(cycle, next, adj) + polyhedron_lst = [] - for cycle in G.cycle_basis(): + for cycle in all_cycles: polyhedron = Polyhedron(vertices=cycle) polyhedron_lst.append(polyhedron) pc = PolyhedralComplex(polyhedron_lst) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 7c3bf06dbf8..5d8743f6b71 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -575,9 +575,9 @@ def weight_vectors(self): sage: tv = p.tropical_variety() sage: tv.weight_vectors() ({0: ((1/2*u2, u2 + 1, u2), {u2 <= 1}), - 1: ((1/2, 2, u2), {1 <= u2}), - 2: ((1/2, u2, 1), {2 <= u2}), - 3: ((u1, 2, 1), {(1/2) <= u1})}, + 1: ((1/2, 2, u2), {1 <= u2}), + 2: ((1/2, u2, 1), {2 <= u2}), + 3: ((u1, 2, 1), {(1/2) <= u1})}, {0: [(1, 2, -5/2), (1, -5/2, 2), (-2, 1/2, 1/2)], 1: [(-1, -2, 0), (0, 2, 0), (1, 0, 0)], 2: [(1, 0, 2), (0, 0, -2), (-1, 0, 0)], @@ -591,13 +591,13 @@ def weight_vectors(self): sage: tv = p1.tropical_variety() sage: tv.weight_vectors() ({0: ((u1, u3 - 7/3, u3 - 10/3, u3), {u1 <= u3 - 22/3}), - 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), - 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), - 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, - {0: [(0, 1, 1, -2), (0, 1, -2, 1), (0, -2, 1, 1)], - 1: [(-2, 1, 1, 0), (3, -3, 0, 0), (-1, 2, -1, 0)], - 2: [(-1, 5, 2, -6), (2, 1, -4, 1), (-1, -6, 2, 5)], - 3: [(-1, 0, -1, 2), (-2, 0, 1, 1), (3, 0, 0, -3)]}) + 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), + 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), + 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, + {0: [(0, -1, -1, 2), (0, -1, 2, -1), (0, 2, -1, -1)], + 1: [(2, -1, -1, 0), (-3, 3, 0, 0), (1, -2, 1, 0)], + 2: [(1, -5, -2, 6), (-2, -1, 4, -1), (1, 6, -2, -5)], + 3: [(1, 0, 1, -2), (2, 0, -1, -1), (-3, 0, 0, 3)]}) """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve From 2fe9f386d97605e268a713fc174915067468525f Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Tue, 10 Sep 2024 11:51:59 +0700 Subject: [PATCH 22/24] Modify dual subdivisio to utilize weight vectors --- .../rings/semirings/tropical_mpolynomial.py | 56 ++++++++----------- src/sage/rings/semirings/tropical_variety.py | 25 +++++---- 2 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 4e103112bdc..2e62f11b05a 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -493,46 +493,36 @@ def dual_subdivision(self): sage: T = TropicalSemiring(QQ) sage: R. = PolynomialRing(T) - sage: p1 = a^2 + b^2 + c^2 + d^2 + a*b*c*d + sage: p1 = R(2)*a*b + R(3)*a*c + R(-1)*c^2 + R(-1/3)*a*d sage: p1.dual_subdivision() - Polyhedral complex with 10 maximal cells + Polyhedral complex with 4 maximal cells """ - from sage.graphs.graph import Graph from sage.geometry.polyhedron.constructor import Polyhedron from sage.geometry.polyhedral_complex import PolyhedralComplex TV = self.tropical_variety() - G = Graph() - edges = [e for e in TV._keys] - G.add_edges(edges) - - # Recursive function to find all cycles that partition the graph - # to its dual subdivision - def search(cycle, next, adj): - min_cycle = False - for n in next: - new_cycle = [i for i in cycle] - new_cycle.append(n) - if vertex in G.neighbors(n): - all_cycles.append(new_cycle) - min_cycle = True - if not min_cycle: - for n in next: - new_cycle = [i for i in cycle] - new_cycle.append(n) - new_next = [v for v in G.neighbors(n) if v != adj] - search(new_cycle, new_next, n) - - all_cycles = [] - for vertex in G.vertices(): - for adj in G.neighbors(vertex): - cycle = [vertex] - cycle.append(adj) - next = [v for v in G.neighbors(adj) if v != vertex] - search(cycle, next, adj) - + cycles = [] + + if TV.dimension() == 2: + for indices in TV._vertices_components().values(): + cycle = [] + for index in indices: + vertices = TV._keys[index[0]] + for v in vertices: + cycle.append(v) + cycles.append(cycle) + else: + line_comps = TV.weight_vectors()[1] + for indices in line_comps.values(): + cycle = [] + for index in indices: + vertices = TV._keys[index] + for v in vertices: + cycle.append(v) + cycles.append(cycle) + polyhedron_lst = [] - for cycle in all_cycles: + for cycle in cycles: polyhedron = Polyhedron(vertices=cycle) polyhedron_lst.append(polyhedron) pc = PolyhedralComplex(polyhedron_lst) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 5d8743f6b71..96817231b95 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -561,9 +561,10 @@ def weight_vectors(self): OUTPUT: - A tuple of two dictionaries. The first dictionary contains + A tuple of three dictionaries. The first dictionary contains equations representing the intersections. The second dictionary - contains lists of vectors. + contains indices of components that contains the intersection. + The third dictionary contains lists of vectors. EXAMPLES: @@ -578,10 +579,11 @@ def weight_vectors(self): 1: ((1/2, 2, u2), {1 <= u2}), 2: ((1/2, u2, 1), {2 <= u2}), 3: ((u1, 2, 1), {(1/2) <= u1})}, - {0: [(1, 2, -5/2), (1, -5/2, 2), (-2, 1/2, 1/2)], - 1: [(-1, -2, 0), (0, 2, 0), (1, 0, 0)], - 2: [(1, 0, 2), (0, 0, -2), (-1, 0, 0)], - 3: [(0, 1, 1), (0, 0, -1), (0, -1, 0)]}) + {0: [0, 1, 3], 1: [0, 2, 4], 2: [1, 2, 5], 3: [3, 4, 5]}, + {0: [(1, 2, -5/2), (1, -5/2, 2), (-2, 1/2, 1/2)], + 1: [(-1, -2, 0), (0, 2, 0), (1, 0, 0)], + 2: [(1, 0, 2), (0, 0, -2), (-1, 0, 0)], + 3: [(0, 1, 1), (0, 0, -1), (0, -1, 0)]}) Weight vectors of tropical hypersurface:: @@ -594,10 +596,11 @@ def weight_vectors(self): 1: ((u2 - 4, u2 + 1, u2, u3), {u2 <= u3 - 10/3}), 2: ((2*u1 - u3 - 2/3, u3 - 7/3, u1, u3), {u3 - 10/3 <= u1}), 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, - {0: [(0, -1, -1, 2), (0, -1, 2, -1), (0, 2, -1, -1)], - 1: [(2, -1, -1, 0), (-3, 3, 0, 0), (1, -2, 1, 0)], - 2: [(1, -5, -2, 6), (-2, -1, 4, -1), (1, 6, -2, -5)], - 3: [(1, 0, 1, -2), (2, 0, -1, -1), (-3, 0, 0, 3)]}) + {0: [0, 2, 4], 1: [0, 1, 3], 2: [1, 2, 5], 3: [3, 4, 5]}, + {0: [(0, -1, -1, 2), (0, -1, 2, -1), (0, 2, -1, -1)], + 1: [(2, -1, -1, 0), (-3, 3, 0, 0), (1, -2, 1, 0)], + 2: [(1, -5, -2, 6), (-2, -1, 4, -1), (1, 6, -2, -5)], + 3: [(1, 0, 1, -2), (2, 0, -1, -1), (-3, 0, 0, 3)]}) """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve @@ -735,7 +738,7 @@ def weight_vectors(self): WV[k] = test_vectors break - return index_line, WV + return index_line, line_comps, WV class TropicalSurface(TropicalVariety): From 87cd7bc63efbb8fbf2be584f0d07830c49800115 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Wed, 18 Sep 2024 22:03:46 +0700 Subject: [PATCH 23/24] Add more examples for dual subdivision --- .../rings/semirings/tropical_mpolynomial.py | 63 +++++++++++++++++-- src/sage/rings/semirings/tropical_variety.py | 4 +- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 2e62f11b05a..0935508339d 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -148,6 +148,23 @@ class TropicalMPolynomial(MPolynomial_polydict): p1 = R(3)*a*b + a + R(-1)*b sphinx_plot(p1.plot3d()) + Another way to represent tropical curve is through dual subdivision, + which is a subdivision of Newton polytope of tropical polynomial:: + + sage: p1.Newton_polytope() + A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices + sage: p1.dual_subdivision() + Polyhedral complex with 1 maximal cell + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('a,b')) + a, b = R.gen(), R.gen(1) + p1 = R(3)*a*b + a + R(-1)*b + sphinx_plot(p1.dual_subdivision().plot()) + TESTS: There is no subtraction defined for tropical polynomials:: @@ -388,7 +405,7 @@ def tropical_variety(self): return TropicalSurface(self) return TropicalVariety(self) - def polytope(self): + def Newton_polytope(self): """ Return the Newton polytope of ``self``. @@ -400,12 +417,12 @@ def polytope(self): EXAMPLES: - Newton polytope for two variable tropical polynomial:: + Newton polytope for a two-variable tropical polynomial:: sage: T = TropicalSemiring(QQ) sage: R. = PolynomialRing(T) sage: p1 = x + y - sage: p1.polytope() + sage: p1.Newton_polytope() A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices .. PLOT:: @@ -415,14 +432,14 @@ def polytope(self): R = PolynomialRing(T, ('x,y')) x, y = R.gen(), R.gen(1) p1 = x + y - sphinx_plot(p1.polytope().plot()) + sphinx_plot(p1.Newton_polytope().plot()) Newton polytope in three dimension:: sage: T = TropicalSemiring(QQ) sage: R. = PolynomialRing(T) sage: p1 = x^2 + x*y*z + x + y + z + R(0) - sage: p1.polytope() + sage: p1.Newton_polytope() A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 5 vertices .. PLOT:: @@ -432,7 +449,7 @@ def polytope(self): R = PolynomialRing(T, ('x,y,z')) x, y, z = R.gen(), R.gen(1), R.gen(2) p1 = x**2 + x*y*z + x + y + z + R(0) - sphinx_plot(p1.polytope().plot()) + sphinx_plot(p1.Newton_polytope().plot()) """ from sage.geometry.polyhedron.constructor import Polyhedron @@ -472,6 +489,40 @@ def dual_subdivision(self): p1 = R(3) + R(2)*x + R(2)*y + R(3)*x*y + x**2 + y**2 sphinx_plot(p1.dual_subdivision().plot()) + A subdivision of a pentagonal Newton polytope:: + + sage: p2 = R(3) + x^2 + R(-2)*y + R(1/2)*x^2*y + R(2)*x*y^3 + R(-1)*x^3*y^4 + sage: p2.dual_subdivision() + Polyhedral complex with 5 maximal cells + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p2 = R(3) + x**2 + R(-2)*y + R(1/2)*x**2*y + R(2)*x*y**3 + R(-1)*x**3*y**4 + sphinx_plot(p2.dual_subdivision().plot()) + + A subdivision with many faces, not all of which are triangles:: + + sage: p3 = R(8) + R(4)*x + R(2)*y + R(1)*x^2 + x*y + R(1)*y^2 \ + ....: + R(2)*x^3 + x^2*y + x*y^2 + R(4)*y^3 + R(8)*x^4 \ + ....: + R(4)*x^3*y + x^2*y^2 + R(2)*x*y^3 + y^4 + sage: p3.dual_subdivision().plot() + Graphics object consisting of 10 graphics primitives + + .. PLOT:: + :width: 300 px + + T = TropicalSemiring(QQ, use_min=False) + R = PolynomialRing(T, ('x,y')) + x, y = R.gen(), R.gen(1) + p3 = R(8) + R(4)*x + R(2)*y + R(1)*x**2 + x*y + R(1)*y**2 \ + + R(2)*x**3 + x**2*y + x*y**2 + R(4)*y**3 + R(8)*x**4 \ + + R(4)*x**3*y + x**2*y**2 + R(2)*x*y**3 + y**4 + sphinx_plot(p3.dual_subdivision().plot()) + Dual subdivision of a tropical surface:: sage: T = TropicalSemiring(QQ) diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 96817231b95..a3ed0676b47 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -598,9 +598,9 @@ def weight_vectors(self): 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, {0: [0, 2, 4], 1: [0, 1, 3], 2: [1, 2, 5], 3: [3, 4, 5]}, {0: [(0, -1, -1, 2), (0, -1, 2, -1), (0, 2, -1, -1)], - 1: [(2, -1, -1, 0), (-3, 3, 0, 0), (1, -2, 1, 0)], + 1: [(-2, 1, 1, 0), (3, -3, 0, 0), (-1, 2, -1, 0)], 2: [(1, -5, -2, 6), (-2, -1, 4, -1), (1, 6, -2, -5)], - 3: [(1, 0, 1, -2), (2, 0, -1, -1), (-3, 0, 0, 3)]}) + 3: [(-1, 0, -1, 2), (-2, 0, 1, 1), (3, 0, 0, -3)]}) """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve From 204a525ee1b4c01130ce15cf47c7bc72d02b84b7 Mon Sep 17 00:00:00 2001 From: Verrel Rievaldo Wijaya Date: Sun, 22 Sep 2024 11:11:57 +0700 Subject: [PATCH 24/24] Fix lint error --- src/sage/rings/semirings/tropical_mpolynomial.py | 6 +++--- src/sage/rings/semirings/tropical_variety.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 812fc1bc516..48323fadaf2 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -164,7 +164,7 @@ class TropicalMPolynomial(MPolynomial_polydict): R = PolynomialRing(T, ('a,b')) a, b = R.gen(), R.gen(1) p1 = R(3)*a*b + a + R(-1)*b - sphinx_plot(p1.dual_subdivision().plot()) + sphinx_plot(p1.dual_subdivision().plot()) TESTS: @@ -494,7 +494,7 @@ def dual_subdivision(self): sage: p2 = R(3) + x^2 + R(-2)*y + R(1/2)*x^2*y + R(2)*x*y^3 + R(-1)*x^3*y^4 sage: p2.dual_subdivision() Polyhedral complex with 5 maximal cells - + .. PLOT:: :width: 300 px @@ -503,7 +503,7 @@ def dual_subdivision(self): x, y = R.gen(), R.gen(1) p2 = R(3) + x**2 + R(-2)*y + R(1/2)*x**2*y + R(2)*x*y**3 + R(-1)*x**3*y**4 sphinx_plot(p2.dual_subdivision().plot()) - + A subdivision with many faces, not all of which are triangles:: sage: p3 = R(8) + R(4)*x + R(2)*y + R(1)*x^2 + x*y + R(1)*y^2 \ diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index 4c4dacbe1d9..6d0342a3d74 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -593,9 +593,9 @@ def weight_vectors(self): 3: ((u3 - 22/3, u2, u3 - 10/3, u3), {u3 - 7/3 <= u2})}, {0: [0, 2, 4], 1: [0, 1, 3], 2: [1, 2, 5], 3: [3, 4, 5]}, {0: [(0, -1, -1, 2), (0, -1, 2, -1), (0, 2, -1, -1)], - 1: [(-2, 1, 1, 0), (3, -3, 0, 0), (-1, 2, -1, 0)], + 1: [(2, -1, -1, 0), (-3, 3, 0, 0), (1, -2, 1, 0)], 2: [(1, -5, -2, 6), (-2, -1, 4, -1), (1, 6, -2, -5)], - 3: [(-1, 0, -1, 2), (-2, 0, 1, 1), (3, 0, 0, -3)]}) + 3: [(1, 0, 1, -2), (2, 0, -1, -1), (-3, 0, 0, 3)]}) """ from sage.symbolic.ring import SR from sage.symbolic.relation import solve