passagemath-polyhedra 10.6.31rc3__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.

Potentially problematic release.


This version of passagemath-polyhedra might be problematic. Click here for more details.

Files changed (208) hide show
  1. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
  2. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
  3. passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +208 -0
  4. passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +5 -0
  5. passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
  6. passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
  7. passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
  8. passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
  9. passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
  10. sage/all__sagemath_polyhedra.py +50 -0
  11. sage/game_theory/all.py +8 -0
  12. sage/game_theory/catalog.py +6 -0
  13. sage/game_theory/catalog_normal_form_games.py +923 -0
  14. sage/game_theory/cooperative_game.py +844 -0
  15. sage/game_theory/matching_game.py +1181 -0
  16. sage/game_theory/normal_form_game.py +2697 -0
  17. sage/game_theory/parser.py +275 -0
  18. sage/geometry/all__sagemath_polyhedra.py +22 -0
  19. sage/geometry/cone.py +6940 -0
  20. sage/geometry/cone_catalog.py +847 -0
  21. sage/geometry/cone_critical_angles.py +1027 -0
  22. sage/geometry/convex_set.py +1119 -0
  23. sage/geometry/fan.py +3743 -0
  24. sage/geometry/fan_isomorphism.py +389 -0
  25. sage/geometry/fan_morphism.py +1884 -0
  26. sage/geometry/hasse_diagram.py +202 -0
  27. sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
  28. sage/geometry/hyperplane_arrangement/all.py +1 -0
  29. sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
  30. sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
  31. sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
  32. sage/geometry/hyperplane_arrangement/library.py +825 -0
  33. sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
  34. sage/geometry/hyperplane_arrangement/plot.py +520 -0
  35. sage/geometry/integral_points.py +35 -0
  36. sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
  37. sage/geometry/integral_points_generic_dense.pyx +7 -0
  38. sage/geometry/lattice_polytope.py +5894 -0
  39. sage/geometry/linear_expression.py +773 -0
  40. sage/geometry/newton_polygon.py +767 -0
  41. sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
  42. sage/geometry/point_collection.pyx +1008 -0
  43. sage/geometry/polyhedral_complex.py +2616 -0
  44. sage/geometry/polyhedron/all.py +8 -0
  45. sage/geometry/polyhedron/backend_cdd.py +460 -0
  46. sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
  47. sage/geometry/polyhedron/backend_field.py +347 -0
  48. sage/geometry/polyhedron/backend_normaliz.py +2503 -0
  49. sage/geometry/polyhedron/backend_number_field.py +168 -0
  50. sage/geometry/polyhedron/backend_polymake.py +765 -0
  51. sage/geometry/polyhedron/backend_ppl.py +582 -0
  52. sage/geometry/polyhedron/base.py +1206 -0
  53. sage/geometry/polyhedron/base0.py +1444 -0
  54. sage/geometry/polyhedron/base1.py +886 -0
  55. sage/geometry/polyhedron/base2.py +812 -0
  56. sage/geometry/polyhedron/base3.py +1845 -0
  57. sage/geometry/polyhedron/base4.py +1262 -0
  58. sage/geometry/polyhedron/base5.py +2700 -0
  59. sage/geometry/polyhedron/base6.py +1741 -0
  60. sage/geometry/polyhedron/base7.py +997 -0
  61. sage/geometry/polyhedron/base_QQ.py +1258 -0
  62. sage/geometry/polyhedron/base_RDF.py +98 -0
  63. sage/geometry/polyhedron/base_ZZ.py +934 -0
  64. sage/geometry/polyhedron/base_mutable.py +215 -0
  65. sage/geometry/polyhedron/base_number_field.py +122 -0
  66. sage/geometry/polyhedron/cdd_file_format.py +155 -0
  67. sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
  68. sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
  69. sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
  70. sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
  71. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
  72. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
  73. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
  74. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
  75. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
  76. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
  77. sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
  78. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
  79. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
  80. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
  81. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
  82. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
  83. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
  84. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
  85. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
  86. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
  87. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
  88. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
  89. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
  90. sage/geometry/polyhedron/constructor.py +773 -0
  91. sage/geometry/polyhedron/double_description.py +753 -0
  92. sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
  93. sage/geometry/polyhedron/face.py +1060 -0
  94. sage/geometry/polyhedron/generating_function.py +1810 -0
  95. sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
  96. sage/geometry/polyhedron/library.py +3502 -0
  97. sage/geometry/polyhedron/misc.py +121 -0
  98. sage/geometry/polyhedron/modules/all.py +1 -0
  99. sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
  100. sage/geometry/polyhedron/palp_database.py +447 -0
  101. sage/geometry/polyhedron/parent.py +1279 -0
  102. sage/geometry/polyhedron/plot.py +1986 -0
  103. sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
  104. sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
  105. sage/geometry/polyhedron/representation.py +1723 -0
  106. sage/geometry/pseudolines.py +515 -0
  107. sage/geometry/relative_interior.py +445 -0
  108. sage/geometry/toric_plotter.py +1103 -0
  109. sage/geometry/triangulation/all.py +2 -0
  110. sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
  111. sage/geometry/triangulation/base.pyx +963 -0
  112. sage/geometry/triangulation/data.h +147 -0
  113. sage/geometry/triangulation/data.pxd +4 -0
  114. sage/geometry/triangulation/element.py +914 -0
  115. sage/geometry/triangulation/functions.h +10 -0
  116. sage/geometry/triangulation/functions.pxd +4 -0
  117. sage/geometry/triangulation/point_configuration.py +2256 -0
  118. sage/geometry/triangulation/triangulations.h +49 -0
  119. sage/geometry/triangulation/triangulations.pxd +7 -0
  120. sage/geometry/voronoi_diagram.py +319 -0
  121. sage/interfaces/all__sagemath_polyhedra.py +1 -0
  122. sage/interfaces/polymake.py +2028 -0
  123. sage/numerical/all.py +13 -0
  124. sage/numerical/all__sagemath_polyhedra.py +11 -0
  125. sage/numerical/backends/all.py +1 -0
  126. sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
  127. sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
  128. sage/numerical/backends/cvxopt_backend.pyx +1006 -0
  129. sage/numerical/backends/cvxopt_backend_test.py +19 -0
  130. sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
  131. sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
  132. sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
  133. sage/numerical/backends/cvxpy_backend.pxd +41 -0
  134. sage/numerical/backends/cvxpy_backend.pyx +934 -0
  135. sage/numerical/backends/cvxpy_backend_test.py +13 -0
  136. sage/numerical/backends/generic_backend_test.py +24 -0
  137. sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
  138. sage/numerical/backends/interactivelp_backend.pxd +36 -0
  139. sage/numerical/backends/interactivelp_backend.pyx +1231 -0
  140. sage/numerical/backends/interactivelp_backend_test.py +12 -0
  141. sage/numerical/backends/logging_backend.py +391 -0
  142. sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
  143. sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
  144. sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
  145. sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
  146. sage/numerical/backends/ppl_backend.pyx +1126 -0
  147. sage/numerical/backends/ppl_backend_test.py +13 -0
  148. sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
  149. sage/numerical/backends/scip_backend.pxd +22 -0
  150. sage/numerical/backends/scip_backend.pyx +1289 -0
  151. sage/numerical/backends/scip_backend_test.py +13 -0
  152. sage/numerical/interactive_simplex_method.py +5338 -0
  153. sage/numerical/knapsack.py +665 -0
  154. sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
  155. sage/numerical/linear_functions.pxd +31 -0
  156. sage/numerical/linear_functions.pyx +1648 -0
  157. sage/numerical/linear_tensor.py +470 -0
  158. sage/numerical/linear_tensor_constraints.py +448 -0
  159. sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
  160. sage/numerical/linear_tensor_element.pxd +6 -0
  161. sage/numerical/linear_tensor_element.pyx +459 -0
  162. sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
  163. sage/numerical/mip.pxd +40 -0
  164. sage/numerical/mip.pyx +3667 -0
  165. sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
  166. sage/numerical/sdp.pxd +39 -0
  167. sage/numerical/sdp.pyx +1433 -0
  168. sage/rings/all__sagemath_polyhedra.py +3 -0
  169. sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
  170. sage/rings/polynomial/omega.py +982 -0
  171. sage/schemes/all__sagemath_polyhedra.py +2 -0
  172. sage/schemes/toric/all.py +10 -0
  173. sage/schemes/toric/chow_group.py +1248 -0
  174. sage/schemes/toric/divisor.py +2082 -0
  175. sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
  176. sage/schemes/toric/divisor_class.pyx +322 -0
  177. sage/schemes/toric/fano_variety.py +1606 -0
  178. sage/schemes/toric/homset.py +650 -0
  179. sage/schemes/toric/ideal.py +451 -0
  180. sage/schemes/toric/library.py +1322 -0
  181. sage/schemes/toric/morphism.py +1958 -0
  182. sage/schemes/toric/points.py +1032 -0
  183. sage/schemes/toric/sheaf/all.py +1 -0
  184. sage/schemes/toric/sheaf/constructor.py +302 -0
  185. sage/schemes/toric/sheaf/klyachko.py +921 -0
  186. sage/schemes/toric/toric_subscheme.py +905 -0
  187. sage/schemes/toric/variety.py +3460 -0
  188. sage/schemes/toric/weierstrass.py +1078 -0
  189. sage/schemes/toric/weierstrass_covering.py +457 -0
  190. sage/schemes/toric/weierstrass_higher.py +288 -0
  191. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
  192. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
  193. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
  194. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
  195. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
  196. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
  197. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
  198. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
  199. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
  200. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
  201. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
  202. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
  203. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
  204. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
  205. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
  206. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
  207. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
  208. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
@@ -0,0 +1,1027 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ # sage.doctest: needs sage.rings.number_field
3
+ r"""
4
+ Find maximal angles between polyhedral convex cones
5
+
6
+ .. WARNING::
7
+
8
+ This module is considered internal and its contents are subject to
9
+ change at any time without (deprecation) warning. The stable
10
+ interface is
11
+ :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle`.
12
+
13
+ Finding the maximal (or equivalently, the minimal) angle between two
14
+ polyhedral convex cones is a hard nonconvex optimization problem. The
15
+ problem for a single cone was introduced in [IS2005]_, and was later
16
+ extended in [SS2016]_ to two cones as a generalization of the
17
+ principal angle between two vector subspaces.
18
+
19
+ Seeger and Sossa proposed an algorithm in [SS2016]_ to find maximal
20
+ angles, and [Or2020]_ elaborates on that algorithm. It is this latest
21
+ improvement that is implemented (more or less) by this module. The
22
+ fact that perturbations of pointed cones may not change the answer too
23
+ much [Or2024]_ is taken into consideration to avoid pathological
24
+ cases.
25
+
26
+ This module is internal to SageMath; the interface presented to users
27
+ consists of a public method,
28
+ :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle` for
29
+ polyhedral convex cones. Even though all of the functions in this
30
+ module are internal, some are more internal than others. There are a
31
+ few functions that are used only in doctests, and not by any code that
32
+ an end-user would run. Breaking somewhat with tradition, only those
33
+ methods have been prefixed with an underscore.
34
+ """
35
+
36
+ from sage.functions.trig import arccos
37
+ from sage.matrix.constructor import matrix
38
+ from sage.misc.lazy_import import lazy_import
39
+ from sage.rings.integer_ring import ZZ
40
+ from sage.rings.qqbar import AA
41
+ from sage.rings.rational_field import QQ
42
+ from sage.rings.real_double import RDF
43
+
44
+ lazy_import('sage.symbolic.constants', 'pi')
45
+
46
+
47
+ def _normalize_gevp_solution(gevp_solution):
48
+ r"""
49
+ Normalize the results of :func:`solve_gevp_nonzero` and
50
+ :func:`_solve_gevp_naive`.
51
+
52
+ Those two functions return solutions (pairs of vectors) to an
53
+ eigenvalue problem, but eigenvectors are only unique up to a
54
+ scalar multiple. This function normalizes those results so that
55
+ every eigenvector has a leading entry of positive one. This allows
56
+ us to identity equivalent solutions to those problems.
57
+
58
+ INPUT:
59
+
60
+ A quartet ``gevp_solution`` whose components are, in order:
61
+
62
+ - ``eigenvalue`` -- ignored
63
+
64
+ - ``xi`` -- first component of the `( \xi, \eta )` eigenvector
65
+
66
+ - ``eta`` -- second component of the `( \xi, \eta )` eigenvector
67
+
68
+ - ``multiplicity`` -- ignored
69
+
70
+ OUTPUT:
71
+
72
+ If `c` is the first nonzero component of the concatenated `( \xi,
73
+ \eta )` vector, then a quartet whose components are, in order:
74
+
75
+ - ``eigenvalue`` -- the unmodified ``eigenvalue`` argument
76
+
77
+ - ``xi*(1/c)`` -- the `\xi` component normalized so that the first
78
+ nonzero component of the concatenated vector
79
+ `( \xi, \eta )` is positive one
80
+
81
+ - ``eta*(1/c)`` -- the `\eta` component normalized so that the
82
+ first nonzero component of the concatenated vector
83
+ `( \xi, \eta )` is positive one
84
+
85
+ - ``multiplicity`` -- the unmodified ``multiplicity`` argument
86
+
87
+ If there is no such `c` (that is, if both `\xi` and `\eta` are
88
+ zero), then the entire input quartet is returned unmodified.
89
+
90
+ EXAMPLES::
91
+
92
+ sage: from sage.geometry.cone_critical_angles import (
93
+ ....: _normalize_gevp_solution)
94
+ sage: s1 = (-1, vector(QQ,[0,-2]), vector(QQ,[1]), 1)
95
+ sage: _normalize_gevp_solution(s1)
96
+ (-1, (0, 1), (-1/2), 1)
97
+ sage: s2 = (1, vector(QQ,[0,0]), vector(QQ,[0,0,-1]), 2)
98
+ sage: _normalize_gevp_solution(s2)
99
+ (1, (0, 0), (0, 0, 1), 2)
100
+ """
101
+ eigenvalue, xi, eta, multiplicity = gevp_solution
102
+ from itertools import chain
103
+
104
+ # We'll use this default of zero as the scaling factor if we don't
105
+ # find a better one; that is, if xi = eta = 0 -- in which case the
106
+ # additional multiplication by zero is a no-op.
107
+ scale = 0
108
+ for c in chain(xi, eta):
109
+ if c != 0:
110
+ scale = ~c
111
+ break
112
+
113
+ xi *= scale
114
+ xi.set_immutable()
115
+ eta *= scale
116
+ eta.set_immutable()
117
+ return (eigenvalue, xi, eta, multiplicity)
118
+
119
+
120
+ def _random_admissible_cone(ambient_dim):
121
+ r"""
122
+ Generate a random cone in a lattice of dimension
123
+ ``ambient_dim`` that isn't trivial.
124
+
125
+ This is a convenience method used to simplify some test cases. The
126
+ number of rays that the cone possesses is limited to two more than
127
+ ``ambient_dim``; so, for example, you will not get more than five
128
+ rays in a three-dimensional space. This limits the amount of time
129
+ spent in any one test case. In contrast with the definition in
130
+ [Or2020]_ we consider the full ambient space to be admissible (it
131
+ doesn't hurt anything, and [Or2024]_ was forced to allow it).
132
+
133
+ INPUT:
134
+
135
+ - ``ambient_dim`` -- positive integer representing the dimension
136
+ of the ambient lattice in which the returned cone lives
137
+
138
+ OUTPUT:
139
+
140
+ A "random" nontrivial closed convex cone in a lattice of dimension
141
+ ``ambient_dim``.
142
+
143
+ A :exc:`ValueError` is raised if ``ambient_dim`` is not
144
+ positive.
145
+
146
+ EXAMPLES:
147
+
148
+ The result has all of the desired properties::
149
+
150
+ sage: from sage.geometry.cone_critical_angles import (
151
+ ....: _random_admissible_cone )
152
+ sage: K = _random_admissible_cone(5)
153
+ sage: K.lattice_dim()
154
+ 5
155
+ sage: K.is_trivial()
156
+ False
157
+
158
+ Unless the ``ambient_dim`` argument is nonsense::
159
+
160
+ sage: from sage.geometry.cone_critical_angles import (
161
+ ....: _random_admissible_cone )
162
+ sage: K = _random_admissible_cone(0)
163
+ Traceback (most recent call last):
164
+ ...
165
+ ValueError: there are no nontrivial cones in dimension 0
166
+ """
167
+ if ambient_dim < 1 or ambient_dim not in ZZ:
168
+ # The random_cone() method already crashes if we ask the
169
+ # impossible of it, but having this here emits a more sensible
170
+ # error message.
171
+ raise ValueError("there are no nontrivial cones in dimension %d"
172
+ % ambient_dim)
173
+
174
+ args = { 'min_ambient_dim': ambient_dim,
175
+ 'max_ambient_dim': ambient_dim,
176
+ 'min_rays': 1,
177
+ 'max_rays': ambient_dim+2 }
178
+
179
+ from sage.geometry.cone import random_cone
180
+ return random_cone(**args)
181
+
182
+ return K
183
+
184
+
185
+ def gevp_licis(G):
186
+ r"""
187
+ Return all nonempty subsets of indices for the columns of
188
+ ``G`` that correspond to linearly independent sets (of columns of
189
+ ``G``).
190
+
191
+ Mnemonic: linearly independent column-index subsets (LICIS).
192
+
193
+ The returned lists are all sorted in the same (the natural) order;
194
+ and are returned as lists so that they may be used to index into
195
+ the rows/columns of matrices.
196
+
197
+ INPUT:
198
+
199
+ - ``G`` -- the matrix whose linearly independent column index sets
200
+ we want
201
+
202
+ OUTPUT:
203
+
204
+ A generator that returns sorted lists of natural numbers. Each
205
+ generated list ``I`` is a set of indices corresponding to columns
206
+ of ``G`` that, when considered as a set, is linearly independent.
207
+
208
+ EXAMPLES:
209
+
210
+ The linearly independent subsets of the matrix corresponding to a
211
+ line (with two generators pointing in opposite directions) are the
212
+ one-element subsets, since the only two-element subset isn't
213
+ linearly independent::
214
+
215
+ sage: from sage.geometry.cone_critical_angles import gevp_licis
216
+ sage: K = Cone([(1,0),(-1,0)])
217
+ sage: G = matrix.column(K.rays())
218
+ sage: list(gevp_licis(G))
219
+ [[0], [1]]
220
+
221
+ The matrix for the trivial cone has no linearly independent
222
+ subsets, since we require them to be nonempty::
223
+
224
+ sage: from sage.geometry.cone_critical_angles import gevp_licis
225
+ sage: trivial_cone = cones.trivial(0)
226
+ sage: trivial_cone.is_trivial()
227
+ True
228
+ sage: list(gevp_licis(matrix.column(trivial_cone.rays())))
229
+ []
230
+
231
+ All rays in the nonnegative orthant of `R^{n}` are
232
+ linearly independent, so we should get back `2^{n} - 1` subsets
233
+ after accounting for the absence of the empty set::
234
+
235
+ sage: from sage.geometry.cone_critical_angles import gevp_licis
236
+ sage: K = cones.nonnegative_orthant(3)
237
+ sage: G = matrix.column(K.rays())
238
+ sage: len(list(gevp_licis(G))) == 2^(K.nrays()) - 1
239
+ True
240
+
241
+ TESTS:
242
+
243
+ All sets corresponding to the returned indices should be linearly
244
+ independent::
245
+
246
+ sage: from sage.geometry.cone_critical_angles import gevp_licis
247
+ sage: K = random_cone(max_rays=8)
248
+ sage: G = matrix.column(K.rays())
249
+ sage: all( len(s) == K.rays(s).dimension() for s in gevp_licis(G) )
250
+ True
251
+ """
252
+ from sage.matroids.linear_matroid import LinearMatroid
253
+
254
+ # There's a fast implementation of this for matroids, but we need
255
+ # to drop the empty set from its output and convert the rest to
256
+ # lists that are all sorted in the same order.
257
+ return map(sorted, filter(bool, LinearMatroid(G).independent_sets()))
258
+
259
+
260
+ def _solve_gevp_naive(GG, HH, M, I, J):
261
+ r"""
262
+ Solve the generalized eigenvalue problem in Theorem 3
263
+ [Or2020]_ in a very naive way, by (slowly) inverting the matrices
264
+ and finding the eigenvalues in the product space.
265
+
266
+ This is used only for testing, to ensure that the smart way of
267
+ solving the generalized eigenvalue problem via
268
+ :func:`solve_gevp_zero` and :func:`solve_gevp_nonzero` returns the
269
+ same answers as the dumb way.
270
+
271
+ This returns a generator, like :func:`solve_gevp_zero` and
272
+ :func:`solve_gevp_nonzero`.
273
+
274
+ INPUT:
275
+
276
+ See the arguments for :func:`solve_gevp_nonzero`.
277
+
278
+ ALGORITHM:
279
+
280
+ We construct the two matrices `A` and `B` in Theorem 3 [Or2020]_
281
+ in block form, and then use the naive "inverse" method on `B` to
282
+ move it to the left and obtain `M = B^{-1}A`. We then compute the
283
+ right eigenvectors of the whole big matrix `M`.
284
+
285
+ EXAMPLES:
286
+
287
+ A simple usage example, that also appears as Example 3 in
288
+ [Or2020]_::
289
+
290
+ sage: from sage.geometry.cone_critical_angles import _solve_gevp_naive
291
+ sage: K = cones.nonnegative_orthant(2)
292
+ sage: G = matrix.column(K.rays())
293
+ sage: GG = G.transpose() * G
294
+ sage: I = [0]
295
+ sage: J = [1]
296
+ sage: list(_solve_gevp_naive(GG,GG,GG,I,J))
297
+ [(0, (1), (0), 2), (0, (0), (1), 2)]
298
+
299
+ Check Example 4 [Or2020]_ symbolically to ensure that we get
300
+ eigenspaces of dimension `n=2` corresponding to the eigenvalues
301
+ `\cos\theta = -1` and `\cos\theta = 1`::
302
+
303
+ sage: # needs sage.symbolic
304
+ sage: from sage.geometry.cone_critical_angles import _solve_gevp_naive
305
+ sage: g11,g12,g21,g22 = SR.var('g11,g12,g21,g22', domain='real')
306
+ sage: h11,h12,h21,h22 = SR.var('h11,h12,h21,h22', domain='real')
307
+ sage: gs = [[g11,g12], [g21,g22]]
308
+ sage: hs = [[h11,h12], [h21,h22]]
309
+ sage: G = matrix.column(gs)
310
+ sage: H = matrix.column(hs)
311
+ sage: GG = G.transpose() * G
312
+ sage: HH = H.transpose() * H
313
+ sage: M = G.transpose() * H
314
+ sage: I = [0, 1]
315
+ sage: J = [0, 1]
316
+ sage: all( v in [-1,1] and m == 2
317
+ ....: for (v,_,_,m) in _solve_gevp_naive(GG,HH,M,I,J) )
318
+ True
319
+ """
320
+ A = matrix.block([
321
+ [ZZ.zero(), M[I,J]],
322
+ [M.transpose()[J,I], ZZ.zero()]
323
+ ])
324
+ B = matrix.block([
325
+ [GG[I,I], ZZ.zero()],
326
+ [ZZ.zero(), HH[J,J]]
327
+ ])
328
+ M = B.inverse() * A
329
+
330
+ # We'll format the result to match the solve_gevp_nonzero() return value.
331
+ for (evalue, evectors, multiplicity) in M.eigenvectors_right():
332
+ for z in evectors:
333
+ xi = z[0:len(I)]
334
+ xi.set_immutable()
335
+ eta = z[len(I):]
336
+ eta.set_immutable()
337
+ yield (evalue, xi, eta, multiplicity)
338
+
339
+
340
+ def solve_gevp_zero(M, I, J):
341
+ r"""
342
+ Solve the generalized eigenvalue problem in Theorem 3
343
+ [Or2020]_ for a zero eigenvalue using Propositions 3 and 4
344
+ [Or2020]_.
345
+
346
+ INPUT:
347
+
348
+ - ``M`` -- the matrix whose `(i,j)`-th entry is the inner product
349
+ of `g_{i}` and `h_{j}` as in Proposition 6 [Or2020]_
350
+
351
+ - ``I`` -- a linearly independent column-index set for the matrix
352
+ `G` that appears in Theorem 3 [Or2020]_
353
+
354
+ - ``J`` -- a linearly independent column-index set for the matrix
355
+ `H` that appears in Theorem 3 [Or2020]_
356
+
357
+ OUTPUT:
358
+
359
+ A generator of ``(eigenvalue, xi, eta, multiplicity)`` quartets
360
+ where
361
+
362
+ - ``eigenvalue`` is zero (the eigenvalue of the system)
363
+
364
+ - ``xi`` is the first (length ``len(I)``) component of an
365
+ eigenvector associated with ``eigenvalue``
366
+
367
+ - ``eta`` is the second (length ``len(J)``) component of an
368
+ eigenvector associated with ``eigenvalue``
369
+
370
+ - ``multiplicity`` is the dimension of the eigenspace associated
371
+ with ``eigenvalue``
372
+
373
+ ALGORITHM:
374
+
375
+ Proposition 4 in [Or2020]_ is used.
376
+
377
+ EXAMPLES:
378
+
379
+ This particular configuration results in the zero matrix in the
380
+ eigenvalue problem, so the only solutions correspond to the
381
+ eigenvalue zero::
382
+
383
+ sage: from sage.geometry.cone_critical_angles import solve_gevp_zero
384
+ sage: K = cones.nonnegative_orthant(2)
385
+ sage: G = matrix.column(K.rays())
386
+ sage: GG = G.transpose() * G
387
+ sage: I = [0]
388
+ sage: J = [1]
389
+ sage: list(solve_gevp_zero(GG, I, J))
390
+ [(0, (1), (0), 2), (0, (0), (1), 2)]
391
+ """
392
+ # A Cartesian product would be more appropriate here, but Sage
393
+ # isn't smart enough to figure out a basis for the product. So,
394
+ # we use the direct sum and then chop it up.
395
+ M_IJ = M[I,J]
396
+ xi_space = M_IJ.left_kernel()
397
+ eta_space = M_IJ.right_kernel()
398
+
399
+ fake_cartprod = xi_space.direct_sum(eta_space)
400
+ multiplicity = fake_cartprod.dimension()
401
+
402
+ for z in fake_cartprod.basis():
403
+ z1 = z[0:len(I)]
404
+ z1.set_immutable()
405
+ z2 = z[len(I):]
406
+ z2.set_immutable()
407
+
408
+ # The base ring of M will either be RDF or AA, which is enough
409
+ # to contain any eigenvalues that will arise... meaning that
410
+ # if we use the corresponding "zero" here, it will match the
411
+ # field of the eigenvalues returned by the nonzero function.
412
+ yield (M.base_ring().zero(), z1, z2, multiplicity)
413
+
414
+
415
+ def solve_gevp_nonzero(GG, HH, M, I, J):
416
+ r"""
417
+ Solve the generalized eigenvalue problem in Theorem 3
418
+ [Or2020]_ for a nonzero eigenvalue using Propositions 3 and 5
419
+ [Or2020]_.
420
+
421
+ INPUT:
422
+
423
+ - ``GG`` -- the matrix whose `(i,j)`-th entry is the inner product
424
+ of `g_{i}` and `g_{j}`, which are in turn the `i`-th and `j`-th
425
+ columns of the matrix `G` in Theorem 3 [Or2020]_
426
+
427
+ - ``HH`` -- the matrix whose `(i,j)`-th entry is the inner product
428
+ of `h_{i}` and `h_{j}`, which are in turn the `i`-th and `j`-th
429
+ columns of the matrix `H` in Theorem 3 [Or2020]_
430
+
431
+ - ``M`` -- the matrix whose `(i,j)`-th entry is the inner product
432
+ of `g_{i}` and `h_{j}` as in Proposition 6 in [Or2020]_
433
+
434
+ - ``I`` -- a linearly independent column-index set for the matrix
435
+ `G` that appears in Theorem 3 [Or2020]_
436
+
437
+ - ``J`` -- a linearly independent column-index set for the matrix
438
+ `H` that appears in Theorem 3 [Or2020]_
439
+
440
+ OUTPUT:
441
+
442
+ A generator of ``(eigenvalue, xi, eta, multiplicity)`` quartets
443
+ where
444
+
445
+ - ``eigenvalue`` is a real eigenvalue of the system
446
+
447
+ - ``xi`` is the first (length ``len(I)``) component of an
448
+ eigenvector associated with ``eigenvalue``
449
+
450
+ - ``eta`` is the second (length ``len(J)``) component of an
451
+ eigenvector associated with ``eigenvalue``
452
+
453
+ - ``multiplicity`` is the dimension of the eigenspace associated
454
+ with ``eigenvalue``
455
+
456
+ Note that we do not return a basis for each eigenspace along with
457
+ its eigenvalue. For the application we have in mind, an eigenspace
458
+ of dimension greater than one (so, ``multiplicity > 1``) is an
459
+ error. As such, our return value is optimized for convenience in
460
+ the non-error case, where there is only one eigenvector (spanning
461
+ a one-dimensional eigenspace) associated with each eigenvalue.
462
+
463
+ ALGORITHM:
464
+
465
+ According to Proposition 5 [Or2020]_, the solutions corresponding
466
+ to nonzero eigenvalues can be found by solving a smaller
467
+ eigenvalue problem in only the variable `\xi`. So, we do that, and
468
+ then solve for `\eta` in terms of `\xi` as described in the
469
+ proposition.
470
+
471
+ EXAMPLES:
472
+
473
+ When the zero solutions are included, this function returns the
474
+ same solutions as the naive method on the Schur cone in three
475
+ dimensions::
476
+
477
+ sage: from itertools import chain
478
+ sage: from sage.geometry.cone_critical_angles import (
479
+ ....: _normalize_gevp_solution,
480
+ ....: _solve_gevp_naive,
481
+ ....: gevp_licis,
482
+ ....: solve_gevp_nonzero,
483
+ ....: solve_gevp_zero)
484
+ sage: K = cones.schur(3)
485
+ sage: gs = [g.change_ring(AA).normalized() for g in K]
486
+ sage: G = matrix.column(gs)
487
+ sage: GG = G.transpose() * G
488
+ sage: G_index_sets = list(gevp_licis(G))
489
+ sage: all(
490
+ ....: set(
491
+ ....: _normalize_gevp_solution(s)
492
+ ....: for s in
493
+ ....: chain(
494
+ ....: solve_gevp_zero(GG, I, J),
495
+ ....: solve_gevp_nonzero(GG, GG, GG, I, J)
496
+ ....: )
497
+ ....: )
498
+ ....: ==
499
+ ....: set(
500
+ ....: _normalize_gevp_solution(s)
501
+ ....: for s in
502
+ ....: _solve_gevp_naive(GG,GG,GG,I,J)
503
+ ....: )
504
+ ....: for I in G_index_sets
505
+ ....: for J in G_index_sets
506
+ ....: )
507
+ True
508
+
509
+ TESTS:
510
+
511
+ This function should return the same solutions (with zero included,
512
+ of course) as the naive implementation even for random cones::
513
+
514
+ sage: # long time
515
+ sage: from itertools import chain
516
+ sage: from sage.geometry.cone_critical_angles import (
517
+ ....: _normalize_gevp_solution,
518
+ ....: _random_admissible_cone,
519
+ ....: _solve_gevp_naive,
520
+ ....: gevp_licis,
521
+ ....: solve_gevp_nonzero,
522
+ ....: solve_gevp_zero)
523
+ sage: n = ZZ.random_element(1,3)
524
+ sage: P = _random_admissible_cone(ambient_dim=n)
525
+ sage: Q = _random_admissible_cone(ambient_dim=n)
526
+ sage: gs = [g.change_ring(AA).normalized() for g in P]
527
+ sage: G = matrix.column(gs)
528
+ sage: GG = G.transpose() * G
529
+ sage: hs = [h.change_ring(AA).normalized() for h in Q]
530
+ sage: H = matrix.column(hs)
531
+ sage: HH = H.transpose() * H
532
+ sage: M = G.transpose() * H
533
+ sage: G_index_sets = list(gevp_licis(G))
534
+ sage: H_index_sets = list(gevp_licis(H))
535
+ sage: all(
536
+ ....: set(
537
+ ....: _normalize_gevp_solution(s)
538
+ ....: for s in
539
+ ....: chain(
540
+ ....: solve_gevp_zero(M, I, J),
541
+ ....: solve_gevp_nonzero(GG, HH, M, I, J)
542
+ ....: )
543
+ ....: )
544
+ ....: ==
545
+ ....: set(
546
+ ....: _normalize_gevp_solution(s)
547
+ ....: for s in
548
+ ....: _solve_gevp_naive(GG, HH, M, I, J)
549
+ ....: )
550
+ ....: for I in G_index_sets
551
+ ....: for J in H_index_sets
552
+ ....: )
553
+ True
554
+
555
+ According to Proposition 7 [Or2020]_, the only eigenvalues that
556
+ arise when either ``G`` or ``H`` is invertible are `-1`, `0`, and
557
+ `1`::
558
+
559
+ sage: # long time
560
+ sage: from sage.geometry.cone_critical_angles import (
561
+ ....: _random_admissible_cone,
562
+ ....: gevp_licis,
563
+ ....: solve_gevp_nonzero)
564
+ sage: n = ZZ.random_element(1,3)
565
+ sage: P = _random_admissible_cone(ambient_dim=n)
566
+ sage: Q = _random_admissible_cone(ambient_dim=n)
567
+ sage: gs = [g.change_ring(AA).normalized() for g in P]
568
+ sage: hs = [h.change_ring(AA).normalized() for h in Q]
569
+ sage: G = matrix.column(gs)
570
+ sage: GG = G.transpose() * G
571
+ sage: H = matrix.column(hs)
572
+ sage: HH = H.transpose() * H
573
+ sage: M = G.transpose() * H
574
+ sage: from itertools import product
575
+ sage: all(
576
+ ....: (v in [-1,0,1]
577
+ ....: for (v,_,_,_) in solve_gevp_nonzero(GG, HH, M, I, J))
578
+ ....: for (I,J) in product(gevp_licis(G),gevp_licis(H))
579
+ ....: if len(I) == n or len(J) == n )
580
+ True
581
+ """
582
+ if len(J) < len(I):
583
+ # We can always opt to solve the smaller problem. Reading the
584
+ # first three assignments below, you should be able to
585
+ # convince yourself that switching GG <-> HH, I <-> J, and
586
+ # transposing M does in fact switch from the "xi problem" to
587
+ # the "eta problem."
588
+ yield from ((l, xi, eta, m)
589
+ for (l, eta, xi, m)
590
+ in solve_gevp_nonzero(HH, GG, M.transpose(), J, I))
591
+ else:
592
+ M_IJ = M[I,J]
593
+ G_I_pinv_H_J = GG[I,I].inverse_positive_definite() * M_IJ
594
+ H_J_pinv_G_I = HH[J,J].inverse_positive_definite() * M_IJ.transpose()
595
+ L = (G_I_pinv_H_J * H_J_pinv_G_I)
596
+
597
+ for (sigma, xis, m) in L.eigenvectors_right():
598
+ if sigma > 0:
599
+ # Avoid recomputing these for each xi in xis
600
+ sigma_sqrt = sigma.sqrt()
601
+ inv_sqrt = ~sigma_sqrt
602
+ pm_sqrt_inv_pairs = [
603
+ (-sigma_sqrt, -inv_sqrt),
604
+ (sigma_sqrt, inv_sqrt)
605
+ ]
606
+
607
+ for xi in xis:
608
+ for l, li in pm_sqrt_inv_pairs:
609
+ eta = li * H_J_pinv_G_I*xi
610
+ eta.set_immutable()
611
+ yield (l, xi, eta, m)
612
+
613
+
614
+ def compute_gevp_M(gs, hs):
615
+ r"""
616
+ Compute the matrix `M` whose `(i,j)`-th entry is the inner
617
+ product of ``gs[i]`` and ``hs[j]``.
618
+
619
+ This is the "generalized Gram matrix" appearing in Proposition 6
620
+ in [Or2020]_. For efficiency, we also return the minimal pair,
621
+ whose inner product is minimal among the entries of `M`. This
622
+ allows our consumer to bail out immediately (knowing the optimal
623
+ pair!) if it turns out that the maximal angle is acute; i.e. if
624
+ the smallest entry of `M` is nonnegative.
625
+
626
+ INPUT:
627
+
628
+ - ``gs`` -- a linearly independent list of unit-norm generators
629
+ for the cone `P`
630
+
631
+ - ``hs`` -- a linearly independent list of unit-norm generators
632
+ for the cone `Q`
633
+
634
+ OUTPUT: a tuple containing four elements, in order:
635
+
636
+ - The matrix `M` described in Proposition 6
637
+
638
+ - The minimal entry in the matrix `M`
639
+
640
+ - A vector in ``gs`` that achieves that minimal inner product
641
+ along with the next element of the tuple
642
+
643
+ - A vector in ``hs`` that achieves the minimal inner product
644
+ along with the previous element in the tuple
645
+
646
+ EXAMPLES::
647
+
648
+ sage: from sage.geometry.cone_critical_angles import compute_gevp_M
649
+ sage: P = Cone([ (1,2,0), (3,4,0) ])
650
+ sage: Q = Cone([ (-1,4,1), (5,-2,-1), (-1,-1,5) ])
651
+ sage: gs = [g.change_ring(QQ) for g in P]
652
+ sage: hs = [h.change_ring(QQ) for h in Q]
653
+ sage: M = compute_gevp_M(gs, hs)[0]
654
+ sage: all( M[i][j] == gs[i].inner_product(hs[j])
655
+ ....: for i in range(P.nrays())
656
+ ....: for j in range(Q.nrays()) )
657
+ True
658
+
659
+ TESTS:
660
+
661
+ The products `(G_{I})^{T}H_{J}` correspond to
662
+ submatrices of the "generalized Gram matrix" `M` in Proposition
663
+ 6. Note that SageMath does (row,column) indexing but [Or2020]_
664
+ does (column,row) indexing::
665
+
666
+ sage: from sage.geometry.cone_critical_angles import (
667
+ ....: _random_admissible_cone,
668
+ ....: compute_gevp_M,
669
+ ....: gevp_licis)
670
+ sage: n = ZZ.random_element(1,4)
671
+ sage: n = ZZ.random_element(1,8) # long time
672
+ sage: P = _random_admissible_cone(ambient_dim=n)
673
+ sage: Q = _random_admissible_cone(ambient_dim=n)
674
+ sage: gs = [g.change_ring(QQ) for g in P]
675
+ sage: hs = [h.change_ring(QQ) for h in Q]
676
+ sage: M = compute_gevp_M(gs,hs)[0]
677
+ sage: f = lambda i,j: gs[i].inner_product(hs[j])
678
+ sage: expected_M = matrix(QQ, P.nrays(), Q.nrays(), f)
679
+ sage: M == expected_M
680
+ True
681
+ sage: G = matrix.column(gs)
682
+ sage: H = matrix.column(hs)
683
+ sage: def _test_indexing(I, J):
684
+ ....: G_I = G.matrix_from_columns(I)
685
+ ....: H_J = H.matrix_from_columns(J)
686
+ ....: return (G_I.transpose()*H_J == M[I,J]
687
+ ....: and
688
+ ....: H_J.transpose()*G_I == M.transpose()[J,I])
689
+ sage: G_index_sets = list(gevp_licis(G))
690
+ sage: H_index_sets = list(gevp_licis(H))
691
+ sage: all( _test_indexing(I,J) for I in G_index_sets
692
+ ....: for J in H_index_sets )
693
+ True
694
+ """
695
+ min_u = gs[0]
696
+ min_v = hs[0]
697
+ min_ip = min_u.inner_product(min_v)
698
+
699
+ M = []
700
+ for g in gs:
701
+ M_i = []
702
+ for h in hs:
703
+ val = g.inner_product(h)
704
+ M_i.append(val)
705
+ if (val < min_ip):
706
+ min_ip = val
707
+ min_u = g
708
+ min_v = h
709
+ M.append(M_i)
710
+
711
+ return (matrix(M), min_ip, min_u, min_v)
712
+
713
+
714
+ def check_gevp_feasibility(cos_theta, xi, eta, G_I, G_I_c_T,
715
+ H_J, H_J_c_T, epsilon):
716
+ r"""
717
+ Determine if a solution to the generalized eigenvalue problem
718
+ in Theorem 3 [Or2020]_ is feasible.
719
+
720
+ Implementation detail: we take four matrices that we are capable
721
+ of computing as parameters instead, because we will be called in a
722
+ nested loop "for all `I`... and for all `J`..." The data
723
+ corresponding to `I` should be computed only once, which means
724
+ that we can't do it here -- it needs to be done outside of the `J`
725
+ loop. For symmetry (and to avoid relying on too many
726
+ cross-function implementation details), we also insist that the
727
+ `J` data be passed in.
728
+
729
+ INPUT:
730
+
731
+ - ``cos_theta`` -- an eigenvalue corresponding to
732
+ `( \xi, \eta )`
733
+
734
+ - ``xi`` -- first component of the `( \xi, \eta )` eigenvector
735
+
736
+ - ``eta`` -- second component of the `( \xi, \eta )` eigenvector
737
+
738
+ - ``G_I`` -- the submatrix of `G` with columns indexed by `I`
739
+
740
+ - ``G_I_c_T`` -- a matrix whose rows are the non-`I` columns of `G`
741
+
742
+ - ``H_J`` -- the submatrix of `H` with columns indexed by `J`
743
+
744
+ - ``H_J_c_T`` -- a matrix whose rows are the non-`J` columns of `H`
745
+
746
+ - ``epsilon`` -- the tolerance to use when making comparisons
747
+
748
+ OUTPUT:
749
+
750
+ A triple containing (in order),
751
+
752
+ - a boolean,
753
+ - a vector in the cone `P` (of the same length as ``xi``), and
754
+ - a vector in the cone `Q` (of the same length as ``eta``).
755
+
756
+ If `( \xi, \eta )` is feasible, we return ``(True, u, v)`` where `u`
757
+ and `v` are the vectors in `P` and `Q` respectively that form the
758
+ the angle `\theta`.
759
+
760
+ If `( \xi, \eta )` is **not** feasible, then we return ``(False, 0, 0)``
761
+ where ``0`` should be interpreted to mean the zero vector in the
762
+ appropriate space.
763
+
764
+ EXAMPLES:
765
+
766
+ If `\xi` has any components less than "zero," it isn't feasible::
767
+
768
+ sage: from sage.geometry.cone_critical_angles import(
769
+ ....: check_gevp_feasibility)
770
+ sage: xi = vector(QQ, [-1,1])
771
+ sage: eta = vector(QQ, [1,1,1])
772
+ sage: check_gevp_feasibility(0,xi,eta,None,None,None,None,0)
773
+ (False, (0, 0), (0, 0, 0))
774
+
775
+ If `\eta` has any components less than "zero," it isn't feasible::
776
+
777
+ sage: from sage.geometry.cone_critical_angles import(
778
+ ....: check_gevp_feasibility)
779
+ sage: xi = vector(QQ, [2])
780
+ sage: eta = vector(QQ, [1,-4,4,5])
781
+ sage: check_gevp_feasibility(0,xi,eta,None,None,None,None,0)
782
+ (False, (0), (0, 0, 0, 0))
783
+
784
+ If `\xi` and `\eta` are equal and if `G_{I}` and `H_{J}` are not,
785
+ then the copy of `\eta` that's been scaled by the norm of `G_{I}\xi`
786
+ generally won't satisfy its norm-equality constraint::
787
+
788
+ sage: from sage.geometry.cone_critical_angles import(
789
+ ....: check_gevp_feasibility)
790
+ sage: xi = vector(QQ, [1,1])
791
+ sage: eta = xi
792
+ sage: G_I = matrix.identity(QQ,2)
793
+ sage: H_J = 2*G_I
794
+ sage: check_gevp_feasibility(0,xi,eta,G_I,None,H_J,None,0) # needs sage.symbolic
795
+ (False, (0, 0), (0, 0))
796
+
797
+ When `\cos\theta` is zero, the inequality (42) in Theorem 7.3
798
+ [SS2016]_ is just an inner product with `v` which we can make
799
+ positive by ensuring that all of the entries of `H_{J}` are
800
+ positive. So, if any of the rows of ``G_I_c_T`` contain a negative
801
+ entry, (42) will fail::
802
+
803
+ sage: from sage.geometry.cone_critical_angles import(
804
+ ....: check_gevp_feasibility)
805
+ sage: xi = vector(QQ, [1/2,1/2,1/2,1/2])
806
+ sage: eta = xi
807
+ sage: G_I = matrix.identity(QQ,4)
808
+ sage: G_I_c_T = matrix(QQ, [[0,-1,0,0]])
809
+ sage: H_J = G_I
810
+ sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,None,0) # needs sage.symbolic
811
+ (False, (0, 0, 0, 0), (0, 0, 0, 0))
812
+
813
+ Likewise we can make (43) fail in exactly the same way::
814
+
815
+ sage: from sage.geometry.cone_critical_angles import(
816
+ ....: check_gevp_feasibility)
817
+ sage: xi = vector(QQ, [1/2,1/2,1/2,1/2])
818
+ sage: eta = xi
819
+ sage: G_I = matrix.identity(QQ,4)
820
+ sage: G_I_c_T = matrix(QQ, [[0,1,0,0]])
821
+ sage: H_J = G_I
822
+ sage: H_J_c_T = matrix(QQ, [[0,-1,0,0]])
823
+ sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,H_J_c_T,0) # needs sage.symbolic
824
+ (False, (0, 0, 0, 0), (0, 0, 0, 0))
825
+
826
+ Finally, if we ensure that everything works, we get back a feasible
827
+ result along with the vectors (scaled `\xi` and `\eta`) that worked::
828
+
829
+ sage: from sage.geometry.cone_critical_angles import(
830
+ ....: check_gevp_feasibility)
831
+ sage: xi = vector(QQ, [1/2,1/2,1/2,1/2])
832
+ sage: eta = xi
833
+ sage: G_I = matrix.identity(QQ,4)
834
+ sage: G_I_c_T = matrix(QQ, [[0,1,0,0]])
835
+ sage: H_J = G_I
836
+ sage: H_J_c_T = matrix(QQ, [[0,1,0,0]])
837
+ sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,H_J_c_T,0) # needs sage.symbolic
838
+ (True, (1/2, 1/2, 1/2, 1/2), (1/2, 1/2, 1/2, 1/2))
839
+ """
840
+ infeasible_result = (False, 0*xi, 0*eta)
841
+ if min(xi) <= -epsilon or min(eta) <= -epsilon:
842
+ # xi or eta isn't in the interior of the nonnegative orthant,
843
+ # so skip this (non-)solution.
844
+ return infeasible_result
845
+
846
+ # Rescale xi to satisfy (44), and rescale eta by the same amount,
847
+ # because (xi,eta) needs to remain in the same one-dimensional
848
+ # eigenspace.
849
+ scale = ~((G_I*xi).norm())
850
+ xi_hat = xi * scale
851
+ eta_hat = eta * scale
852
+
853
+ # Now check that (45) is satisfied.
854
+ if ((H_J*eta_hat).norm() - 1).abs() > epsilon:
855
+ return infeasible_result
856
+
857
+ # And check that (42,43) are satisfied.
858
+ v = H_J * eta_hat
859
+ rhs = v - cos_theta*G_I*xi_hat
860
+
861
+ if any(x < -epsilon for x in G_I_c_T * rhs):
862
+ return infeasible_result
863
+
864
+ u = G_I * xi_hat
865
+ rhs = u - cos_theta*H_J*eta_hat
866
+ if any(x < -epsilon for x in H_J_c_T * rhs):
867
+ return infeasible_result
868
+
869
+ return (True, u, v)
870
+
871
+
872
+ def max_angle(P, Q, exact, epsilon):
873
+ r"""
874
+ Find the maximal angle between the cones `P` and `Q`.
875
+
876
+ This implements
877
+ :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle`,
878
+ which should be fully documented.
879
+
880
+ EXAMPLES:
881
+
882
+ For the sake of the user interface, the argument validation for
883
+ this function is performed in the associated cone method; we can
884
+ therefore crash it by feeding it invalid input like an
885
+ inadmissible cone::
886
+
887
+ sage: from sage.geometry.cone_critical_angles import max_angle
888
+ sage: K = cones.trivial(3)
889
+ sage: max_angle(K,K,True,0)
890
+ Traceback (most recent call last):
891
+ ...
892
+ IndexError: list index out of range
893
+ """
894
+ # The lattice dimensions of P and Q are guaranteed to be equal
895
+ # because the cone method checks it before calling us.
896
+ n = P.lattice_dim()
897
+
898
+ ring = RDF
899
+ if exact:
900
+ ring = AA
901
+ # For some reason we can go RR -> QQ -> AA, but not
902
+ # straight from RR to AA.
903
+ epsilon = QQ(epsilon)
904
+ epsilon = ring(epsilon)
905
+
906
+ # First check if P is contained in the dual of Q. Keep track of
907
+ # the minimum inner product (and associated vectors) while doing
908
+ # so; then if P is contained in dual(Q), we just return the pair
909
+ # with the smallest inner product.
910
+ gs = [g.change_ring(ring).normalized() for g in P]
911
+ Q_is_P = (P == Q) # This is used again later
912
+ if Q_is_P:
913
+ hs = gs
914
+ else:
915
+ hs = [h.change_ring(ring).normalized() for h in Q]
916
+
917
+ (M, min_ip, min_u, min_v) = compute_gevp_M(gs,hs)
918
+
919
+ if min_ip >= 0: # The maximal angle is acute!
920
+ return (arccos(min_ip), min_u, min_v)
921
+
922
+ # Also check to see if the maximal angle is pi. In particular this
923
+ # is true when either P or Q is the entire ambient space.
924
+ P_and_negative_Q = P.intersection(-Q)
925
+ if not (P_and_negative_Q.is_trivial()):
926
+ u = P_and_negative_Q.ray(0).change_ring(ring).normalized()
927
+ v = -u
928
+ return (pi, u, v)
929
+
930
+ # When P == Q, GG and HH are both just M. We rule out the
931
+ # cardinality ``n`` in the index sets because it will eventually
932
+ # result in a GEVP whose only solutions are lambda in {-1,0,1};
933
+ # none of which we want! We rule out 0 and 1 with the acute check,
934
+ # and -1 with the pi (P_and_negative_Q) check.
935
+ #
936
+ # It's VERY IMPORTANT that we construct lists from the index set
937
+ # generators, because we're going to use them in a nested loop!
938
+ G = matrix.column(gs)
939
+ G_index_sets = [s for s in gevp_licis(G) if not len(s) == n]
940
+
941
+ if Q_is_P:
942
+ GG = M
943
+ H = G
944
+ HH = M
945
+ H_index_sets = G_index_sets
946
+ else:
947
+ GG = G.transpose() * G
948
+ H = matrix.column(hs)
949
+ HH = H.transpose() * H
950
+ H_index_sets = [s for s in gevp_licis(H) if not len(s) == n]
951
+
952
+ # Keep track of the (cos-theta, xi, eta, multiplicity) tuples with
953
+ # multiplicity > 1. These are only a problem if they could
954
+ # potentially be maximal. Therefore, we want to inspect them AFTER
955
+ # we've checked all of the multiplicity=1 angles and found the
956
+ # largest. This allows us to ignore most of the problematic
957
+ # eigenspaces, independent of the order in which we run through I,J.
958
+ big_eigenspaces = []
959
+
960
+ for I in G_index_sets:
961
+ G_I = G.matrix_from_columns(I)
962
+ I_complement = [i for i in range(P.nrays()) if i not in I]
963
+ G_I_c_T = G.matrix_from_columns(I_complement).transpose()
964
+
965
+ for J in H_index_sets:
966
+ J_complement = [j for j in range(Q.nrays()) if j not in J]
967
+ H_J = H.matrix_from_columns(J)
968
+ H_J_c_T = H.matrix_from_columns(J_complement).transpose()
969
+
970
+ for (cos_theta,xi,eta,mult) in solve_gevp_nonzero(GG, HH, M, I, J):
971
+
972
+ if cos_theta >= min_ip:
973
+ # This potential critical angle is smaller than or
974
+ # equal to one that we've already found. Why
975
+ # bother?
976
+ continue
977
+
978
+ if cos_theta == -1:
979
+ # We already ruled this case out with the
980
+ # "P_and_negative_Q" trick.
981
+ continue
982
+
983
+ (is_feasible, u, v) = check_gevp_feasibility(cos_theta,
984
+ xi,
985
+ eta,
986
+ G_I,
987
+ G_I_c_T,
988
+ H_J,
989
+ H_J_c_T,
990
+ epsilon)
991
+
992
+ if is_feasible:
993
+ min_ip = cos_theta
994
+ min_u = u
995
+ min_v = v
996
+ elif mult > 1:
997
+ # Save this for later. The eigenvalue cos_theta
998
+ # might be so big that we can ignore it.
999
+ big_eigenspaces.append((cos_theta, xi, eta, mult))
1000
+ continue
1001
+
1002
+ for (cos_theta, xi, eta, mult) in big_eigenspaces:
1003
+ if cos_theta < min_ip:
1004
+ # The existence of a big eigenspace is only a problem if
1005
+ # cos_theta could actually be minimal.
1006
+
1007
+ if exact and P.is_strictly_convex():
1008
+ if Q_is_P or Q.is_strictly_convex():
1009
+ # The maximal angle composed with the conic hull
1010
+ # is continuous at (gs,hs), so we can retry with
1011
+ # inexact arithmetic. That will "perturb"
1012
+ # everything, hopefully eliminating any larger
1013
+ # eigenspaces and without changing the answer too
1014
+ # much.
1015
+ return max_angle(P, Q, False, epsilon)
1016
+
1017
+ # Either we don't know that the maximal angle of the conic
1018
+ # hull is continuous at (gs,hs), or we're already using
1019
+ # inexact arithmetic. There's nothing left to try. (Note
1020
+ # that the case where either P or Q is the ambient space
1021
+ # was handled much earlier, since in that case the maximal
1022
+ # angle is obviously pi.)
1023
+ raise ValueError('eigenspace of dimension %d > 1 '
1024
+ 'corresponding to eigenvalue %s'
1025
+ % (mult, cos_theta))
1026
+
1027
+ return (arccos(min_ip), min_u, min_v)