passagemath-polyhedra 10.6.37__cp314-cp314-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_polyhedra/__init__.py +3 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.37.dist-info/RECORD +209 -0
- passagemath_polyhedra-10.6.37.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.37.dist-info/top_level.txt +3 -0
- passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
- passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3905 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
|
@@ -0,0 +1,1810 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
# sage.doctest: needs sage.combinat
|
|
3
|
+
r"""
|
|
4
|
+
Generating Function of Polyhedron's Integral Points
|
|
5
|
+
|
|
6
|
+
This module provides :func:`generating_function_of_integral_points` which
|
|
7
|
+
computes the generating function of the integral points of a polyhedron.
|
|
8
|
+
|
|
9
|
+
The main function is accessible via
|
|
10
|
+
:meth:`sage.geometry.polyhedron.base.Polyhedron_base.generating_function_of_integral_points`
|
|
11
|
+
as well.
|
|
12
|
+
|
|
13
|
+
Various
|
|
14
|
+
=======
|
|
15
|
+
|
|
16
|
+
AUTHORS:
|
|
17
|
+
|
|
18
|
+
- Daniel Krenn (2016, 2021)
|
|
19
|
+
|
|
20
|
+
ACKNOWLEDGEMENT:
|
|
21
|
+
|
|
22
|
+
- Daniel Krenn is supported by the Austrian Science Fund (FWF): P 24644-N26
|
|
23
|
+
and by the Austrian Science Fund (FWF): P 28466-N35.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
Functions
|
|
27
|
+
=========
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# *****************************************************************************
|
|
31
|
+
# Copyright (C) 2016, 2021 Daniel Krenn <dev@danielkrenn.at>
|
|
32
|
+
#
|
|
33
|
+
# This program is free software: you can redistribute it and/or modify
|
|
34
|
+
# it under the terms of the GNU General Public License as published by
|
|
35
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
36
|
+
# (at your option) any later version.
|
|
37
|
+
# http://www.gnu.org/licenses/
|
|
38
|
+
# *****************************************************************************
|
|
39
|
+
|
|
40
|
+
Hrepresentation_str_options = {'prefix': 'b', 'style': 'positive'}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def generating_function_of_integral_points(polyhedron, split=False,
|
|
44
|
+
result_as_tuple=None,
|
|
45
|
+
name=None, names=None,
|
|
46
|
+
algorithm="omega",
|
|
47
|
+
**kwds):
|
|
48
|
+
r"""
|
|
49
|
+
Return the multivariate generating function of the
|
|
50
|
+
integral points of the ``polyhedron``.
|
|
51
|
+
|
|
52
|
+
To be precise, this returns
|
|
53
|
+
|
|
54
|
+
.. MATH::
|
|
55
|
+
|
|
56
|
+
\sum_{(r_0,\dots,r_{d-1}) \in \mathit{polyhedron}\cap \ZZ^d}
|
|
57
|
+
y_0^{r_0} \dots y_{d-1}^{r_{d-1}}.
|
|
58
|
+
|
|
59
|
+
INPUT:
|
|
60
|
+
|
|
61
|
+
- ``polyhedron`` -- an instance of
|
|
62
|
+
:class:`~sage.geometry.polyhedron.base.Polyhedron_base`
|
|
63
|
+
(see also :mod:`sage.geometry.polyhedron.constructor`)
|
|
64
|
+
|
|
65
|
+
- ``split`` -- (default: ``False``) a boolean or list
|
|
66
|
+
|
|
67
|
+
- ``split=False`` computes the generating function directly,
|
|
68
|
+
without any splitting.
|
|
69
|
+
|
|
70
|
+
- When ``split`` is a list of disjoint polyhedra, then
|
|
71
|
+
for each of these polyhedra, ``polyhedron`` is intersected with it,
|
|
72
|
+
its generating function computed and all these generating functions
|
|
73
|
+
are summed up.
|
|
74
|
+
|
|
75
|
+
- ``split=True`` splits into `d!` disjoint polyhedra.
|
|
76
|
+
|
|
77
|
+
- ``result_as_tuple`` -- (default: ``None``) a boolean or ``None``
|
|
78
|
+
|
|
79
|
+
This specifies whether the output is a (partial) factorization
|
|
80
|
+
(``result_as_tuple=False``) or a sum of such (partial)
|
|
81
|
+
factorizations (``result_as_tuple=True``). By default
|
|
82
|
+
(``result_as_tuple=None``), this is automatically determined.
|
|
83
|
+
If the output is a sum, it is represented as a tuple whose
|
|
84
|
+
entries are the summands.
|
|
85
|
+
|
|
86
|
+
- ``indices`` -- (default: ``None``) a list or tuple
|
|
87
|
+
|
|
88
|
+
If this is ``None``, this is automatically determined.
|
|
89
|
+
|
|
90
|
+
- ``name`` -- (default: ``'y'``) a string
|
|
91
|
+
|
|
92
|
+
The variable names of the Laurent polynomial ring of the output
|
|
93
|
+
are this string followed by an integer.
|
|
94
|
+
|
|
95
|
+
- ``names`` -- list or tuple of names (strings), or a comma separated string
|
|
96
|
+
|
|
97
|
+
``name`` is extracted from ``names``, therefore ``names`` has to contain
|
|
98
|
+
exactly one variable name, and ``name`` and``names`` cannot be specified
|
|
99
|
+
both at the same time.
|
|
100
|
+
|
|
101
|
+
- ``algorithm`` -- (default:``"omega"``) The algorithm which is used
|
|
102
|
+
to compute the multivariate generating function. Options are:
|
|
103
|
+
|
|
104
|
+
* ``"omega"`` -- Run the algorithm via
|
|
105
|
+
:func:`MacMahon's Omega operator <sage.rings.polynomial.omega.MacMahonOmega>`.
|
|
106
|
+
|
|
107
|
+
* ``"latte"`` -- Run the corresponding algorithm of ``LattE``.
|
|
108
|
+
This algorithm requires LattE (Lattice point Enumeration) Integrale.
|
|
109
|
+
To install LattE Integrale, type :code:`sage -i latte_int` in the terminal.
|
|
110
|
+
|
|
111
|
+
* ``"naive"`` -- Only works for bounded polyhedra. Collect the monomials
|
|
112
|
+
corresponding to each integral point.
|
|
113
|
+
|
|
114
|
+
- ``Factorization_sort`` (default: ``False``) and
|
|
115
|
+
``Factorization_simplify`` (default: ``True``) -- booleans
|
|
116
|
+
|
|
117
|
+
These are passed on to
|
|
118
|
+
:class:`sage.structure.factorization.Factorization` when creating
|
|
119
|
+
the result.
|
|
120
|
+
|
|
121
|
+
- ``sort_factors`` -- (default: ``False``) a boolean
|
|
122
|
+
|
|
123
|
+
If set, then
|
|
124
|
+
the factors of the output are sorted such that the numerator is
|
|
125
|
+
first and only then all factors of the denominator. It is ensured
|
|
126
|
+
that the sorting is always the same; use this for doctesting.
|
|
127
|
+
|
|
128
|
+
OUTPUT:
|
|
129
|
+
|
|
130
|
+
The generating function as a (partial)
|
|
131
|
+
:class:`~sage.structure.factorization.Factorization`
|
|
132
|
+
of the result whose factors are Laurent polynomials
|
|
133
|
+
|
|
134
|
+
The result might be a tuple of such factorizations
|
|
135
|
+
(depending on the parameter ``result_as_tuple``) as well.
|
|
136
|
+
|
|
137
|
+
.. NOTE::
|
|
138
|
+
|
|
139
|
+
At the moment, ``algorithm="omega"`` only handles polyhedra with
|
|
140
|
+
nonnegative coordinates (i.e., polyhedra in the nonnegative orthant).
|
|
141
|
+
|
|
142
|
+
``algorithm="latte"`` option can handle general polyhedra,
|
|
143
|
+
but has no loggings for the decompositions and
|
|
144
|
+
only supports a tuple output unless the tuple has only one element.
|
|
145
|
+
|
|
146
|
+
EXAMPLES::
|
|
147
|
+
|
|
148
|
+
sage: from sage.geometry.polyhedron.generating_function import generating_function_of_integral_points
|
|
149
|
+
|
|
150
|
+
::
|
|
151
|
+
|
|
152
|
+
sage: P2 = (Polyhedron(ieqs=[(0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, -1)]),
|
|
153
|
+
....: Polyhedron(ieqs=[(0, -1, 0, 1), (0, 1, 0, 0), (0, 0, 1, 0)]))
|
|
154
|
+
sage: generating_function_of_integral_points(P2[0], sort_factors=True)
|
|
155
|
+
1 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1
|
|
156
|
+
sage: generating_function_of_integral_points(P2[1], sort_factors=True)
|
|
157
|
+
1 * (-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1
|
|
158
|
+
sage: (P2[0] & P2[1]).Hrepresentation()
|
|
159
|
+
(An equation (1, 0, -1) x + 0 == 0,
|
|
160
|
+
An inequality (1, 0, 0) x + 0 >= 0,
|
|
161
|
+
An inequality (0, 1, 0) x + 0 >= 0)
|
|
162
|
+
sage: generating_function_of_integral_points(P2[0] & P2[1], sort_factors=True)
|
|
163
|
+
1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1
|
|
164
|
+
|
|
165
|
+
sage: # optional - latte_int
|
|
166
|
+
sage: generating_function_of_integral_points(P2[0], sort_factors=True,
|
|
167
|
+
....: algorithm="latte")
|
|
168
|
+
1 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1
|
|
169
|
+
sage: generating_function_of_integral_points(P2[1], sort_factors=True,
|
|
170
|
+
....: algorithm="latte")
|
|
171
|
+
1 * (-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1
|
|
172
|
+
sage: generating_function_of_integral_points(P2[0] & P2[1], sort_factors=True,
|
|
173
|
+
....: algorithm="latte")
|
|
174
|
+
1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1
|
|
175
|
+
|
|
176
|
+
sage: generating_function_of_integral_points(P2[0], sort_factors=True,
|
|
177
|
+
....: algorithm="naive")
|
|
178
|
+
Traceback (most recent call last):
|
|
179
|
+
...
|
|
180
|
+
ValueError: Polyhedron must be bounded for the naive algorithm
|
|
181
|
+
|
|
182
|
+
::
|
|
183
|
+
|
|
184
|
+
sage: P3 = (
|
|
185
|
+
....: Polyhedron(
|
|
186
|
+
....: ieqs=[(0, 0, 0, 0, 1), (0, 0, 0, 1, 0),
|
|
187
|
+
....: (0, 0, 1, 0, -1), (-1, 1, 0, -1, -1)]),
|
|
188
|
+
....: Polyhedron(
|
|
189
|
+
....: ieqs=[(0, 0, -1, 0, 1), (0, 1, 0, 0, -1),
|
|
190
|
+
....: (0, 0, 0, 1, 0), (0, 0, 1, 0, 0), (-1, 1, -1, -1, 0)]),
|
|
191
|
+
....: Polyhedron(
|
|
192
|
+
....: ieqs=[(1, -1, 0, 1, 1), (1, -1, 1, 1, 0),
|
|
193
|
+
....: (0, 0, 0, 0, 1), (0, 0, 0, 1, 0), (0, 0, 1, 0, 0),
|
|
194
|
+
....: (1, 0, 1, 1, -1), (0, 1, 0, 0, 0), (1, 1, 1, 0, -1)]),
|
|
195
|
+
....: Polyhedron(
|
|
196
|
+
....: ieqs=[(0, 1, 0, -1, 0), (0, -1, 0, 0, 1),
|
|
197
|
+
....: (-1, 0, -1, -1, 1), (0, 0, 1, 0, 0), (0, 0, 0, 1, 0)]),
|
|
198
|
+
....: Polyhedron(
|
|
199
|
+
....: ieqs=[(0, 1, 0, 0, 0), (0, 0, 1, 0, 0),
|
|
200
|
+
....: (-1, -1, -1, 0, 1), (0, -1, 0, 1, 0)]))
|
|
201
|
+
sage: def intersect(I):
|
|
202
|
+
....: I = iter(I)
|
|
203
|
+
....: result = next(I)
|
|
204
|
+
....: for i in I:
|
|
205
|
+
....: result &= i
|
|
206
|
+
....: return result
|
|
207
|
+
sage: for J in subsets(range(len(P3))):
|
|
208
|
+
....: if not J:
|
|
209
|
+
....: continue
|
|
210
|
+
....: P = intersect([P3[j] for j in J])
|
|
211
|
+
....: print('{}: {}'.format(J, P.Hrepresentation()))
|
|
212
|
+
....: print(generating_function_of_integral_points(P, sort_factors=True))
|
|
213
|
+
[0]: (An inequality (0, 0, 0, 1) x + 0 >= 0,
|
|
214
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0,
|
|
215
|
+
An inequality (0, 1, 0, -1) x + 0 >= 0,
|
|
216
|
+
An inequality (1, 0, -1, -1) x - 1 >= 0)
|
|
217
|
+
y0 * (-y0 + 1)^-1 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1
|
|
218
|
+
[1]: (An inequality (0, -1, 0, 1) x + 0 >= 0,
|
|
219
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0,
|
|
220
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
221
|
+
An inequality (1, -1, -1, 0) x - 1 >= 0,
|
|
222
|
+
An inequality (1, 0, 0, -1) x + 0 >= 0)
|
|
223
|
+
(-y0^2*y2*y3 - y0^2*y3 + y0*y3 + y0) *
|
|
224
|
+
(-y0 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y3 + 1)^-1 *
|
|
225
|
+
(-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
226
|
+
[0, 1]: (An equation (0, 1, 0, -1) x + 0 == 0,
|
|
227
|
+
An inequality (1, -1, -1, 0) x - 1 >= 0,
|
|
228
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
229
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0)
|
|
230
|
+
y0 * (-y0 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1
|
|
231
|
+
[2]: (An inequality (-1, 0, 1, 1) x + 1 >= 0,
|
|
232
|
+
An inequality (-1, 1, 1, 0) x + 1 >= 0,
|
|
233
|
+
An inequality (0, 0, 0, 1) x + 0 >= 0,
|
|
234
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0,
|
|
235
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
236
|
+
An inequality (0, 1, 1, -1) x + 1 >= 0,
|
|
237
|
+
An inequality (1, 0, 0, 0) x + 0 >= 0,
|
|
238
|
+
An inequality (1, 1, 0, -1) x + 1 >= 0)
|
|
239
|
+
(y0^2*y1*y2*y3^2 + y0^2*y2^2*y3 + y0*y1^2*y3^2 - y0^2*y2*y3 +
|
|
240
|
+
y0*y1*y2*y3 - y0*y1*y3^2 - 2*y0*y1*y3 - 2*y0*y2*y3 - y0*y2 +
|
|
241
|
+
y0*y3 - y1*y3 + y0 + y3 + 1) *
|
|
242
|
+
(-y1 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y2 + 1)^-1 *
|
|
243
|
+
(-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
244
|
+
[0, 2]: (An equation (1, 0, -1, -1) x - 1 == 0,
|
|
245
|
+
An inequality (-1, 1, 1, 0) x + 1 >= 0,
|
|
246
|
+
An inequality (1, 0, -1, 0) x - 1 >= 0,
|
|
247
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0)
|
|
248
|
+
y0 * (-y1 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1
|
|
249
|
+
[1, 2]: (An equation (1, -1, -1, 0) x - 1 == 0,
|
|
250
|
+
An inequality (0, -1, 0, 1) x + 0 >= 0,
|
|
251
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
252
|
+
An inequality (1, 0, 0, -1) x + 0 >= 0,
|
|
253
|
+
An inequality (1, -1, 0, 0) x - 1 >= 0)
|
|
254
|
+
(-y0^2*y2*y3 + y0*y3 + y0) *
|
|
255
|
+
(-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
256
|
+
[0, 1, 2]: (An equation (0, 1, 0, -1) x + 0 == 0,
|
|
257
|
+
An equation (1, -1, -1, 0) x - 1 == 0,
|
|
258
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
259
|
+
An inequality (1, -1, 0, 0) x - 1 >= 0)
|
|
260
|
+
y0 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1
|
|
261
|
+
[3]: (An inequality (-1, 0, 0, 1) x + 0 >= 0,
|
|
262
|
+
An inequality (0, -1, -1, 1) x - 1 >= 0,
|
|
263
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0,
|
|
264
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
265
|
+
An inequality (1, 0, -1, 0) x + 0 >= 0)
|
|
266
|
+
(-y0*y1*y3^2 - y0*y3^2 + y0*y3 + y3) *
|
|
267
|
+
(-y3 + 1)^-1 * (-y0*y3 + 1)^-1 *
|
|
268
|
+
(-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
269
|
+
[0, 3]: (An equation -1 == 0,)
|
|
270
|
+
0
|
|
271
|
+
[1, 3]: (An equation (1, 0, 0, -1) x + 0 == 0,
|
|
272
|
+
An inequality (1, -1, -1, 0) x - 1 >= 0,
|
|
273
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
274
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0)
|
|
275
|
+
y0*y3 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
276
|
+
[0, 1, 3]: (An equation -1 == 0,)
|
|
277
|
+
0
|
|
278
|
+
[2, 3]: (An equation (0, 1, 1, -1) x + 1 == 0,
|
|
279
|
+
An inequality (1, 0, -1, 0) x + 0 >= 0,
|
|
280
|
+
An inequality (-1, 1, 1, 0) x + 1 >= 0,
|
|
281
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0,
|
|
282
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0)
|
|
283
|
+
(-y0*y1*y3^2 + y0*y3 + y3) *
|
|
284
|
+
(-y1*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
285
|
+
[0, 2, 3]: (An equation -1 == 0,)
|
|
286
|
+
0
|
|
287
|
+
[1, 2, 3]: (An equation (1, 0, 0, -1) x + 0 == 0,
|
|
288
|
+
An equation (1, -1, -1, 0) x - 1 == 0,
|
|
289
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
290
|
+
An inequality (1, -1, 0, 0) x - 1 >= 0)
|
|
291
|
+
y0*y3 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
292
|
+
[0, 1, 2, 3]: (An equation -1 == 0,)
|
|
293
|
+
0
|
|
294
|
+
[4]: (An inequality (-1, -1, 0, 1) x - 1 >= 0,
|
|
295
|
+
An inequality (-1, 0, 1, 0) x + 0 >= 0,
|
|
296
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
297
|
+
An inequality (1, 0, 0, 0) x + 0 >= 0)
|
|
298
|
+
y3 * (-y2 + 1)^-1 * (-y3 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
299
|
+
[0, 4]: (An equation -1 == 0,)
|
|
300
|
+
0
|
|
301
|
+
[1, 4]: (An equation -1 == 0,)
|
|
302
|
+
0
|
|
303
|
+
[0, 1, 4]: (An equation -1 == 0,)
|
|
304
|
+
0
|
|
305
|
+
[2, 4]: (An equation (1, 1, 0, -1) x + 1 == 0,
|
|
306
|
+
An inequality (-1, 0, 1, 0) x + 0 >= 0,
|
|
307
|
+
An inequality (1, 0, 0, 0) x + 0 >= 0,
|
|
308
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0)
|
|
309
|
+
y3 * (-y2 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
310
|
+
[0, 2, 4]: (An equation -1 == 0,)
|
|
311
|
+
0
|
|
312
|
+
[1, 2, 4]: (An equation -1 == 0,)
|
|
313
|
+
0
|
|
314
|
+
[0, 1, 2, 4]: (An equation -1 == 0,)
|
|
315
|
+
0
|
|
316
|
+
[3, 4]: (An equation (1, 0, -1, 0) x + 0 == 0,
|
|
317
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
318
|
+
An inequality (-1, -1, 0, 1) x - 1 >= 0,
|
|
319
|
+
An inequality (1, 0, 0, 0) x + 0 >= 0)
|
|
320
|
+
y3 * (-y3 + 1)^-1 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
321
|
+
[0, 3, 4]: (An equation -1 == 0,)
|
|
322
|
+
0
|
|
323
|
+
[1, 3, 4]: (An equation -1 == 0,)
|
|
324
|
+
0
|
|
325
|
+
[0, 1, 3, 4]: (An equation -1 == 0,)
|
|
326
|
+
0
|
|
327
|
+
[2, 3, 4]: (An equation (1, 1, 0, -1) x + 1 == 0,
|
|
328
|
+
An equation (1, 0, -1, 0) x + 0 == 0,
|
|
329
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
330
|
+
An inequality (1, 0, 0, 0) x + 0 >= 0)
|
|
331
|
+
y3 * (-y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
332
|
+
[0, 2, 3, 4]: (An equation -1 == 0,)
|
|
333
|
+
0
|
|
334
|
+
[1, 2, 3, 4]: (An equation -1 == 0,)
|
|
335
|
+
0
|
|
336
|
+
[0, 1, 2, 3, 4]: (An equation -1 == 0,)
|
|
337
|
+
0
|
|
338
|
+
|
|
339
|
+
sage: # optional - latte_int
|
|
340
|
+
sage: for J in subsets(range(len(P3))):
|
|
341
|
+
....: if not J:
|
|
342
|
+
....: continue
|
|
343
|
+
....: P = intersect([P3[j] for j in J])
|
|
344
|
+
....: f1 = generating_function_of_integral_points(P, sort_factors=True)
|
|
345
|
+
....: f2 = generating_function_of_integral_points(P, result_as_tuple=True,
|
|
346
|
+
....: algorithm="latte")
|
|
347
|
+
....: print('{}: {}'.format(J, f1.value() == sum(f2)))
|
|
348
|
+
[0]: True
|
|
349
|
+
[1]: True
|
|
350
|
+
[0, 1]: True
|
|
351
|
+
[2]: True
|
|
352
|
+
[0, 2]: True
|
|
353
|
+
[1, 2]: True
|
|
354
|
+
[0, 1, 2]: True
|
|
355
|
+
[3]: True
|
|
356
|
+
[0, 3]: True
|
|
357
|
+
[1, 3]: True
|
|
358
|
+
[0, 1, 3]: True
|
|
359
|
+
[2, 3]: True
|
|
360
|
+
[0, 2, 3]: True
|
|
361
|
+
[1, 2, 3]: True
|
|
362
|
+
[0, 1, 2, 3]: True
|
|
363
|
+
[4]: True
|
|
364
|
+
[0, 4]: True
|
|
365
|
+
[1, 4]: True
|
|
366
|
+
[0, 1, 4]: True
|
|
367
|
+
[2, 4]: True
|
|
368
|
+
[0, 2, 4]: True
|
|
369
|
+
[1, 2, 4]: True
|
|
370
|
+
[0, 1, 2, 4]: True
|
|
371
|
+
[3, 4]: True
|
|
372
|
+
[0, 3, 4]: True
|
|
373
|
+
[1, 3, 4]: True
|
|
374
|
+
[0, 1, 3, 4]: True
|
|
375
|
+
[2, 3, 4]: True
|
|
376
|
+
[0, 2, 3, 4]: True
|
|
377
|
+
[1, 2, 3, 4]: True
|
|
378
|
+
[0, 1, 2, 3, 4]: True
|
|
379
|
+
|
|
380
|
+
::
|
|
381
|
+
|
|
382
|
+
sage: P = Polyhedron(vertices=[[1], [5]])
|
|
383
|
+
sage: P.generating_function_of_integral_points()
|
|
384
|
+
y0^5 + y0^4 + y0^3 + y0^2 + y0
|
|
385
|
+
sage: P.generating_function_of_integral_points(algorithm="latte", # optional - latte_int
|
|
386
|
+
....: result_as_tuple=True)
|
|
387
|
+
(y0 * (-y0 + 1)^-1, y0^5 * (1 - y0^-1)^-1)
|
|
388
|
+
sage: P.generating_function_of_integral_points(algorithm="naive")
|
|
389
|
+
y0^5 + y0^4 + y0^3 + y0^2 + y0
|
|
390
|
+
|
|
391
|
+
.. SEEALSO::
|
|
392
|
+
|
|
393
|
+
This function is accessible via
|
|
394
|
+
:meth:`sage.geometry.polyhedron.base.Polyhedron_base.generating_function_of_integral_points`
|
|
395
|
+
as well. More examples can be found there.
|
|
396
|
+
|
|
397
|
+
TESTS::
|
|
398
|
+
|
|
399
|
+
sage: generating_function_of_integral_points(
|
|
400
|
+
....: Polyhedron(ieqs=[(0, 0, 1, 0, 0), (-1, 1, -1, 0, 0),
|
|
401
|
+
....: (0, 0, 0, 1, 0), (0, 0, 0, 0, 1)]),
|
|
402
|
+
....: sort_factors=True)
|
|
403
|
+
y0 * (-y0 + 1)^-1 * (-y2 + 1)^-1 * (-y3 + 1)^-1 * (-y0*y1 + 1)^-1
|
|
404
|
+
sage: generating_function_of_integral_points(
|
|
405
|
+
....: Polyhedron(ieqs=[(0, 0, -1, 0, 1), (0, 0, 1, 0, 0),
|
|
406
|
+
....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0),
|
|
407
|
+
....: (0, 0, 0, 1, 0)]),
|
|
408
|
+
....: sort_factors=True)
|
|
409
|
+
(-y0^2*y3 + y0*y3 + y0) *
|
|
410
|
+
(-y0 + 1)^-1 * (-y2 + 1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1
|
|
411
|
+
|
|
412
|
+
sage: generating_function_of_integral_points(
|
|
413
|
+
....: Polyhedron(ieqs=[(0, 1, 0, -1, 0, 0), (0, 0, 0, 1, 0, 0)],
|
|
414
|
+
....: eqns=[(0, 0, 0, 1, 0, -1), (0, 1, 0, 0, -1, 0),
|
|
415
|
+
....: (0, 1, -1, 0, 0, 0)]),
|
|
416
|
+
....: sort_factors=True)
|
|
417
|
+
1 * (-y0*y1*y3 + 1)^-1 * (-y0*y1*y2*y3*y4 + 1)^-1
|
|
418
|
+
|
|
419
|
+
::
|
|
420
|
+
|
|
421
|
+
sage: G = generating_function_of_integral_points(P2[0], sort_factors=True)
|
|
422
|
+
sage: S = generating_function_of_integral_points(P2[0], sort_factors=True,
|
|
423
|
+
....: split=True)
|
|
424
|
+
sage: sum(S) == G.value()
|
|
425
|
+
True
|
|
426
|
+
|
|
427
|
+
sage: G = generating_function_of_integral_points(P2[1], sort_factors=True)
|
|
428
|
+
sage: S = generating_function_of_integral_points(P2[1], sort_factors=True,
|
|
429
|
+
....: split=True)
|
|
430
|
+
sage: sum(S) == G.value()
|
|
431
|
+
True
|
|
432
|
+
|
|
433
|
+
sage: G = generating_function_of_integral_points(P3[0], sort_factors=True)
|
|
434
|
+
sage: S = generating_function_of_integral_points(P3[0], sort_factors=True,
|
|
435
|
+
....: split=True)
|
|
436
|
+
sage: sum(S) == G.value()
|
|
437
|
+
True
|
|
438
|
+
|
|
439
|
+
sage: G = generating_function_of_integral_points(P3[1], sort_factors=True)
|
|
440
|
+
sage: S1 = generating_function_of_integral_points(P3[1], sort_factors=True,
|
|
441
|
+
....: split=True)
|
|
442
|
+
sage: S2 = generating_function_of_integral_points(P3[1], # optional - latte_int
|
|
443
|
+
....: sort_factors=True,
|
|
444
|
+
....: algorithm="latte",
|
|
445
|
+
....: result_as_tuple=True)
|
|
446
|
+
sage: sum(S1) == G.value()
|
|
447
|
+
True
|
|
448
|
+
sage: sum(S2) == G.value() # optional - latte_int
|
|
449
|
+
True
|
|
450
|
+
sage: S1
|
|
451
|
+
(0,
|
|
452
|
+
...
|
|
453
|
+
0,
|
|
454
|
+
y0*y3 * (-y0*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3^2 + 1)^-1,
|
|
455
|
+
(-y0^4*y1*y2*y3^2 + y0) * (-y0 + 1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3^2 + 1)^-1,
|
|
456
|
+
0,
|
|
457
|
+
y0^2*y2 * (-y0 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3 + 1)^-1,
|
|
458
|
+
0,
|
|
459
|
+
0,
|
|
460
|
+
y0^2*y1*y3^2 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0^2*y1*y2*y3^2 + 1)^-1,
|
|
461
|
+
(-y0^5*y1^2*y2*y3^3 + y0^2*y1*y3) * (-y0 + 1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0^2*y1*y2*y3 + 1)^-1 * (-y0^2*y1*y2*y3^2 + 1)^-1,
|
|
462
|
+
0,
|
|
463
|
+
...
|
|
464
|
+
0)
|
|
465
|
+
sage: S2 # optional - latte_int
|
|
466
|
+
(y0 * (-y0 + 1)^-1 * (-y3 + 1)^-1 * (-y0*y2 + 1)^-1 * (-y0*y1*y3 + 1)^-1,
|
|
467
|
+
y0*y3 * (1 - y3^-1)^-1 * (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1)
|
|
468
|
+
sage: G
|
|
469
|
+
(-y0^2*y2*y3 - y0^2*y3 + y0*y3 + y0) * (-y0 + 1)^-1 * (-y0*y2 + 1)^-1
|
|
470
|
+
* (-y0*y3 + 1)^-1 * (-y0*y1*y3 + 1)^-1 * (-y0*y2*y3 + 1)^-1
|
|
471
|
+
|
|
472
|
+
We show the distinct polyhedra that are used when ``split=True`` and the
|
|
473
|
+
resulting polyhedra that are used in the individual computations::
|
|
474
|
+
|
|
475
|
+
sage: import logging
|
|
476
|
+
sage: logging.basicConfig(level=logging.INFO)
|
|
477
|
+
sage: sum(generating_function_of_integral_points(P2[1], sort_factors=True,
|
|
478
|
+
....: split=True))
|
|
479
|
+
...
|
|
480
|
+
INFO:sage.geometry.polyhedron.generating_function:(1/6) split polyhedron by b0 <= b1 <= b2
|
|
481
|
+
INFO:sage.geometry.polyhedron.generating_function:using polyhedron
|
|
482
|
+
b0 >= 0
|
|
483
|
+
b1 >= b0
|
|
484
|
+
b2 >= b1
|
|
485
|
+
...
|
|
486
|
+
INFO:sage.geometry.polyhedron.generating_function:(2/6) split polyhedron by b0 <= b2 < b1
|
|
487
|
+
INFO:sage.geometry.polyhedron.generating_function:using polyhedron
|
|
488
|
+
b2 >= b0
|
|
489
|
+
b1 >= 1 + b2
|
|
490
|
+
b0 >= 0
|
|
491
|
+
...
|
|
492
|
+
INFO:sage.geometry.polyhedron.generating_function:(3/6) split polyhedron by b1 < b0 <= b2
|
|
493
|
+
INFO:sage.geometry.polyhedron.generating_function:using polyhedron
|
|
494
|
+
b2 >= b0
|
|
495
|
+
b1 >= 0
|
|
496
|
+
b0 >= 1 + b1
|
|
497
|
+
...
|
|
498
|
+
INFO:sage.geometry.polyhedron.generating_function:(4/6) split polyhedron by b1 <= b2 < b0
|
|
499
|
+
INFO:sage.geometry.polyhedron.generating_function:using polyhedron
|
|
500
|
+
0 == 1
|
|
501
|
+
INFO:sage.geometry.polyhedron.generating_function:(5/6) split polyhedron by b2 < b0 <= b1
|
|
502
|
+
INFO:sage.geometry.polyhedron.generating_function:using polyhedron
|
|
503
|
+
0 == 1
|
|
504
|
+
INFO:sage.geometry.polyhedron.generating_function:(6/6) split polyhedron by b2 < b1 < b0
|
|
505
|
+
INFO:sage.geometry.polyhedron.generating_function:using polyhedron
|
|
506
|
+
0 == 1
|
|
507
|
+
1/(-y0*y1*y2^2 + y0*y1*y2 + y0*y2^2 - y0*y2 + y1*y2 - y1 - y2 + 1)
|
|
508
|
+
sage: logging.disable()
|
|
509
|
+
|
|
510
|
+
``"omega"`` can only handle polyhedra with nonnegative coordinates.
|
|
511
|
+
``"latte"`` can handle more general polyhedra.
|
|
512
|
+
(the following two examples also fail in ``"latte"``)::
|
|
513
|
+
|
|
514
|
+
sage: generating_function_of_integral_points(
|
|
515
|
+
....: Polyhedron(ieqs=[(0, 0, 1, 0, 0), (-1, 1, -1, 0, 0)]),
|
|
516
|
+
....: sort_factors=True)
|
|
517
|
+
Traceback (most recent call last):
|
|
518
|
+
...
|
|
519
|
+
NotImplementedError: cannot compute the generating function of
|
|
520
|
+
polyhedra with negative coordinates
|
|
521
|
+
sage: generating_function_of_integral_points(
|
|
522
|
+
....: Polyhedron(ieqs=[(0, 0, -1, 0, 1), (0, 0, 1, 0, 0),
|
|
523
|
+
....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0)]),
|
|
524
|
+
....: sort_factors=True)
|
|
525
|
+
Traceback (most recent call last):
|
|
526
|
+
...
|
|
527
|
+
NotImplementedError: cannot compute the generating function of
|
|
528
|
+
polyhedra with negative coordinates
|
|
529
|
+
|
|
530
|
+
sage: generating_function_of_integral_points(
|
|
531
|
+
....: Polyhedron(vertices=[(-2, 0), (5, 0)]),
|
|
532
|
+
....: sort_factors=True)
|
|
533
|
+
Traceback (most recent call last):
|
|
534
|
+
...
|
|
535
|
+
NotImplementedError: cannot compute the generating function of
|
|
536
|
+
polyhedra with negative coordinates
|
|
537
|
+
sage: generating_function_of_integral_points( # optional - latte_int
|
|
538
|
+
....: Polyhedron(vertices=[(-2, 0), (5, 0)]),
|
|
539
|
+
....: sort_factors=True, algorithm="latte", result_as_tuple=True)
|
|
540
|
+
((y0^-2) * (-y0 + 1)^-1, y0^5 * (1 - y0^-1)^-1)
|
|
541
|
+
sage: generating_function_of_integral_points(
|
|
542
|
+
....: Polyhedron(vertices=[(-2, 0), (5, 0)]),
|
|
543
|
+
....: sort_factors=True, algorithm="naive")
|
|
544
|
+
y0^5 + y0^4 + y0^3 + y0^2 + y0 + 1 + y0^-1 + y0^-2
|
|
545
|
+
|
|
546
|
+
The outputs of different algorithm options might be different.
|
|
547
|
+
But the sum of the output from ``"latte"`` is the same as the output from ``"omega"``::
|
|
548
|
+
|
|
549
|
+
sage: generating_function_of_integral_points(
|
|
550
|
+
....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
|
|
551
|
+
....: (0, 0, 0, 1), (0, 1, 0, 0)]),
|
|
552
|
+
....: name='z',
|
|
553
|
+
....: sort_factors=True)
|
|
554
|
+
(-z0*z1*z2 - z0*z2 + z0 + z2) *
|
|
555
|
+
(-z0 + 1)^-1 * (-z2 + 1)^-1 * (-z0*z1 + 1)^-1 * (-z1*z2 + 1)^-1
|
|
556
|
+
sage: generating_function_of_integral_points(
|
|
557
|
+
....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
|
|
558
|
+
....: (0, 0, 0, 1), (0, 1, 0, 0)]),
|
|
559
|
+
....: name='mu',
|
|
560
|
+
....: sort_factors=True)
|
|
561
|
+
(-mu0*mu1*mu2 - mu0*mu2 + mu0 + mu2) *
|
|
562
|
+
(-mu0 + 1)^-1 * (-mu2 + 1)^-1 * (-mu0*mu1 + 1)^-1 * (-mu1*mu2 + 1)^-1
|
|
563
|
+
|
|
564
|
+
sage: # optional - latte_int
|
|
565
|
+
sage: generating_function_of_integral_points(
|
|
566
|
+
....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
|
|
567
|
+
....: (0, 0, 0, 1), (0, 1, 0, 0)]),
|
|
568
|
+
....: name='z',
|
|
569
|
+
....: sort_factors=True, algorithm="latte", result_as_tuple=True)
|
|
570
|
+
(z0 * (-z0 + 1)^-1 * (-z0*z1 + 1)^-1 * (1 - z0^-1*z2)^-1,
|
|
571
|
+
z2 * (-z2 + 1)^-1 * (-z0*z2^-1 + 1)^-1 * (-z1*z2 + 1)^-1)
|
|
572
|
+
sage: generating_function_of_integral_points(
|
|
573
|
+
....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
|
|
574
|
+
....: (0, 0, 0, 1), (0, 1, 0, 0)]),
|
|
575
|
+
....: name='mu',
|
|
576
|
+
....: sort_factors=True, algorithm="latte", result_as_tuple=True)
|
|
577
|
+
(mu0 * (-mu0 + 1)^-1 * (-mu0*mu1 + 1)^-1 * (1 - mu0^-1*mu2)^-1,
|
|
578
|
+
mu2 * (-mu2 + 1)^-1 * (-mu0*mu2^-1 + 1)^-1 * (-mu1*mu2 + 1)^-1)
|
|
579
|
+
|
|
580
|
+
sage: f1 = generating_function_of_integral_points(
|
|
581
|
+
....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
|
|
582
|
+
....: (0, 0, 0, 1), (0, 1, 0, 0)]),
|
|
583
|
+
....: name='z',
|
|
584
|
+
....: sort_factors=True)
|
|
585
|
+
sage: f2 = generating_function_of_integral_points( # optional - latte_int
|
|
586
|
+
....: Polyhedron(ieqs=[(0, 0, 1, 0), (-1, 1, -1, 1),
|
|
587
|
+
....: (0, 0, 0, 1), (0, 1, 0, 0)]),
|
|
588
|
+
....: name='z',
|
|
589
|
+
....: sort_factors=True, algorithm="latte", result_as_tuple=True)
|
|
590
|
+
sage: f1.value() == sum(f2) # optional - latte_int
|
|
591
|
+
True
|
|
592
|
+
|
|
593
|
+
::
|
|
594
|
+
|
|
595
|
+
sage: generating_function_of_integral_points(P2[0],
|
|
596
|
+
....: sort_factors=True, names=('a',))
|
|
597
|
+
1 * (-a0 + 1)^-1 * (-a1 + 1)^-1 * (-a0*a2 + 1)^-1
|
|
598
|
+
sage: generating_function_of_integral_points(P2[0],
|
|
599
|
+
....: sort_factors=True, names=('a', 'b'))
|
|
600
|
+
Traceback (most recent call last):
|
|
601
|
+
...
|
|
602
|
+
NotImplementedError: exactly one variable name has to be provided
|
|
603
|
+
sage: generating_function_of_integral_points(P2[0],
|
|
604
|
+
....: sort_factors=True, name='a', names=('a',))
|
|
605
|
+
1 * (-a0 + 1)^-1 * (-a1 + 1)^-1 * (-a0*a2 + 1)^-1
|
|
606
|
+
sage: generating_function_of_integral_points(P2[0],
|
|
607
|
+
....: sort_factors=True, name='a', names=('b',))
|
|
608
|
+
Traceback (most recent call last):
|
|
609
|
+
...
|
|
610
|
+
ValueError: keyword argument 'name' cannot be combined with 'names'
|
|
611
|
+
|
|
612
|
+
::
|
|
613
|
+
|
|
614
|
+
sage: # needs sage.symbolic
|
|
615
|
+
sage: P = Polyhedron(rays=[(1, sqrt(2)), (0, 1)])
|
|
616
|
+
Traceback (most recent call last):
|
|
617
|
+
...
|
|
618
|
+
ValueError: no default backend for computations with Symbolic Ring
|
|
619
|
+
sage: P = Polyhedron(ieqs=[(RDF(pi), RDF(1))])
|
|
620
|
+
sage: P.generating_function_of_integral_points()
|
|
621
|
+
Traceback (most recent call last):
|
|
622
|
+
...
|
|
623
|
+
TypeError: base ring Real Double Field of the polyhedron not ZZ or QQ
|
|
624
|
+
"""
|
|
625
|
+
import logging
|
|
626
|
+
logger = logging.getLogger(__name__)
|
|
627
|
+
|
|
628
|
+
from sage.combinat.permutation import Permutations
|
|
629
|
+
from sage.geometry.polyhedron.constructor import Polyhedron
|
|
630
|
+
from sage.rings.integer_ring import ZZ
|
|
631
|
+
from sage.rings.rational_field import QQ
|
|
632
|
+
from sage.structure.category_object import normalize_names
|
|
633
|
+
|
|
634
|
+
if result_as_tuple is None:
|
|
635
|
+
result_as_tuple = split
|
|
636
|
+
|
|
637
|
+
if polyhedron.is_empty():
|
|
638
|
+
from sage.structure.factorization import Factorization
|
|
639
|
+
result = Factorization([], unit=0)
|
|
640
|
+
if result_as_tuple:
|
|
641
|
+
return (result,)
|
|
642
|
+
else:
|
|
643
|
+
return result
|
|
644
|
+
|
|
645
|
+
if polyhedron.base_ring() not in (ZZ, QQ):
|
|
646
|
+
raise TypeError('base ring {} of the polyhedron not '
|
|
647
|
+
'ZZ or QQ'.format(polyhedron.base_ring()))
|
|
648
|
+
|
|
649
|
+
d = polyhedron.ambient_dim()
|
|
650
|
+
nonnegative_orthant = Polyhedron(ieqs=[dd*(0,) + (1,) + (d-dd)*(0,)
|
|
651
|
+
for dd in range(1, d+1)])
|
|
652
|
+
if algorithm == "omega" and polyhedron & nonnegative_orthant != polyhedron:
|
|
653
|
+
raise NotImplementedError('cannot compute the generating function of '
|
|
654
|
+
'polyhedra with negative coordinates')
|
|
655
|
+
|
|
656
|
+
logger.info('%s', polyhedron)
|
|
657
|
+
|
|
658
|
+
if names is not None:
|
|
659
|
+
names = normalize_names(-1, names)
|
|
660
|
+
if len(names) != 1:
|
|
661
|
+
raise NotImplementedError('exactly one variable name has to be provided')
|
|
662
|
+
if name is not None and name != names[0]:
|
|
663
|
+
raise ValueError("keyword argument 'name' cannot be combined with 'names'")
|
|
664
|
+
name = names[0]
|
|
665
|
+
if name is None:
|
|
666
|
+
name = 'y'
|
|
667
|
+
|
|
668
|
+
if algorithm == "latte":
|
|
669
|
+
from sage.interfaces.latte import count # optional - latte_int
|
|
670
|
+
cddin = polyhedron.cdd_Hrepresentation()
|
|
671
|
+
if result_as_tuple:
|
|
672
|
+
return count(cddin, cdd=True, multivariate_generating_function=True, name=name, **kwds)
|
|
673
|
+
else:
|
|
674
|
+
result = count(cddin, cdd=True, multivariate_generating_function=True, name=name, **kwds)
|
|
675
|
+
if len(result) != 1:
|
|
676
|
+
raise ValueError("cannot unpack result "
|
|
677
|
+
"(set 'result_as_tuple=True')")
|
|
678
|
+
return result[0]
|
|
679
|
+
elif algorithm == "naive":
|
|
680
|
+
if polyhedron.is_compact():
|
|
681
|
+
from sage.rings.integer_ring import ZZ
|
|
682
|
+
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
|
|
683
|
+
from sage.structure.factorization import Factorization
|
|
684
|
+
|
|
685
|
+
indices = range(polyhedron.ambient_dim())
|
|
686
|
+
|
|
687
|
+
B = LaurentPolynomialRing(ZZ,
|
|
688
|
+
tuple(name + str(k) for k in indices),
|
|
689
|
+
len(indices))
|
|
690
|
+
result = sum(B.monomial(*r) for r in polyhedron.integral_points())
|
|
691
|
+
return result
|
|
692
|
+
else:
|
|
693
|
+
raise ValueError("Polyhedron must be bounded for the naive algorithm")
|
|
694
|
+
elif algorithm != "omega":
|
|
695
|
+
raise ValueError('Algorithm must be omega, latte, or naive.')
|
|
696
|
+
|
|
697
|
+
if split is False:
|
|
698
|
+
result = _generating_function_of_integral_points_(polyhedron, name=name, **kwds)
|
|
699
|
+
if result_as_tuple:
|
|
700
|
+
return result
|
|
701
|
+
else:
|
|
702
|
+
if len(result) != 1:
|
|
703
|
+
raise ValueError("cannot unpack result "
|
|
704
|
+
"(set 'result_as_tuple=True')")
|
|
705
|
+
return result[0]
|
|
706
|
+
|
|
707
|
+
if d <= 1:
|
|
708
|
+
raise ValueError('cannot do splitting with only '
|
|
709
|
+
'dimension {}'.format(d))
|
|
710
|
+
|
|
711
|
+
parts = None
|
|
712
|
+
if split is True:
|
|
713
|
+
|
|
714
|
+
def polyhedron_from_permutation(pi):
|
|
715
|
+
|
|
716
|
+
def ieq(a, b):
|
|
717
|
+
return ((0 if a < b else -1,) +
|
|
718
|
+
tuple(1 if i == b else (-1 if i == a else 0)
|
|
719
|
+
for i in range(1, d + 1)))
|
|
720
|
+
|
|
721
|
+
def ieq_repr_rhs(a, b):
|
|
722
|
+
return (' <= ' if a < b else ' < ') + 'b{}'.format(b-1)
|
|
723
|
+
|
|
724
|
+
def ieqs_repr_lhs(pi):
|
|
725
|
+
return 'b{}'.format(pi[0]-1)
|
|
726
|
+
|
|
727
|
+
ieqs, repr_rhss = zip(*[(ieq(a, b), ieq_repr_rhs(a, b))
|
|
728
|
+
for a, b in zip(pi[:-1], pi[1:])])
|
|
729
|
+
return Polyhedron(ieqs=ieqs), ieqs_repr_lhs(pi) + ''.join(repr_rhss)
|
|
730
|
+
|
|
731
|
+
split = (polyhedron_from_permutation(pi) for pi in Permutations(d))
|
|
732
|
+
parts = ZZ(d).factorial()
|
|
733
|
+
else:
|
|
734
|
+
if isinstance(split, (list, tuple)):
|
|
735
|
+
parts = len(split)
|
|
736
|
+
split = ((ph, ph.Hrepresentation_str(**Hrepresentation_str_options))
|
|
737
|
+
for ph in split)
|
|
738
|
+
|
|
739
|
+
result = []
|
|
740
|
+
for part, (split_polyhedron, pi_log) in enumerate(split):
|
|
741
|
+
if parts is None:
|
|
742
|
+
parts_log = str(part+1)
|
|
743
|
+
else:
|
|
744
|
+
parts_log = '{}/{}'.format(part+1, parts)
|
|
745
|
+
logger.info('(%s) split polyhedron by %s', parts_log, pi_log)
|
|
746
|
+
result.append(_generating_function_of_integral_points_(
|
|
747
|
+
polyhedron & split_polyhedron, name=name, **kwds))
|
|
748
|
+
if not result_as_tuple:
|
|
749
|
+
raise ValueError("cannot unpack result"
|
|
750
|
+
"(unset 'result_as_tuple=False')")
|
|
751
|
+
return sum(result, ())
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def _generating_function_of_integral_points_(
|
|
755
|
+
polyhedron, indices=None, **kwds):
|
|
756
|
+
r"""
|
|
757
|
+
Helper function for :func:`generating_function_of_integral_points` which
|
|
758
|
+
does the mid-level stuff.
|
|
759
|
+
|
|
760
|
+
TESTS::
|
|
761
|
+
|
|
762
|
+
sage: from sage.geometry.polyhedron.generating_function import generating_function_of_integral_points
|
|
763
|
+
|
|
764
|
+
sage: generating_function_of_integral_points( # indirect doctest
|
|
765
|
+
....: Polyhedron(ieqs=[(0, 1, 0, 0), (0, -1, 1, 0)],
|
|
766
|
+
....: eqns=[(0, -1, -1, 2)]),
|
|
767
|
+
....: result_as_tuple=True, sort_factors=True)
|
|
768
|
+
(1 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1,
|
|
769
|
+
y0*y1*y2 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1)
|
|
770
|
+
"""
|
|
771
|
+
import logging
|
|
772
|
+
logger = logging.getLogger(__name__)
|
|
773
|
+
|
|
774
|
+
logger.info('using polyhedron %s',
|
|
775
|
+
polyhedron.Hrepresentation_str(**Hrepresentation_str_options))
|
|
776
|
+
|
|
777
|
+
if polyhedron.is_empty():
|
|
778
|
+
from sage.structure.factorization import Factorization
|
|
779
|
+
return (Factorization([], unit=0),)
|
|
780
|
+
|
|
781
|
+
Hrepr = polyhedron.Hrepresentation()
|
|
782
|
+
|
|
783
|
+
inequalities = tuple(tuple(entry)
|
|
784
|
+
for entry in Hrepr if entry.is_inequality())
|
|
785
|
+
equations = tuple(tuple(entry)
|
|
786
|
+
for entry in Hrepr if entry.is_equation())
|
|
787
|
+
if len(inequalities) + len(equations) != len(Hrepr):
|
|
788
|
+
raise ValueError('cannot handle {}.'.format(polyhedron))
|
|
789
|
+
|
|
790
|
+
if not inequalities:
|
|
791
|
+
raise NotImplementedError('no inequality given')
|
|
792
|
+
|
|
793
|
+
if indices is None:
|
|
794
|
+
indices = range(len(inequalities[0]) - 1)
|
|
795
|
+
|
|
796
|
+
n = len(indices) + 1
|
|
797
|
+
if any(len(e) != n for e in inequalities):
|
|
798
|
+
raise ValueError('not all coefficient vectors of the inequalities '
|
|
799
|
+
'have the same length')
|
|
800
|
+
if any(len(e) != n for e in equations):
|
|
801
|
+
raise ValueError('not all coefficient vectors of the equations '
|
|
802
|
+
'have the same length')
|
|
803
|
+
|
|
804
|
+
mods = _TransformMod.generate_mods(equations)
|
|
805
|
+
logger.debug('splitting by moduli %s', mods)
|
|
806
|
+
|
|
807
|
+
return tuple(__generating_function_of_integral_points__(
|
|
808
|
+
indices, inequalities, equations, mod, **kwds) for mod in mods)
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
def __generating_function_of_integral_points__(
|
|
812
|
+
indices, inequalities, equations, mod,
|
|
813
|
+
name,
|
|
814
|
+
Factorization_sort=False, Factorization_simplify=False,
|
|
815
|
+
sort_factors=False):
|
|
816
|
+
r"""
|
|
817
|
+
Helper function for :func:`generating_function_of_integral_points` which
|
|
818
|
+
does the actual computation of the generating function.
|
|
819
|
+
|
|
820
|
+
TESTS::
|
|
821
|
+
|
|
822
|
+
sage: from sage.geometry.polyhedron.generating_function import __generating_function_of_integral_points__
|
|
823
|
+
|
|
824
|
+
sage: __generating_function_of_integral_points__(
|
|
825
|
+
....: (0, 2), [(0, 1, 0)], [(1, -1, 2)],
|
|
826
|
+
....: {0: (2, 1)}, name='y', sort_factors=True)
|
|
827
|
+
y0 * (-y0^2*y2 + 1)^-1
|
|
828
|
+
sage: __generating_function_of_integral_points__(
|
|
829
|
+
....: srange(3), [(0, 1, 0, 0), (0, 0, 1, 0)], [(1, -1, 0, 2)],
|
|
830
|
+
....: {0: (2, 1)}, name='y', sort_factors=True)
|
|
831
|
+
y0 * (-y1 + 1)^-1 * (-y0^2*y2 + 1)^-1
|
|
832
|
+
sage: __generating_function_of_integral_points__(
|
|
833
|
+
....: srange(3), [(0, 1, 0, 0), (0, -1, 1, 0)], [(0, -1, -1, 2)],
|
|
834
|
+
....: {0: (2, 1), 1: (2, 1)}, name='y', sort_factors=True)
|
|
835
|
+
y0*y1*y2 * (-y1^2*y2 + 1)^-1 * (-y0^2*y1^2*y2^2 + 1)^-1
|
|
836
|
+
"""
|
|
837
|
+
import logging
|
|
838
|
+
logger = logging.getLogger(__name__)
|
|
839
|
+
|
|
840
|
+
from sage.rings.integer_ring import ZZ
|
|
841
|
+
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
|
|
842
|
+
from sage.structure.factorization import Factorization
|
|
843
|
+
|
|
844
|
+
B = LaurentPolynomialRing(ZZ,
|
|
845
|
+
tuple(name + str(k) for k in indices),
|
|
846
|
+
len(indices))
|
|
847
|
+
|
|
848
|
+
logger.info('preprocessing %s inequalities and %s equations...',
|
|
849
|
+
len(inequalities), len(equations))
|
|
850
|
+
|
|
851
|
+
T_mod = _TransformMod(inequalities, equations, B, mod)
|
|
852
|
+
inequalities = T_mod.inequalities
|
|
853
|
+
equations = T_mod.equations
|
|
854
|
+
|
|
855
|
+
T_equations = _EliminateByEquations(inequalities, equations, B)
|
|
856
|
+
inequalities = T_equations.inequalities
|
|
857
|
+
equations = T_equations.equations
|
|
858
|
+
|
|
859
|
+
T_inequalities = _SplitOffSimpleInequalities(inequalities, equations, B)
|
|
860
|
+
inequalities = T_inequalities.inequalities
|
|
861
|
+
equations = T_inequalities.equations
|
|
862
|
+
assert not equations
|
|
863
|
+
|
|
864
|
+
logger.info('%s inequalities left; using Omega...', len(inequalities))
|
|
865
|
+
numerator, terms = _generating_function_via_Omega_(
|
|
866
|
+
inequalities, B, skip_indices=T_equations.indices)
|
|
867
|
+
|
|
868
|
+
numerator, terms = T_inequalities.apply_rules(numerator, terms)
|
|
869
|
+
numerator, terms = T_equations.apply_rules(numerator, terms)
|
|
870
|
+
numerator, terms = T_mod.apply_rules(numerator, terms)
|
|
871
|
+
|
|
872
|
+
if sort_factors:
|
|
873
|
+
def key(t):
|
|
874
|
+
D = t.monomial_coefficients().popitem()[0]
|
|
875
|
+
return (-sum(abs(d) for d in D), D)
|
|
876
|
+
terms = sorted(terms, key=key, reverse=True)
|
|
877
|
+
return Factorization([(numerator, 1)] +
|
|
878
|
+
list((1-t, -1) for t in terms),
|
|
879
|
+
sort=Factorization_sort,
|
|
880
|
+
simplify=Factorization_simplify)
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
def _generating_function_via_Omega_(inequalities, B, skip_indices=()):
|
|
884
|
+
r"""
|
|
885
|
+
Compute the generating function of the integral points of the
|
|
886
|
+
polyhedron specified by ``inequalities`` via
|
|
887
|
+
:func:`MacMahon's Omega operator <sage.rings.polynomial.omega.MacMahonOmega>`.
|
|
888
|
+
|
|
889
|
+
INPUT:
|
|
890
|
+
|
|
891
|
+
- ``inequalities`` -- list or other iterable of tuples
|
|
892
|
+
of numbers
|
|
893
|
+
|
|
894
|
+
- ``B`` -- a Laurent polynomial ring
|
|
895
|
+
|
|
896
|
+
- ``skip_indices`` -- list or tuple of indices
|
|
897
|
+
|
|
898
|
+
The variables corresponding to ``skip_indices`` are not handled
|
|
899
|
+
(e.g. because they are determined by an equation).
|
|
900
|
+
|
|
901
|
+
OUTPUT: a pair of
|
|
902
|
+
|
|
903
|
+
- a Laurent polynomial specifying the numerator and
|
|
904
|
+
|
|
905
|
+
- a tuple of Laurent polynomials specifying the denominator;
|
|
906
|
+
each entry `t` corresponds to a factor `1 - t`.
|
|
907
|
+
|
|
908
|
+
EXAMPLES::
|
|
909
|
+
|
|
910
|
+
sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_
|
|
911
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
|
|
912
|
+
sage: _generating_function_via_Omega_([(0, -1, -1, 1)], B)
|
|
913
|
+
(1, (y2, y0*y2, y1*y2))
|
|
914
|
+
"""
|
|
915
|
+
import logging
|
|
916
|
+
logger = logging.getLogger(__name__)
|
|
917
|
+
|
|
918
|
+
from .representation import repr_pretty
|
|
919
|
+
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
|
|
920
|
+
from sage.rings.polynomial.omega import _Omega_, _simplify_, partition
|
|
921
|
+
|
|
922
|
+
numerator = B(1)
|
|
923
|
+
terms = B.gens()
|
|
924
|
+
L = B
|
|
925
|
+
mu = 'mu' if not repr(B.gen()).startswith('mu') else 'nu'
|
|
926
|
+
for i, coeffs in enumerate(inequalities):
|
|
927
|
+
L = LaurentPolynomialRing(L, mu + str(i), sparse=True)
|
|
928
|
+
l = L.gen()
|
|
929
|
+
logger.debug('mapping %s --> %s', l, repr_pretty(coeffs, 0))
|
|
930
|
+
it_coeffs = iter(coeffs)
|
|
931
|
+
numerator *= l**next(it_coeffs)
|
|
932
|
+
assert numerator.parent() == L
|
|
933
|
+
terms = tuple(l**c * t for c, t in zip(it_coeffs, terms))
|
|
934
|
+
assert all(y == t for y, t in
|
|
935
|
+
(tuple(zip(B.gens(), terms))[i] for i in skip_indices))
|
|
936
|
+
terms = tuple(t for i, t in enumerate(terms)
|
|
937
|
+
if i not in skip_indices)
|
|
938
|
+
|
|
939
|
+
logger.debug('terms denominator %s', terms)
|
|
940
|
+
|
|
941
|
+
def decode_factor(factor):
|
|
942
|
+
D = factor.monomial_coefficients()
|
|
943
|
+
assert len(D) == 1
|
|
944
|
+
exponent, coefficient = next(iter(D.items()))
|
|
945
|
+
return coefficient, exponent
|
|
946
|
+
|
|
947
|
+
while repr(numerator.parent().gen()).startswith(mu):
|
|
948
|
+
logger.info('applying Omega[%s]...', numerator.parent().gen())
|
|
949
|
+
logger.debug('...on terms denominator %s', terms)
|
|
950
|
+
logger.debug('...(numerator has %s terms)', numerator.number_of_terms())
|
|
951
|
+
logger.debug('...numerator %s', numerator)
|
|
952
|
+
|
|
953
|
+
decoded_factors, other_factors = \
|
|
954
|
+
partition((decode_factor(factor) for factor in terms),
|
|
955
|
+
lambda factor: factor[1] == 0)
|
|
956
|
+
other_factors = tuple(factor[0] for factor in other_factors)
|
|
957
|
+
numerator, factors_denominator = \
|
|
958
|
+
_Omega_(numerator.monomial_coefficients(), tuple(decoded_factors))
|
|
959
|
+
terms = other_factors + factors_denominator
|
|
960
|
+
|
|
961
|
+
return _simplify_(numerator, terms)
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
class _TransformHrepresentation:
|
|
965
|
+
r"""
|
|
966
|
+
An abstract base class for transformations of the
|
|
967
|
+
Hrepresentation of a polyhedron together with its
|
|
968
|
+
back-substitutions of the corresponding generating function.
|
|
969
|
+
|
|
970
|
+
INPUT:
|
|
971
|
+
|
|
972
|
+
- ``inequalities`` -- list of tuples of numbers
|
|
973
|
+
|
|
974
|
+
- ``equations`` -- list of tuples of numbers
|
|
975
|
+
|
|
976
|
+
- ``B`` -- a Laurent polynomial ring
|
|
977
|
+
|
|
978
|
+
ATTRIBUTES:
|
|
979
|
+
|
|
980
|
+
- ``inequalities``, ``equations`` -- list of tuples
|
|
981
|
+
|
|
982
|
+
Determine the generating function of these inequalities
|
|
983
|
+
and equations instead of the input.
|
|
984
|
+
|
|
985
|
+
- ``factor`` -- a Laurent polynomial
|
|
986
|
+
|
|
987
|
+
The numerator of the generating function has to be multiplied
|
|
988
|
+
with ``factor`` *after* substituting ``rules``.
|
|
989
|
+
|
|
990
|
+
- ``rules`` -- dictionary mapping Laurent polynomial variables to
|
|
991
|
+
Laurent polynomials
|
|
992
|
+
|
|
993
|
+
Substitute ``rules`` into the generating function.
|
|
994
|
+
|
|
995
|
+
The generating function of the input ``inequalities`` and
|
|
996
|
+
``equations`` is equal to the generating function of the
|
|
997
|
+
attributes ``inequalities`` and ``equations`` in which ``rules``
|
|
998
|
+
were substituted and ``factor`` was multiplied (via
|
|
999
|
+
:meth:`~_TransformHrepresentation.apply_rules`).
|
|
1000
|
+
"""
|
|
1001
|
+
|
|
1002
|
+
def __init__(self, inequalities, equations, B):
|
|
1003
|
+
r"""
|
|
1004
|
+
See :class:`_TransformHrepresentation` for details.
|
|
1005
|
+
|
|
1006
|
+
TESTS::
|
|
1007
|
+
|
|
1008
|
+
sage: from sage.geometry.polyhedron.generating_function import _TransformHrepresentation
|
|
1009
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
|
|
1010
|
+
sage: _TransformHrepresentation([(1, 2, 3)], [(0, -1, 1)], B)
|
|
1011
|
+
Traceback (most recent call last):
|
|
1012
|
+
...
|
|
1013
|
+
NotImplementedError
|
|
1014
|
+
"""
|
|
1015
|
+
self.inequalities = inequalities
|
|
1016
|
+
self.equations = equations
|
|
1017
|
+
self.B = B
|
|
1018
|
+
self._transform_()
|
|
1019
|
+
|
|
1020
|
+
def _transform_(self):
|
|
1021
|
+
r"""
|
|
1022
|
+
Transform the input given on construction of this object.
|
|
1023
|
+
|
|
1024
|
+
This is called during :meth:`__init__`. Overload this method
|
|
1025
|
+
in derived classes.
|
|
1026
|
+
|
|
1027
|
+
TESTS::
|
|
1028
|
+
|
|
1029
|
+
sage: from sage.geometry.polyhedron.generating_function import _TransformHrepresentation
|
|
1030
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
|
|
1031
|
+
sage: _TransformHrepresentation([(1, 2, 3)], [(0, -1, 1)], B)
|
|
1032
|
+
Traceback (most recent call last):
|
|
1033
|
+
...
|
|
1034
|
+
NotImplementedError
|
|
1035
|
+
"""
|
|
1036
|
+
raise NotImplementedError
|
|
1037
|
+
|
|
1038
|
+
def apply_rules(self, numerator, terms):
|
|
1039
|
+
r"""
|
|
1040
|
+
Substitute the generated rules.
|
|
1041
|
+
|
|
1042
|
+
INPUT:
|
|
1043
|
+
|
|
1044
|
+
- ``numerator`` -- a Laurent polynomial
|
|
1045
|
+
|
|
1046
|
+
- ``terms`` -- tuple or other iterable of Laurent polynomials
|
|
1047
|
+
|
|
1048
|
+
The denominator is the product of factors `1 - t` for each
|
|
1049
|
+
`t` in ``terms``.
|
|
1050
|
+
|
|
1051
|
+
OUTPUT:
|
|
1052
|
+
|
|
1053
|
+
A pair of a Laurent polynomial and a tuple of Laurent polynomials
|
|
1054
|
+
representing numerator and denominator as described in the
|
|
1055
|
+
INPUT-section.
|
|
1056
|
+
|
|
1057
|
+
EXAMPLES::
|
|
1058
|
+
|
|
1059
|
+
sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities as prepare
|
|
1060
|
+
sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_ as gf
|
|
1061
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
|
|
1062
|
+
sage: ieqs = [(0, -1, 1, 0)]
|
|
1063
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1064
|
+
([], 1, {y2: y2, y1: y1, y0: y0*y1})
|
|
1065
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1066
|
+
(1, (y0*y1, y1, y2))
|
|
1067
|
+
"""
|
|
1068
|
+
return (numerator.subs(self.rules) * self.factor,
|
|
1069
|
+
tuple(t.subs(self.rules) for t in terms))
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
class _SplitOffSimpleInequalities(_TransformHrepresentation):
|
|
1073
|
+
r"""
|
|
1074
|
+
Split off (simple) inequalities which can be handled better
|
|
1075
|
+
without passing them to Omega.
|
|
1076
|
+
|
|
1077
|
+
INPUT:
|
|
1078
|
+
|
|
1079
|
+
- ``inequalities`` -- list of tuples of numbers
|
|
1080
|
+
|
|
1081
|
+
- ``equations`` -- list of tuples of numbers
|
|
1082
|
+
|
|
1083
|
+
- ``B`` -- a Laurent polynomial ring
|
|
1084
|
+
|
|
1085
|
+
ATTRIBUTES:
|
|
1086
|
+
|
|
1087
|
+
- ``inequalities``, ``equations`` -- list of tuples
|
|
1088
|
+
|
|
1089
|
+
Determine the generating function of these inequalities
|
|
1090
|
+
and equations instead of the input.
|
|
1091
|
+
|
|
1092
|
+
- ``factor`` -- a Laurent polynomial
|
|
1093
|
+
|
|
1094
|
+
The numerator of the generating function has to be multiplied
|
|
1095
|
+
with ``factor`` *after* substituting ``rules``.
|
|
1096
|
+
|
|
1097
|
+
- ``rules`` -- dictionary mapping Laurent polynomial variables to
|
|
1098
|
+
Laurent polynomials
|
|
1099
|
+
|
|
1100
|
+
Substitute ``rules`` into the generating function.
|
|
1101
|
+
|
|
1102
|
+
The generating function of the input ``inequalities`` and
|
|
1103
|
+
``equations`` is equal to the generating function of the
|
|
1104
|
+
attributes ``inequalities`` and ``equations`` in which ``rules``
|
|
1105
|
+
were substitited and ``factor`` was multiplied (via
|
|
1106
|
+
:meth:`~_TransformHrepresentation.apply_rules`).
|
|
1107
|
+
|
|
1108
|
+
EXAMPLES::
|
|
1109
|
+
|
|
1110
|
+
sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities as prepare
|
|
1111
|
+
sage: from sage.geometry.polyhedron.generating_function import _generating_function_via_Omega_ as gf
|
|
1112
|
+
|
|
1113
|
+
sage: def eq(A, B):
|
|
1114
|
+
....: return (A[0] == B[0] and
|
|
1115
|
+
....: sorted(A[1], key=repr) == sorted(B[1], key=repr))
|
|
1116
|
+
|
|
1117
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
|
|
1118
|
+
|
|
1119
|
+
sage: ieqs = [(0, -1, 1, 0), (2, -1, -1, 1)]
|
|
1120
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1121
|
+
([(2, -2, -1, 1)], 1, {y2: y2, y1: y1, y0: y0*y1})
|
|
1122
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1123
|
+
(y0*y1^3*y2^3 - y0*y1^3*y2^2 + y0*y1^2*y2^3 - y0*y1^2*y2^2
|
|
1124
|
+
- y0*y1*y2^2 - y1^2*y2 + y0*y1 + y1^2 - y1*y2 + y1 + 1,
|
|
1125
|
+
(y2, y1*y2, y0*y1*y2^2))
|
|
1126
|
+
sage: eq(_, gf(ieqs, B))
|
|
1127
|
+
True
|
|
1128
|
+
|
|
1129
|
+
sage: ieqs = [(-1, -1, 1, 0), (2, -1, -1, 1)]
|
|
1130
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1131
|
+
([(1, -2, -1, 1)], y1, {y2: y2, y1: y1, y0: y0*y1})
|
|
1132
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1133
|
+
(y0*y1^3*y2^3 - y0*y1^3*y2^2 - y0*y1^2*y2^2
|
|
1134
|
+
+ y0*y1^2*y2 - y1^2*y2 + y1^2 + y1,
|
|
1135
|
+
(y2, y1*y2, y0*y1*y2^2))
|
|
1136
|
+
sage: eq(_, gf(ieqs, B))
|
|
1137
|
+
True
|
|
1138
|
+
|
|
1139
|
+
sage: ieqs = [(-2, -1, 1, 0), (2, -1, -1, 1)]
|
|
1140
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1141
|
+
([(0, -2, -1, 1)], y1^2, {y2: y2, y1: y1, y0: y0*y1})
|
|
1142
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1143
|
+
(y1^2, (y2, y1*y2, y0*y1*y2^2))
|
|
1144
|
+
sage: eq(_, gf(ieqs, B))
|
|
1145
|
+
True
|
|
1146
|
+
|
|
1147
|
+
sage: ieqs = [(2, -1, 1, 0), (2, -1, -1, 1)]
|
|
1148
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1149
|
+
([(2, -1, 1, 0), (2, -1, -1, 1)], 1, {y2: y2, y1: y1, y0: y0})
|
|
1150
|
+
sage: eq(T.apply_rules(*gf(T.inequalities, B)), gf(ieqs, B))
|
|
1151
|
+
True
|
|
1152
|
+
|
|
1153
|
+
TESTS::
|
|
1154
|
+
|
|
1155
|
+
sage: # needs sage.symbolic
|
|
1156
|
+
sage: def eq2(A, B):
|
|
1157
|
+
....: a = SR(repr(A[0])) * prod(1-SR(repr(t)) for t in B[1])
|
|
1158
|
+
....: b = SR(repr(B[0])) * prod(1-SR(repr(t)) for t in A[1])
|
|
1159
|
+
....: return bool((a-b).full_simplify() == 0)
|
|
1160
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
|
|
1161
|
+
sage: ieqs = [(-2, 1, -1, 0), (-2, -1, 0, 1), (-1, -1, -1, 3)]
|
|
1162
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1163
|
+
([(9, 2, 1, 3)], y0^2*y2^4, {y2: y2, y1: y0*y1*y2, y0: y0*y2})
|
|
1164
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1165
|
+
(y0^2*y2^4, (y2, y0*y2, y0*y1*y2))
|
|
1166
|
+
sage: eq2(_, gf(ieqs, B))
|
|
1167
|
+
True
|
|
1168
|
+
|
|
1169
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 4)
|
|
1170
|
+
sage: ieqs = [(-1, 1, -1, 0, 0)]
|
|
1171
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1172
|
+
([], y0, {y3: y3, y2: y2, y1: y0*y1, y0: y0})
|
|
1173
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1174
|
+
(y0, (y0, y0*y1, y2, y3))
|
|
1175
|
+
sage: eq(_, gf(ieqs, B))
|
|
1176
|
+
True
|
|
1177
|
+
|
|
1178
|
+
sage: ieqs = [(0, 0, -1, 0, 1), (0, 0, 1, 0, 0),
|
|
1179
|
+
....: (0, 1, 0, 0, -1), (-1, 1, -1, 0, 0)]
|
|
1180
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1181
|
+
([(1, 1, 0, 0, -1)], y0, {y3: y3, y2: y2, y1: y0*y1*y3, y0: y0})
|
|
1182
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1183
|
+
(-y0^2*y3 + y0*y3 + y0, (y0*y1*y3, y2, y0, y0*y3))
|
|
1184
|
+
sage: eq(_, gf(ieqs, B))
|
|
1185
|
+
True
|
|
1186
|
+
|
|
1187
|
+
sage: ieqs = [(-2, 1, -1, 0, 0)]
|
|
1188
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1189
|
+
([], y0^2, {y3: y3, y2: y2, y1: y0*y1, y0: y0})
|
|
1190
|
+
|
|
1191
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1192
|
+
(y0^2, (y0, y0*y1, y2, y3))
|
|
1193
|
+
sage: eq(_, gf(ieqs, B))
|
|
1194
|
+
True
|
|
1195
|
+
|
|
1196
|
+
sage: ieqs = [(0, -1, 1, 0, 0), (-2, 0, -1, 0, 1),
|
|
1197
|
+
....: (0, -1, 0, 1, 0), (-3, 0, 0, -1, 1)]
|
|
1198
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1199
|
+
([(1, 0, -1, 1, 1)],
|
|
1200
|
+
y3^3,
|
|
1201
|
+
{y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3})
|
|
1202
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1203
|
+
(-y1*y2*y3^4 - y1*y3^4 + y1*y3^3 + y3^3,
|
|
1204
|
+
(y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3))
|
|
1205
|
+
sage: eq(_, gf(ieqs, B))
|
|
1206
|
+
True
|
|
1207
|
+
|
|
1208
|
+
sage: ieqs = [(0, -1, 1, 0, 0), (-3, 0, -1, 0, 1),
|
|
1209
|
+
....: (0, -1, 0, 1, 0), (-2, 0, 0, -1, 1)]
|
|
1210
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1211
|
+
([(1, 0, 1, -1, 1)],
|
|
1212
|
+
y3^3,
|
|
1213
|
+
{y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3})
|
|
1214
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1215
|
+
(-y1*y2*y3^4 - y2*y3^4 + y2*y3^3 + y3^3,
|
|
1216
|
+
(y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3))
|
|
1217
|
+
sage: eq(_, gf(ieqs, B))
|
|
1218
|
+
True
|
|
1219
|
+
|
|
1220
|
+
sage: ieqs = [(0, -1, 1, 0, 0), (-2, 0, -1, 0, 1),
|
|
1221
|
+
....: (-3, -1, 0, 1, 0), (0, 0, 0, -1, 1)]
|
|
1222
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1223
|
+
([(1, 0, -1, 1, 1)],
|
|
1224
|
+
y2^3*y3^3,
|
|
1225
|
+
{y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3})
|
|
1226
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1227
|
+
(-y1*y2^4*y3^4 - y1*y2^3*y3^4 + y1*y2^3*y3^3 + y2^3*y3^3,
|
|
1228
|
+
(y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3))
|
|
1229
|
+
sage: eq(_, gf(ieqs, B))
|
|
1230
|
+
True
|
|
1231
|
+
|
|
1232
|
+
sage: ieqs = [(0, -1, 1, 0, 0), (-3, 0, -1, 0, 1),
|
|
1233
|
+
....: (-2, -1, 0, 1, 0), (0, 0, 0, -1, 1)]
|
|
1234
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1235
|
+
([(1, 0, 1, -1, 1)],
|
|
1236
|
+
y2^2*y3^3,
|
|
1237
|
+
{y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3})
|
|
1238
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1239
|
+
(-y1*y2^3*y3^4 - y2^3*y3^4 + y2^3*y3^3 + y2^2*y3^3,
|
|
1240
|
+
(y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3))
|
|
1241
|
+
sage: eq(_, gf(ieqs, B))
|
|
1242
|
+
True
|
|
1243
|
+
|
|
1244
|
+
sage: ieqs = [(-2, -1, 1, 0, 0), (0, 0, -1, 0, 1),
|
|
1245
|
+
....: (0, -1, 0, 1, 0), (-3, 0, 0, -1, 1)]
|
|
1246
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1247
|
+
([(1, 0, -1, 1, 1)],
|
|
1248
|
+
y1^2*y3^3,
|
|
1249
|
+
{y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3})
|
|
1250
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1251
|
+
(-y1^3*y2*y3^4 - y1^3*y3^4 + y1^3*y3^3 + y1^2*y3^3,
|
|
1252
|
+
(y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3))
|
|
1253
|
+
sage: eq(_, gf(ieqs, B))
|
|
1254
|
+
True
|
|
1255
|
+
|
|
1256
|
+
sage: ieqs = [(-3, -1, 1, 0, 0), (0, 0, -1, 0, 1),
|
|
1257
|
+
....: (0, -1, 0, 1, 0), (-2, 0, 0, -1, 1)]
|
|
1258
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1259
|
+
([(1, 0, 1, -1, 1)],
|
|
1260
|
+
y1^3*y3^3,
|
|
1261
|
+
{y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3})
|
|
1262
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1263
|
+
(-y1^4*y2*y3^4 - y1^3*y2*y3^4 + y1^3*y2*y3^3 + y1^3*y3^3,
|
|
1264
|
+
(y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3))
|
|
1265
|
+
sage: eq(_, gf(ieqs, B))
|
|
1266
|
+
True
|
|
1267
|
+
|
|
1268
|
+
sage: ieqs = [(-2, -1, 1, 0, 0), (0, 0, -1, 0, 1),
|
|
1269
|
+
....: (-3, -1, 0, 1, 0), (0, 0, 0, -1, 1)]
|
|
1270
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1271
|
+
([(1, 0, -1, 1, 1)],
|
|
1272
|
+
y1^2*y2^3*y3^3,
|
|
1273
|
+
{y3: y3, y2: y2*y3, y1: y1, y0: y0*y1*y2*y3})
|
|
1274
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1275
|
+
(-y1^3*y2^4*y3^4 - y1^3*y2^3*y3^4 + y1^3*y2^3*y3^3 + y1^2*y2^3*y3^3,
|
|
1276
|
+
(y0*y1*y2*y3, y2*y3, y3, y1*y2*y3, y1*y3))
|
|
1277
|
+
sage: eq(_, gf(ieqs, B))
|
|
1278
|
+
True
|
|
1279
|
+
|
|
1280
|
+
sage: ieqs = [(-3, -1, 1, 0, 0), (0, 0, -1, 0, 1),
|
|
1281
|
+
....: (-2, -1, 0, 1, 0), (0, 0, 0, -1, 1)]
|
|
1282
|
+
sage: T = prepare(ieqs, [], B); T.inequalities, T.factor, T.rules
|
|
1283
|
+
([(1, 0, 1, -1, 1)],
|
|
1284
|
+
y1^3*y2^2*y3^3,
|
|
1285
|
+
{y3: y3, y2: y2, y1: y1*y3, y0: y0*y1*y2*y3})
|
|
1286
|
+
sage: T.apply_rules(*gf(T.inequalities, B))
|
|
1287
|
+
(-y1^4*y2^3*y3^4 - y1^3*y2^3*y3^4 + y1^3*y2^3*y3^3 + y1^3*y2^2*y3^3,
|
|
1288
|
+
(y0*y1*y2*y3, y1*y3, y3, y1*y2*y3, y2*y3))
|
|
1289
|
+
sage: eq(_, gf(ieqs, B))
|
|
1290
|
+
True
|
|
1291
|
+
"""
|
|
1292
|
+
|
|
1293
|
+
def _transform_(self):
|
|
1294
|
+
r"""
|
|
1295
|
+
Do the actual transformation.
|
|
1296
|
+
|
|
1297
|
+
This is called during initialization.
|
|
1298
|
+
See :class:`_SplitOffSimpleInequalities` for details.
|
|
1299
|
+
|
|
1300
|
+
TESTS::
|
|
1301
|
+
|
|
1302
|
+
sage: from sage.geometry.polyhedron.generating_function import _SplitOffSimpleInequalities
|
|
1303
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
|
|
1304
|
+
sage: T = _SplitOffSimpleInequalities([(1, 1, 1)], [], B) # indirect doctest
|
|
1305
|
+
"""
|
|
1306
|
+
inequalities = self.inequalities
|
|
1307
|
+
B = self.B
|
|
1308
|
+
|
|
1309
|
+
import logging
|
|
1310
|
+
logger = logging.getLogger(__name__)
|
|
1311
|
+
|
|
1312
|
+
from itertools import takewhile
|
|
1313
|
+
from .representation import repr_pretty
|
|
1314
|
+
from sage.graphs.digraph import DiGraph
|
|
1315
|
+
from sage.matrix.constructor import matrix
|
|
1316
|
+
from sage.modules.free_module_element import vector
|
|
1317
|
+
from sage.rings.integer_ring import ZZ
|
|
1318
|
+
|
|
1319
|
+
inequalities_filtered = []
|
|
1320
|
+
chain_links = {}
|
|
1321
|
+
for coeffs in inequalities:
|
|
1322
|
+
dim = len(coeffs)
|
|
1323
|
+
if all(c >= 0 for c in coeffs):
|
|
1324
|
+
logger.debug('skipping %s (all coefficients >= 0)',
|
|
1325
|
+
repr_pretty(coeffs, 0))
|
|
1326
|
+
continue
|
|
1327
|
+
constant = coeffs[0]
|
|
1328
|
+
ones = tuple(i+1 for i, c in enumerate(coeffs[1:]) if c == 1)
|
|
1329
|
+
mones = tuple(i+1 for i, c in enumerate(coeffs[1:]) if c == -1)
|
|
1330
|
+
absgetwo = tuple(i+1 for i, c in enumerate(coeffs[1:]) if abs(c) >= 2)
|
|
1331
|
+
if len(ones) == 1 and not mones and not absgetwo:
|
|
1332
|
+
if constant < 0:
|
|
1333
|
+
# This case could be cleverly skipped...
|
|
1334
|
+
inequalities_filtered.append(coeffs)
|
|
1335
|
+
elif len(ones) == 1 and len(mones) == 1 and not absgetwo and constant <= 0:
|
|
1336
|
+
logger.debug('handling %s',
|
|
1337
|
+
repr_pretty(coeffs, 0))
|
|
1338
|
+
chain_links[(mones[0], ones[0])] = constant
|
|
1339
|
+
else:
|
|
1340
|
+
inequalities_filtered.append(coeffs)
|
|
1341
|
+
|
|
1342
|
+
G = DiGraph(chain_links, format='list_of_edges')
|
|
1343
|
+
potential = {}
|
|
1344
|
+
paths = {}
|
|
1345
|
+
D = {}
|
|
1346
|
+
inequalities_extra = []
|
|
1347
|
+
for i in range(dim):
|
|
1348
|
+
D[(i, i)] = 1
|
|
1349
|
+
for v in G.topological_sort():
|
|
1350
|
+
NP = iter(sorted(((n, potential[n] + chain_links[(n, v)])
|
|
1351
|
+
for n in G.neighbor_in_iterator(v)),
|
|
1352
|
+
key=lambda k: (k[1], k[0])))
|
|
1353
|
+
n, p = next(NP, (None, 0))
|
|
1354
|
+
potential[v] = p
|
|
1355
|
+
D[(0, v)] = -p
|
|
1356
|
+
paths[v] = paths.get(n, ()) + (v,)
|
|
1357
|
+
for u in paths[v]:
|
|
1358
|
+
D[(u, v)] = 1
|
|
1359
|
+
|
|
1360
|
+
for n, p in NP:
|
|
1361
|
+
ell = len(tuple(takewhile(lambda u: u[0] == u[1],
|
|
1362
|
+
zip(paths[n], paths[v]))))
|
|
1363
|
+
coeffs = dim*[0]
|
|
1364
|
+
for u in paths[v][ell:]:
|
|
1365
|
+
coeffs[u] = 1
|
|
1366
|
+
for u in paths[n][ell:]:
|
|
1367
|
+
coeffs[u] = -1
|
|
1368
|
+
coeffs[0] = p - potential[v]
|
|
1369
|
+
inequalities_extra.append(tuple(coeffs))
|
|
1370
|
+
T = matrix(ZZ, dim, dim, D)
|
|
1371
|
+
|
|
1372
|
+
self.inequalities = (list(tuple(T*vector(ieq))
|
|
1373
|
+
for ieq in inequalities_filtered)
|
|
1374
|
+
+ inequalities_extra)
|
|
1375
|
+
|
|
1376
|
+
rules_pre = ((y, B({tuple(row[1:]): 1}))
|
|
1377
|
+
for y, row in zip((1,) + B.gens(), T.rows()))
|
|
1378
|
+
self.factor = next(rules_pre)[1]
|
|
1379
|
+
self.rules = dict(rules_pre)
|
|
1380
|
+
|
|
1381
|
+
|
|
1382
|
+
class _EliminateByEquations(_TransformHrepresentation):
|
|
1383
|
+
r"""
|
|
1384
|
+
Prepare the substitutions coming from "eliminated" variables
|
|
1385
|
+
in the given equations.
|
|
1386
|
+
|
|
1387
|
+
INPUT:
|
|
1388
|
+
|
|
1389
|
+
- ``inequalities`` -- list of tuples of numbers
|
|
1390
|
+
|
|
1391
|
+
- ``equations`` -- list of tuples of numbers
|
|
1392
|
+
|
|
1393
|
+
- ``B`` -- a Laurent polynomial ring
|
|
1394
|
+
|
|
1395
|
+
ATTRIBUTES:
|
|
1396
|
+
|
|
1397
|
+
- ``inequalities``, ``equations`` -- list of tuples
|
|
1398
|
+
|
|
1399
|
+
Determine the generating function of these inequalities
|
|
1400
|
+
and equations instead of the input.
|
|
1401
|
+
|
|
1402
|
+
- ``factor`` -- a Laurent polynomial
|
|
1403
|
+
|
|
1404
|
+
The numerator of the generating function has to be multiplied
|
|
1405
|
+
with ``factor`` *after* substituting ``rules``.
|
|
1406
|
+
|
|
1407
|
+
- ``rules`` -- dictionary mapping Laurent polynomial variables to
|
|
1408
|
+
Laurent polynomials
|
|
1409
|
+
|
|
1410
|
+
Substitute ``rules`` into the generating function.
|
|
1411
|
+
|
|
1412
|
+
- ``indices`` -- a sorted tuple of integers representing
|
|
1413
|
+
indices of Laurent polynomial ring variables
|
|
1414
|
+
|
|
1415
|
+
These are exactly the "eliminated" variables which we take care of
|
|
1416
|
+
by ``factor`` and ``rules``.
|
|
1417
|
+
|
|
1418
|
+
The generating function of the input ``inequalities`` and
|
|
1419
|
+
``equations`` is equal to the generating function of the
|
|
1420
|
+
attributes ``inequalities`` and ``equations`` in which ``rules``
|
|
1421
|
+
were substitited and ``factor`` was multiplied (via
|
|
1422
|
+
:meth:`~_TransformHrepresentation.apply_rules`).
|
|
1423
|
+
|
|
1424
|
+
EXAMPLES::
|
|
1425
|
+
|
|
1426
|
+
sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations
|
|
1427
|
+
|
|
1428
|
+
sage: def prepare_equations(equations, B):
|
|
1429
|
+
....: T = _EliminateByEquations([], equations, B)
|
|
1430
|
+
....: return T.factor, T.rules, T.indices
|
|
1431
|
+
|
|
1432
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 4)
|
|
1433
|
+
sage: prepare_equations([(1, 1, 1, -1, 0)], B)
|
|
1434
|
+
(y2, {y1: y1*y2, y0: y0*y2}, (2,))
|
|
1435
|
+
sage: prepare_equations([(0, 1, 0, -1, 0)], B)
|
|
1436
|
+
(1, {y0: y0*y2}, (2,))
|
|
1437
|
+
sage: prepare_equations([(-1, 0, 1, -1, -1), (1, 1, 0, 1, 2)], B)
|
|
1438
|
+
(y2^-1, {y1: y1*y2^2*y3^-1, y0: y0*y2*y3^-1}, (2, 3))
|
|
1439
|
+
|
|
1440
|
+
TESTS::
|
|
1441
|
+
|
|
1442
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 4)
|
|
1443
|
+
sage: prepare_equations([(0, 0, 1, 0, -1), (-1, 1, -1, -1, 0)], B)
|
|
1444
|
+
(y2^-1, {y1: y1*y2^-1*y3, y0: y0*y2}, (2, 3))
|
|
1445
|
+
|
|
1446
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 5)
|
|
1447
|
+
sage: prepare_equations([(0, 0, 0, 1, 0, -1), (0, 1, 0, 0, -1, 0),
|
|
1448
|
+
....: (0, 1, -1, 0, 0, 0)], B)
|
|
1449
|
+
(1, {y2: y2*y4, y0: y0*y1*y3}, (1, 3, 4))
|
|
1450
|
+
"""
|
|
1451
|
+
|
|
1452
|
+
def _transform_(self):
|
|
1453
|
+
r"""
|
|
1454
|
+
Do the actual transformation.
|
|
1455
|
+
|
|
1456
|
+
This is called during initialization.
|
|
1457
|
+
See :class:`_EliminateByEquations` for details.
|
|
1458
|
+
|
|
1459
|
+
TESTS::
|
|
1460
|
+
|
|
1461
|
+
sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations
|
|
1462
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
|
|
1463
|
+
sage: T = _EliminateByEquations([], [], B) # indirect doctest
|
|
1464
|
+
"""
|
|
1465
|
+
from sage.matrix.constructor import matrix
|
|
1466
|
+
from sage.misc.misc_c import prod
|
|
1467
|
+
|
|
1468
|
+
equations = self.equations
|
|
1469
|
+
B = self.B
|
|
1470
|
+
|
|
1471
|
+
E = matrix(equations)
|
|
1472
|
+
if not E:
|
|
1473
|
+
self.factor = 1
|
|
1474
|
+
self.rules = {}
|
|
1475
|
+
self.indices = ()
|
|
1476
|
+
return
|
|
1477
|
+
|
|
1478
|
+
TE, indices, indicesn = _EliminateByEquations.prepare_equations_transformation(E)
|
|
1479
|
+
|
|
1480
|
+
gens = (1,) + B.gens()
|
|
1481
|
+
z = tuple(gens[i] for i in indices)
|
|
1482
|
+
gens_cols = tuple(zip(gens, TE.columns()))
|
|
1483
|
+
rules_pre = ((y, y * prod(zz**(-c) for zz, c in zip(z, col)))
|
|
1484
|
+
for y, col in (gens_cols[i] for i in indicesn))
|
|
1485
|
+
self.factor = next(rules_pre)[1]
|
|
1486
|
+
self.rules = dict(rules_pre)
|
|
1487
|
+
self.indices = tuple(i-1 for i in indices)
|
|
1488
|
+
self.equations = []
|
|
1489
|
+
|
|
1490
|
+
@staticmethod
|
|
1491
|
+
def prepare_equations_transformation(E):
|
|
1492
|
+
r"""
|
|
1493
|
+
Return a transformation matrix and indices which variables
|
|
1494
|
+
in the equation to "eliminate" and deal with later.
|
|
1495
|
+
|
|
1496
|
+
INPUT:
|
|
1497
|
+
|
|
1498
|
+
- ``E`` -- a matrix whose rows represent equations
|
|
1499
|
+
|
|
1500
|
+
OUTPUT:
|
|
1501
|
+
|
|
1502
|
+
A triple ``(TE, indices, indicesn)`` with the following properties:
|
|
1503
|
+
|
|
1504
|
+
- ``TE`` -- a matrix
|
|
1505
|
+
|
|
1506
|
+
This matrix arises from ``E`` by multiplying a transformation matrix
|
|
1507
|
+
on the left.
|
|
1508
|
+
|
|
1509
|
+
- ``indices`` -- a sorted tuple of integers representing column indices
|
|
1510
|
+
|
|
1511
|
+
The the sub-matrix of ``TE`` with columns ``indices``
|
|
1512
|
+
is the identity matrix.
|
|
1513
|
+
|
|
1514
|
+
- ``indicesn`` -- a sorted tuple of integers representing column indices
|
|
1515
|
+
|
|
1516
|
+
``indicesn`` contains ``0`` and all indices of the columns of ``E``
|
|
1517
|
+
which are nonzero.
|
|
1518
|
+
|
|
1519
|
+
TESTS::
|
|
1520
|
+
|
|
1521
|
+
sage: from sage.geometry.polyhedron.generating_function import _EliminateByEquations
|
|
1522
|
+
|
|
1523
|
+
sage: _EliminateByEquations.prepare_equations_transformation(matrix([(0, 1, 0, -2)]))
|
|
1524
|
+
([ 0 -1/2 0 1], (3,), (0, 1))
|
|
1525
|
+
sage: _EliminateByEquations.prepare_equations_transformation(matrix([(0, 1, -2, 0), (0, 2, 0, -3)]))
|
|
1526
|
+
(
|
|
1527
|
+
[ 0 -1/2 1 0]
|
|
1528
|
+
[ 0 -2/3 0 1], (2, 3), (0, 1)
|
|
1529
|
+
)
|
|
1530
|
+
"""
|
|
1531
|
+
indices_nonzero = tuple(i for i, col in enumerate(E.columns())
|
|
1532
|
+
if i > 0 and not col.is_zero())
|
|
1533
|
+
indices = []
|
|
1534
|
+
r = 0
|
|
1535
|
+
for i in reversed(indices_nonzero):
|
|
1536
|
+
indices.append(i)
|
|
1537
|
+
r1 = E.matrix_from_columns(indices).rank()
|
|
1538
|
+
if r1 > r:
|
|
1539
|
+
r = r1
|
|
1540
|
+
if len(indices) >= E.nrows():
|
|
1541
|
+
break
|
|
1542
|
+
else:
|
|
1543
|
+
indices = indices[:-1]
|
|
1544
|
+
assert len(indices) == E.nrows()
|
|
1545
|
+
indices = tuple(reversed(indices))
|
|
1546
|
+
indicesn = (0,) + tuple(i for i in indices_nonzero if i not in indices)
|
|
1547
|
+
TE = E.matrix_from_columns(indices).inverse() * E
|
|
1548
|
+
return TE, indices, indicesn
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
class _TransformMod(_TransformHrepresentation):
|
|
1552
|
+
r"""
|
|
1553
|
+
Prepare the substitutions coming from the moduli.
|
|
1554
|
+
|
|
1555
|
+
INPUT:
|
|
1556
|
+
|
|
1557
|
+
- ``inequalities`` -- list of tuples of numbers
|
|
1558
|
+
|
|
1559
|
+
- ``equations`` -- list of tuples of numbers
|
|
1560
|
+
|
|
1561
|
+
- ``B`` -- a Laurent polynomial ring
|
|
1562
|
+
|
|
1563
|
+
- ``mod`` -- dictionary mapping an index ``i`` to ``(m, r)``
|
|
1564
|
+
|
|
1565
|
+
This is one entry of the output tuple of :meth:`generate_mods`.
|
|
1566
|
+
|
|
1567
|
+
ATTRIBUTES:
|
|
1568
|
+
|
|
1569
|
+
- ``inequalities``, ``equations`` -- list of tuples
|
|
1570
|
+
|
|
1571
|
+
Determine the generating function of these inequalities
|
|
1572
|
+
and equations instead of the input.
|
|
1573
|
+
|
|
1574
|
+
- ``factor`` -- a Laurent polynomial
|
|
1575
|
+
|
|
1576
|
+
The numerator of the generating function has to be multiplied
|
|
1577
|
+
with ``factor`` *after* substituting ``rules``.
|
|
1578
|
+
|
|
1579
|
+
- ``rules`` -- dictionary mapping Laurent polynomial variables to
|
|
1580
|
+
Laurent polynomials
|
|
1581
|
+
|
|
1582
|
+
Substitute ``rules`` into the generating function.
|
|
1583
|
+
|
|
1584
|
+
The generating function of the input ``inequalities`` and
|
|
1585
|
+
``equations`` is equal to the generating function of the
|
|
1586
|
+
attributes ``inequalities`` and ``equations`` in which ``rules``
|
|
1587
|
+
were substitited and ``factor`` was multiplied (via
|
|
1588
|
+
:meth:`~_TransformHrepresentation.apply_rules`).
|
|
1589
|
+
|
|
1590
|
+
EXAMPLES::
|
|
1591
|
+
|
|
1592
|
+
sage: from sage.geometry.polyhedron.generating_function import _TransformMod
|
|
1593
|
+
|
|
1594
|
+
sage: def prepare_mod(mod, B, *vecs):
|
|
1595
|
+
....: inequalities, equations = vecs
|
|
1596
|
+
....: T = _TransformMod(inequalities, equations, B, mod)
|
|
1597
|
+
....: return T.factor, T.rules, T.inequalities, T.equations
|
|
1598
|
+
|
|
1599
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 3)
|
|
1600
|
+
sage: prepare_mod({0: (2, 1)}, B, [(1, -1, 0, 2)], [])
|
|
1601
|
+
(y0, {y2: y2, y1: y1, y0: y0^2}, [(0, -2, 0, 2)], [])
|
|
1602
|
+
sage: prepare_mod({0: (2, 1), 1: (2, 1)}, B,
|
|
1603
|
+
....: [(0, -1, -1, 2)], [(0, -1, 1, 0)])
|
|
1604
|
+
(y0*y1, {y2: y2, y1: y1^2, y0: y0^2},
|
|
1605
|
+
[(-2, -2, -2, 2)], [(0, -2, 2, 0)])
|
|
1606
|
+
"""
|
|
1607
|
+
|
|
1608
|
+
def __init__(self, inequalities, equations, B, mod):
|
|
1609
|
+
r"""
|
|
1610
|
+
See :class:`_TransformMod` for details.
|
|
1611
|
+
|
|
1612
|
+
TESTS::
|
|
1613
|
+
|
|
1614
|
+
sage: from sage.geometry.polyhedron.generating_function import _TransformMod
|
|
1615
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
|
|
1616
|
+
sage: _TransformMod([], [], B, {})
|
|
1617
|
+
<..._TransformMod object at 0x...>
|
|
1618
|
+
"""
|
|
1619
|
+
self.mod = mod
|
|
1620
|
+
super().__init__(inequalities, equations, B)
|
|
1621
|
+
|
|
1622
|
+
def _transform_(self):
|
|
1623
|
+
r"""
|
|
1624
|
+
Do the actual transformation.
|
|
1625
|
+
|
|
1626
|
+
This is called during initialization.
|
|
1627
|
+
See :class:`_TransformMod` for details.
|
|
1628
|
+
|
|
1629
|
+
TESTS::
|
|
1630
|
+
|
|
1631
|
+
sage: from sage.geometry.polyhedron.generating_function import _TransformMod
|
|
1632
|
+
sage: B = LaurentPolynomialRing(ZZ, 'y', 2)
|
|
1633
|
+
sage: T = _TransformMod([], [], B, {}) # indirect doctest
|
|
1634
|
+
"""
|
|
1635
|
+
from sage.matrix.constructor import matrix
|
|
1636
|
+
from sage.modules.free_module_element import vector
|
|
1637
|
+
from sage.rings.integer_ring import ZZ
|
|
1638
|
+
|
|
1639
|
+
mod = self.mod
|
|
1640
|
+
B = self.B
|
|
1641
|
+
|
|
1642
|
+
if not mod:
|
|
1643
|
+
self.factor = 1
|
|
1644
|
+
self.rules = {}
|
|
1645
|
+
|
|
1646
|
+
n = len(B.gens()) + 1
|
|
1647
|
+
|
|
1648
|
+
D = {(i, i): 1 for i in range(n)}
|
|
1649
|
+
for i, mr in mod.items():
|
|
1650
|
+
D[(i+1, i+1)] = mr[0]
|
|
1651
|
+
D[(i+1, 0)] = mr[1]
|
|
1652
|
+
T = matrix(ZZ, n, n, D)
|
|
1653
|
+
|
|
1654
|
+
rules_pre = ((y, B({tuple(row[1:]): 1}))
|
|
1655
|
+
for y, row in zip((1,) + B.gens(), T.columns()))
|
|
1656
|
+
self.factor = next(rules_pre)[1]
|
|
1657
|
+
self.rules = dict(rules_pre)
|
|
1658
|
+
|
|
1659
|
+
self.inequalities = list(tuple(vector(e)*T) for e in self.inequalities)
|
|
1660
|
+
self.equations = list(tuple(vector(e)*T) for e in self.equations)
|
|
1661
|
+
|
|
1662
|
+
@staticmethod
|
|
1663
|
+
def generate_mods(equations):
|
|
1664
|
+
r"""
|
|
1665
|
+
Extract the moduli and residue classes implied
|
|
1666
|
+
by the equations.
|
|
1667
|
+
|
|
1668
|
+
INPUT:
|
|
1669
|
+
|
|
1670
|
+
- ``equations`` -- list of tuples
|
|
1671
|
+
|
|
1672
|
+
OUTPUT:
|
|
1673
|
+
|
|
1674
|
+
A tuple where each entry represents one possible configuration.
|
|
1675
|
+
Each entry is a dictionary mapping ``i`` to ``(m, r)`` with the following
|
|
1676
|
+
meaning: The ``i``-th coordinate of each element of the polyhedron
|
|
1677
|
+
has to be congruent to ``r`` modulo ``m``.
|
|
1678
|
+
|
|
1679
|
+
TESTS::
|
|
1680
|
+
|
|
1681
|
+
sage: from sage.geometry.polyhedron.generating_function import _TransformMod
|
|
1682
|
+
sage: _TransformMod.generate_mods([(0, 1, 1, -2)])
|
|
1683
|
+
({0: (2, 0), 1: (2, 0)}, {0: (2, 1), 1: (2, 1)})
|
|
1684
|
+
"""
|
|
1685
|
+
from sage.arith.functions import lcm
|
|
1686
|
+
from sage.matrix.constructor import matrix
|
|
1687
|
+
from sage.rings.integer_ring import ZZ
|
|
1688
|
+
from sage.rings.rational_field import QQ
|
|
1689
|
+
|
|
1690
|
+
TE, TEi, TEin = _EliminateByEquations.prepare_equations_transformation(matrix(equations))
|
|
1691
|
+
TEin = TEin[1:]
|
|
1692
|
+
if TE.base_ring() == ZZ:
|
|
1693
|
+
mods = [{}]
|
|
1694
|
+
elif TE.base_ring() == QQ:
|
|
1695
|
+
m = lcm([e.denominator() for e in TE.list()])
|
|
1696
|
+
if m == 1:
|
|
1697
|
+
mods = [{}]
|
|
1698
|
+
else:
|
|
1699
|
+
cols = TE.columns()
|
|
1700
|
+
assert all(cols[j][i] == 1 for i, j in enumerate(TEi))
|
|
1701
|
+
pre_mods = _compositions_mod((tuple(ZZ(cc*m) for cc in cols[i])
|
|
1702
|
+
for i in TEin),
|
|
1703
|
+
m, r=(-cc*m for cc in cols[0]),
|
|
1704
|
+
multidimensional=True)
|
|
1705
|
+
mods = tuple({i-1: (aa.modulus(), ZZ(aa))
|
|
1706
|
+
for i, aa in zip(TEin, a) if aa.modulus() > 1}
|
|
1707
|
+
for a in pre_mods)
|
|
1708
|
+
else:
|
|
1709
|
+
raise TypeError('equations over ZZ or QQ expected, but got '
|
|
1710
|
+
'equations over {}.'.format(TE.base_ring()))
|
|
1711
|
+
|
|
1712
|
+
return mods
|
|
1713
|
+
|
|
1714
|
+
|
|
1715
|
+
def _compositions_mod(u, m, r=0, multidimensional=False):
|
|
1716
|
+
r"""
|
|
1717
|
+
Return an iterable of all tuples `a` such that `a u^T \equiv r \mod m`.
|
|
1718
|
+
|
|
1719
|
+
INPUT:
|
|
1720
|
+
|
|
1721
|
+
- ``m`` -- the modulus as a positive integer
|
|
1722
|
+
|
|
1723
|
+
- ``multidimensional`` -- boolean (default: ``False``)
|
|
1724
|
+
|
|
1725
|
+
If ``multidimensional=False``:
|
|
1726
|
+
|
|
1727
|
+
- ``u`` -- the coefficients as a tuple
|
|
1728
|
+
|
|
1729
|
+
- ``r`` -- (default: `0`)
|
|
1730
|
+
the remainder as a nonnegative integer
|
|
1731
|
+
|
|
1732
|
+
If ``multidimensional=True``:
|
|
1733
|
+
|
|
1734
|
+
- ``u`` -- the coefficients as a tuple of tuples (read column-wise)
|
|
1735
|
+
|
|
1736
|
+
- ``r`` -- (default: the zero vector)
|
|
1737
|
+
the remainder as a tuple of nonnegative integers
|
|
1738
|
+
|
|
1739
|
+
OUTPUT:
|
|
1740
|
+
|
|
1741
|
+
An iterable of tuples. All these tuples have the same size as ``u``.
|
|
1742
|
+
The elements of the tuples are integers modulo some appropriate
|
|
1743
|
+
modulus; this modulus divides `m` and is possibly different for each element.
|
|
1744
|
+
|
|
1745
|
+
EXAMPLES::
|
|
1746
|
+
|
|
1747
|
+
sage: from sage.geometry.polyhedron.generating_function import _compositions_mod
|
|
1748
|
+
sage: def show_cm(cm):
|
|
1749
|
+
....: print(', '.join('({})'.format(
|
|
1750
|
+
....: ', '.join('{}mod{}'.format(aa, aa.modulus())
|
|
1751
|
+
....: for aa in a))
|
|
1752
|
+
....: for a in cm))
|
|
1753
|
+
|
|
1754
|
+
sage: list(_compositions_mod([1, 1], 2))
|
|
1755
|
+
[(0, 0), (1, 1)]
|
|
1756
|
+
sage: show_cm(_compositions_mod([1, 1], 2))
|
|
1757
|
+
(0mod2, 0mod2), (1mod2, 1mod2)
|
|
1758
|
+
sage: show_cm(_compositions_mod([1, 2, 3], 6))
|
|
1759
|
+
(0mod6, 0mod3, 0mod2), (1mod6, 1mod3, 1mod2), (2mod6, 2mod3, 0mod2),
|
|
1760
|
+
(3mod6, 0mod3, 1mod2), (4mod6, 1mod3, 0mod2), (5mod6, 2mod3, 1mod2)
|
|
1761
|
+
sage: show_cm(_compositions_mod([2, 2, 2], 6))
|
|
1762
|
+
(0mod3, 0mod3, 0mod3), (0mod3, 1mod3, 2mod3), (0mod3, 2mod3, 1mod3),
|
|
1763
|
+
(1mod3, 0mod3, 2mod3), (1mod3, 1mod3, 1mod3), (1mod3, 2mod3, 0mod3),
|
|
1764
|
+
(2mod3, 0mod3, 1mod3), (2mod3, 1mod3, 0mod3), (2mod3, 2mod3, 2mod3)
|
|
1765
|
+
|
|
1766
|
+
::
|
|
1767
|
+
|
|
1768
|
+
sage: show_cm(_compositions_mod([(1, 0), (0, 1)], 2,
|
|
1769
|
+
....: multidimensional=True))
|
|
1770
|
+
(0mod2, 0mod2)
|
|
1771
|
+
sage: show_cm(_compositions_mod([(1, 2), (2, 2), (3, 2)], 6,
|
|
1772
|
+
....: multidimensional=True))
|
|
1773
|
+
(0mod6, 0mod3, 0mod6), (1mod6, 1mod3, 1mod6), (2mod6, 2mod3, 2mod6),
|
|
1774
|
+
(3mod6, 0mod3, 3mod6), (4mod6, 1mod3, 4mod6), (5mod6, 2mod3, 5mod6)
|
|
1775
|
+
|
|
1776
|
+
TESTS::
|
|
1777
|
+
|
|
1778
|
+
sage: show_cm(_compositions_mod([1, 0], 2))
|
|
1779
|
+
(0mod2, 0mod1)
|
|
1780
|
+
"""
|
|
1781
|
+
from sage.arith.functions import lcm
|
|
1782
|
+
from sage.arith.srange import srange
|
|
1783
|
+
from sage.modules.free_module_element import vector
|
|
1784
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
|
1785
|
+
|
|
1786
|
+
Z = Zmod(m)
|
|
1787
|
+
if not multidimensional:
|
|
1788
|
+
u = tuple(vector([Z(uu)]) for uu in u)
|
|
1789
|
+
r = vector([Z(r)])
|
|
1790
|
+
else:
|
|
1791
|
+
u = tuple(vector(Z(uuu) for uuu in uu) for uu in u)
|
|
1792
|
+
if r == 0:
|
|
1793
|
+
r = vector(Z(0) for _ in range(len(u[0])))
|
|
1794
|
+
else:
|
|
1795
|
+
r = vector(Z(rr) for rr in r)
|
|
1796
|
+
|
|
1797
|
+
def recursively_build_compositions(u, r):
|
|
1798
|
+
if not u:
|
|
1799
|
+
if all(rr == 0 for rr in r):
|
|
1800
|
+
yield ()
|
|
1801
|
+
return
|
|
1802
|
+
|
|
1803
|
+
v = u[0]
|
|
1804
|
+
m = lcm(vv.order() for vv in v)
|
|
1805
|
+
Z = Zmod(m)
|
|
1806
|
+
for j in srange(m):
|
|
1807
|
+
for a in recursively_build_compositions(u[1:], r - j*v):
|
|
1808
|
+
yield (Z(j),) + a
|
|
1809
|
+
|
|
1810
|
+
yield from recursively_build_compositions(u, r)
|