passagemath-schemes 10.6.38__cp314-cp314t-macosx_13_0_arm64.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-schemes might be problematic. Click here for more details.

Files changed (314) hide show
  1. passagemath_schemes/.dylibs/libflint.21.0.dylib +0 -0
  2. passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
  3. passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
  4. passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
  5. passagemath_schemes/__init__.py +3 -0
  6. passagemath_schemes-10.6.38.dist-info/METADATA +204 -0
  7. passagemath_schemes-10.6.38.dist-info/METADATA.bak +205 -0
  8. passagemath_schemes-10.6.38.dist-info/RECORD +314 -0
  9. passagemath_schemes-10.6.38.dist-info/WHEEL +6 -0
  10. passagemath_schemes-10.6.38.dist-info/top_level.txt +3 -0
  11. sage/all__sagemath_schemes.py +23 -0
  12. sage/databases/all__sagemath_schemes.py +7 -0
  13. sage/databases/cremona.py +1723 -0
  14. sage/dynamics/all__sagemath_schemes.py +2 -0
  15. sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
  16. sage/dynamics/arithmetic_dynamics/all.py +14 -0
  17. sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
  18. sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
  19. sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
  20. sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
  21. sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
  22. sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
  23. sage/dynamics/arithmetic_dynamics/projective_ds.py +9558 -0
  24. sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314t-darwin.so +0 -0
  25. sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
  26. sage/dynamics/arithmetic_dynamics/wehlerK3.py +2576 -0
  27. sage/lfunctions/all.py +18 -0
  28. sage/lfunctions/dokchitser.py +745 -0
  29. sage/lfunctions/pari.py +818 -0
  30. sage/lfunctions/zero_sums.cpython-314t-darwin.so +0 -0
  31. sage/lfunctions/zero_sums.pyx +1847 -0
  32. sage/modular/abvar/abvar.py +5135 -0
  33. sage/modular/abvar/abvar_ambient_jacobian.py +413 -0
  34. sage/modular/abvar/abvar_newform.py +244 -0
  35. sage/modular/abvar/all.py +8 -0
  36. sage/modular/abvar/constructor.py +186 -0
  37. sage/modular/abvar/cuspidal_subgroup.py +371 -0
  38. sage/modular/abvar/finite_subgroup.py +896 -0
  39. sage/modular/abvar/homology.py +720 -0
  40. sage/modular/abvar/homspace.py +998 -0
  41. sage/modular/abvar/lseries.py +415 -0
  42. sage/modular/abvar/morphism.py +935 -0
  43. sage/modular/abvar/torsion_point.py +274 -0
  44. sage/modular/abvar/torsion_subgroup.py +740 -0
  45. sage/modular/all.py +43 -0
  46. sage/modular/arithgroup/all.py +20 -0
  47. sage/modular/arithgroup/arithgroup_element.cpython-314t-darwin.so +0 -0
  48. sage/modular/arithgroup/arithgroup_element.pyx +474 -0
  49. sage/modular/arithgroup/arithgroup_generic.py +1402 -0
  50. sage/modular/arithgroup/arithgroup_perm.py +2692 -0
  51. sage/modular/arithgroup/congroup.cpython-314t-darwin.so +0 -0
  52. sage/modular/arithgroup/congroup.pyx +334 -0
  53. sage/modular/arithgroup/congroup_gamma.py +363 -0
  54. sage/modular/arithgroup/congroup_gamma0.py +692 -0
  55. sage/modular/arithgroup/congroup_gamma1.py +653 -0
  56. sage/modular/arithgroup/congroup_gammaH.py +1469 -0
  57. sage/modular/arithgroup/congroup_generic.py +628 -0
  58. sage/modular/arithgroup/congroup_sl2z.py +267 -0
  59. sage/modular/arithgroup/farey_symbol.cpython-314t-darwin.so +0 -0
  60. sage/modular/arithgroup/farey_symbol.pyx +1066 -0
  61. sage/modular/arithgroup/tests.py +418 -0
  62. sage/modular/btquotients/all.py +4 -0
  63. sage/modular/btquotients/btquotient.py +3753 -0
  64. sage/modular/btquotients/pautomorphicform.py +2570 -0
  65. sage/modular/buzzard.py +100 -0
  66. sage/modular/congroup.py +29 -0
  67. sage/modular/congroup_element.py +13 -0
  68. sage/modular/cusps.py +1109 -0
  69. sage/modular/cusps_nf.py +1270 -0
  70. sage/modular/dims.py +569 -0
  71. sage/modular/dirichlet.py +3310 -0
  72. sage/modular/drinfeld_modform/all.py +2 -0
  73. sage/modular/drinfeld_modform/element.py +446 -0
  74. sage/modular/drinfeld_modform/ring.py +773 -0
  75. sage/modular/drinfeld_modform/tutorial.py +236 -0
  76. sage/modular/etaproducts.py +1065 -0
  77. sage/modular/hecke/algebra.py +746 -0
  78. sage/modular/hecke/all.py +20 -0
  79. sage/modular/hecke/ambient_module.py +1019 -0
  80. sage/modular/hecke/degenmap.py +119 -0
  81. sage/modular/hecke/element.py +325 -0
  82. sage/modular/hecke/hecke_operator.py +780 -0
  83. sage/modular/hecke/homspace.py +206 -0
  84. sage/modular/hecke/module.py +1767 -0
  85. sage/modular/hecke/morphism.py +174 -0
  86. sage/modular/hecke/submodule.py +989 -0
  87. sage/modular/hypergeometric_misc.cpython-314t-darwin.so +0 -0
  88. sage/modular/hypergeometric_misc.pxd +4 -0
  89. sage/modular/hypergeometric_misc.pyx +166 -0
  90. sage/modular/hypergeometric_motive.py +2017 -0
  91. sage/modular/local_comp/all.py +2 -0
  92. sage/modular/local_comp/liftings.py +292 -0
  93. sage/modular/local_comp/local_comp.py +1071 -0
  94. sage/modular/local_comp/smoothchar.py +1825 -0
  95. sage/modular/local_comp/type_space.py +748 -0
  96. sage/modular/modform/all.py +30 -0
  97. sage/modular/modform/ambient.py +815 -0
  98. sage/modular/modform/ambient_R.py +177 -0
  99. sage/modular/modform/ambient_eps.py +306 -0
  100. sage/modular/modform/ambient_g0.py +124 -0
  101. sage/modular/modform/ambient_g1.py +204 -0
  102. sage/modular/modform/constructor.py +545 -0
  103. sage/modular/modform/cuspidal_submodule.py +708 -0
  104. sage/modular/modform/defaults.py +14 -0
  105. sage/modular/modform/eis_series.py +505 -0
  106. sage/modular/modform/eisenstein_submodule.py +663 -0
  107. sage/modular/modform/element.py +4131 -0
  108. sage/modular/modform/find_generators.py +59 -0
  109. sage/modular/modform/half_integral.py +154 -0
  110. sage/modular/modform/hecke_operator_on_qexp.py +247 -0
  111. sage/modular/modform/j_invariant.py +47 -0
  112. sage/modular/modform/l_series_gross_zagier.py +133 -0
  113. sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314t-darwin.so +0 -0
  114. sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
  115. sage/modular/modform/notes.py +45 -0
  116. sage/modular/modform/numerical.py +514 -0
  117. sage/modular/modform/periods.py +14 -0
  118. sage/modular/modform/ring.py +1257 -0
  119. sage/modular/modform/space.py +1860 -0
  120. sage/modular/modform/submodule.py +118 -0
  121. sage/modular/modform/tests.py +64 -0
  122. sage/modular/modform/theta.py +110 -0
  123. sage/modular/modform/vm_basis.py +381 -0
  124. sage/modular/modform/weight1.py +220 -0
  125. sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
  126. sage/modular/modform_hecketriangle/abstract_space.py +2528 -0
  127. sage/modular/modform_hecketriangle/all.py +30 -0
  128. sage/modular/modform_hecketriangle/analytic_type.py +590 -0
  129. sage/modular/modform_hecketriangle/constructor.py +416 -0
  130. sage/modular/modform_hecketriangle/element.py +351 -0
  131. sage/modular/modform_hecketriangle/functors.py +752 -0
  132. sage/modular/modform_hecketriangle/graded_ring.py +541 -0
  133. sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
  134. sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3352 -0
  135. sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1432 -0
  136. sage/modular/modform_hecketriangle/readme.py +1214 -0
  137. sage/modular/modform_hecketriangle/series_constructor.py +580 -0
  138. sage/modular/modform_hecketriangle/space.py +1037 -0
  139. sage/modular/modform_hecketriangle/subspace.py +423 -0
  140. sage/modular/modsym/all.py +17 -0
  141. sage/modular/modsym/ambient.py +3846 -0
  142. sage/modular/modsym/boundary.py +1420 -0
  143. sage/modular/modsym/element.py +336 -0
  144. sage/modular/modsym/g1list.py +178 -0
  145. sage/modular/modsym/ghlist.py +182 -0
  146. sage/modular/modsym/hecke_operator.py +73 -0
  147. sage/modular/modsym/manin_symbol.cpython-314t-darwin.so +0 -0
  148. sage/modular/modsym/manin_symbol.pxd +5 -0
  149. sage/modular/modsym/manin_symbol.pyx +497 -0
  150. sage/modular/modsym/manin_symbol_list.py +1295 -0
  151. sage/modular/modsym/modsym.py +400 -0
  152. sage/modular/modsym/modular_symbols.py +384 -0
  153. sage/modular/modsym/p1list.cpython-314t-darwin.so +0 -0
  154. sage/modular/modsym/p1list.pxd +29 -0
  155. sage/modular/modsym/p1list.pyx +1372 -0
  156. sage/modular/modsym/p1list_nf.py +1241 -0
  157. sage/modular/modsym/relation_matrix.py +591 -0
  158. sage/modular/modsym/relation_matrix_pyx.cpython-314t-darwin.so +0 -0
  159. sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
  160. sage/modular/modsym/space.py +2468 -0
  161. sage/modular/modsym/subspace.py +455 -0
  162. sage/modular/modsym/tests.py +375 -0
  163. sage/modular/multiple_zeta.py +2632 -0
  164. sage/modular/multiple_zeta_F_algebra.py +786 -0
  165. sage/modular/overconvergent/all.py +6 -0
  166. sage/modular/overconvergent/genus0.py +1878 -0
  167. sage/modular/overconvergent/hecke_series.py +1187 -0
  168. sage/modular/overconvergent/weightspace.py +778 -0
  169. sage/modular/pollack_stevens/all.py +4 -0
  170. sage/modular/pollack_stevens/distributions.py +874 -0
  171. sage/modular/pollack_stevens/fund_domain.py +1572 -0
  172. sage/modular/pollack_stevens/manin_map.py +859 -0
  173. sage/modular/pollack_stevens/modsym.py +1593 -0
  174. sage/modular/pollack_stevens/padic_lseries.py +417 -0
  175. sage/modular/pollack_stevens/sigma0.py +534 -0
  176. sage/modular/pollack_stevens/space.py +1076 -0
  177. sage/modular/quasimodform/all.py +3 -0
  178. sage/modular/quasimodform/element.py +845 -0
  179. sage/modular/quasimodform/ring.py +828 -0
  180. sage/modular/quatalg/all.py +3 -0
  181. sage/modular/quatalg/brandt.py +1642 -0
  182. sage/modular/ssmod/all.py +8 -0
  183. sage/modular/ssmod/ssmod.py +827 -0
  184. sage/rings/all__sagemath_schemes.py +1 -0
  185. sage/rings/polynomial/all__sagemath_schemes.py +1 -0
  186. sage/rings/polynomial/binary_form_reduce.py +585 -0
  187. sage/schemes/all.py +41 -0
  188. sage/schemes/berkovich/all.py +6 -0
  189. sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
  190. sage/schemes/berkovich/berkovich_space.py +748 -0
  191. sage/schemes/curves/affine_curve.py +2928 -0
  192. sage/schemes/curves/all.py +33 -0
  193. sage/schemes/curves/closed_point.py +434 -0
  194. sage/schemes/curves/constructor.py +381 -0
  195. sage/schemes/curves/curve.py +542 -0
  196. sage/schemes/curves/plane_curve_arrangement.py +1283 -0
  197. sage/schemes/curves/point.py +463 -0
  198. sage/schemes/curves/projective_curve.py +3026 -0
  199. sage/schemes/curves/zariski_vankampen.py +1932 -0
  200. sage/schemes/cyclic_covers/all.py +2 -0
  201. sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
  202. sage/schemes/cyclic_covers/constructor.py +137 -0
  203. sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
  204. sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
  205. sage/schemes/elliptic_curves/BSD.py +1036 -0
  206. sage/schemes/elliptic_curves/Qcurves.py +592 -0
  207. sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
  208. sage/schemes/elliptic_curves/all.py +49 -0
  209. sage/schemes/elliptic_curves/cardinality.py +609 -0
  210. sage/schemes/elliptic_curves/cm.py +1102 -0
  211. sage/schemes/elliptic_curves/constructor.py +1552 -0
  212. sage/schemes/elliptic_curves/ec_database.py +175 -0
  213. sage/schemes/elliptic_curves/ell_curve_isogeny.py +3972 -0
  214. sage/schemes/elliptic_curves/ell_egros.py +459 -0
  215. sage/schemes/elliptic_curves/ell_field.py +2836 -0
  216. sage/schemes/elliptic_curves/ell_finite_field.py +3359 -0
  217. sage/schemes/elliptic_curves/ell_generic.py +3760 -0
  218. sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
  219. sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
  220. sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
  221. sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
  222. sage/schemes/elliptic_curves/ell_point.py +4787 -0
  223. sage/schemes/elliptic_curves/ell_rational_field.py +7368 -0
  224. sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
  225. sage/schemes/elliptic_curves/ell_torsion.py +436 -0
  226. sage/schemes/elliptic_curves/ell_wp.py +352 -0
  227. sage/schemes/elliptic_curves/formal_group.py +760 -0
  228. sage/schemes/elliptic_curves/gal_reps.py +1459 -0
  229. sage/schemes/elliptic_curves/gal_reps_number_field.py +1669 -0
  230. sage/schemes/elliptic_curves/gp_simon.py +152 -0
  231. sage/schemes/elliptic_curves/heegner.py +7335 -0
  232. sage/schemes/elliptic_curves/height.py +2109 -0
  233. sage/schemes/elliptic_curves/hom.py +1406 -0
  234. sage/schemes/elliptic_curves/hom_composite.py +934 -0
  235. sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
  236. sage/schemes/elliptic_curves/hom_scalar.py +531 -0
  237. sage/schemes/elliptic_curves/hom_sum.py +682 -0
  238. sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
  239. sage/schemes/elliptic_curves/homset.py +271 -0
  240. sage/schemes/elliptic_curves/isogeny_class.py +1521 -0
  241. sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
  242. sage/schemes/elliptic_curves/jacobian.py +237 -0
  243. sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
  244. sage/schemes/elliptic_curves/kraus.py +1014 -0
  245. sage/schemes/elliptic_curves/lseries_ell.py +943 -0
  246. sage/schemes/elliptic_curves/mod5family.py +105 -0
  247. sage/schemes/elliptic_curves/mod_poly.py +197 -0
  248. sage/schemes/elliptic_curves/mod_sym_num.cpython-314t-darwin.so +0 -0
  249. sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
  250. sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
  251. sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
  252. sage/schemes/elliptic_curves/padics.py +1816 -0
  253. sage/schemes/elliptic_curves/period_lattice.py +2234 -0
  254. sage/schemes/elliptic_curves/period_lattice_region.cpython-314t-darwin.so +0 -0
  255. sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
  256. sage/schemes/elliptic_curves/saturation.py +715 -0
  257. sage/schemes/elliptic_curves/sha_tate.py +1158 -0
  258. sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
  259. sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
  260. sage/schemes/hyperelliptic_curves/all.py +6 -0
  261. sage/schemes/hyperelliptic_curves/constructor.py +291 -0
  262. sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1914 -0
  263. sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
  264. sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +954 -0
  265. sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
  266. sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
  267. sage/schemes/hyperelliptic_curves/invariants.py +410 -0
  268. sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +315 -0
  269. sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
  270. sage/schemes/hyperelliptic_curves/jacobian_generic.py +419 -0
  271. sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
  272. sage/schemes/hyperelliptic_curves/jacobian_morphism.py +875 -0
  273. sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
  274. sage/schemes/hyperelliptic_curves/mestre.py +302 -0
  275. sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3871 -0
  276. sage/schemes/jacobians/abstract_jacobian.py +277 -0
  277. sage/schemes/jacobians/all.py +2 -0
  278. sage/schemes/overview.py +161 -0
  279. sage/schemes/plane_conics/all.py +22 -0
  280. sage/schemes/plane_conics/con_field.py +1296 -0
  281. sage/schemes/plane_conics/con_finite_field.py +158 -0
  282. sage/schemes/plane_conics/con_number_field.py +456 -0
  283. sage/schemes/plane_conics/con_rational_field.py +406 -0
  284. sage/schemes/plane_conics/con_rational_function_field.py +580 -0
  285. sage/schemes/plane_conics/constructor.py +249 -0
  286. sage/schemes/plane_quartics/all.py +2 -0
  287. sage/schemes/plane_quartics/quartic_constructor.py +71 -0
  288. sage/schemes/plane_quartics/quartic_generic.py +73 -0
  289. sage/schemes/riemann_surfaces/all.py +1 -0
  290. sage/schemes/riemann_surfaces/riemann_surface.py +4117 -0
  291. sage_wheels/share/cremona/cremona_mini.db +0 -0
  292. sage_wheels/share/ellcurves/rank0 +30427 -0
  293. sage_wheels/share/ellcurves/rank1 +31871 -0
  294. sage_wheels/share/ellcurves/rank10 +6 -0
  295. sage_wheels/share/ellcurves/rank11 +6 -0
  296. sage_wheels/share/ellcurves/rank12 +1 -0
  297. sage_wheels/share/ellcurves/rank14 +1 -0
  298. sage_wheels/share/ellcurves/rank15 +1 -0
  299. sage_wheels/share/ellcurves/rank17 +1 -0
  300. sage_wheels/share/ellcurves/rank19 +1 -0
  301. sage_wheels/share/ellcurves/rank2 +2388 -0
  302. sage_wheels/share/ellcurves/rank20 +1 -0
  303. sage_wheels/share/ellcurves/rank21 +1 -0
  304. sage_wheels/share/ellcurves/rank22 +1 -0
  305. sage_wheels/share/ellcurves/rank23 +1 -0
  306. sage_wheels/share/ellcurves/rank24 +1 -0
  307. sage_wheels/share/ellcurves/rank28 +1 -0
  308. sage_wheels/share/ellcurves/rank3 +836 -0
  309. sage_wheels/share/ellcurves/rank4 +10 -0
  310. sage_wheels/share/ellcurves/rank5 +5 -0
  311. sage_wheels/share/ellcurves/rank6 +5 -0
  312. sage_wheels/share/ellcurves/rank7 +5 -0
  313. sage_wheels/share/ellcurves/rank8 +6 -0
  314. sage_wheels/share/ellcurves/rank9 +7 -0
@@ -0,0 +1,1932 @@
1
+ # sage_setup: distribution = sagemath-schemes
2
+ # sage.doctest: needs sage.geometry.polyhedron sage.graphs sage.groups sage.rings.number_field
3
+ r"""
4
+ Zariski-Van Kampen method implementation
5
+
6
+ This file contains functions to compute the fundamental group of
7
+ the complement of a curve in the complex affine or projective plane,
8
+ using Zariski-Van Kampen approach. It depends on the package ``sirocco``.
9
+
10
+ The current implementation allows to compute a presentation of the
11
+ fundamental group of curves over the rationals or number fields with
12
+ a fixed embedding on `\QQbar`.
13
+
14
+ Instead of computing a representation of the braid monodromy, we
15
+ choose several base points and a system of paths joining them that
16
+ generate all the necessary loops around the points of the discriminant.
17
+ The group is generated by the free groups over these points, and
18
+ braids over these paths give relations between these generators.
19
+ This big group presentation is simplified at the end.
20
+
21
+ AUTHORS:
22
+
23
+ - Miguel Marco (2015-09-30): Initial version
24
+
25
+ EXAMPLES::
26
+
27
+ sage: # needs sirocco
28
+ sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy
29
+ sage: R.<x, y> = QQ[]
30
+ sage: f = y^3 + x^3 - 1
31
+ sage: braid_monodromy(f)
32
+ ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0}, {}, 3)
33
+ sage: fundamental_group(f)
34
+ Finitely presented group < x0 | >
35
+ """
36
+ # ****************************************************************************
37
+ # Copyright (C) 2015 Miguel Marco <mmarco@unizar.es>
38
+ #
39
+ # This program is free software: you can redistribute it and/or modify
40
+ # it under the terms of the GNU General Public License as published by
41
+ # the Free Software Foundation, either version 2 of the License, or
42
+ # (at your option) any later version.
43
+ # https://www.gnu.org/licenses/
44
+ # ****************************************************************************
45
+ import itertools
46
+
47
+ from copy import copy
48
+ from itertools import combinations
49
+
50
+ from sage.combinat.permutation import Permutation
51
+ from sage.functions.generalized import sign
52
+ from sage.geometry.voronoi_diagram import VoronoiDiagram
53
+ from sage.graphs.graph import Graph
54
+ from sage.groups.braid import BraidGroup
55
+ from sage.groups.free_group import FreeGroup
56
+ from sage.groups.perm_gps.permgroup_named import SymmetricGroup
57
+ from sage.matrix.constructor import matrix
58
+ from sage.misc.cachefunc import cached_function
59
+ from sage.misc.flatten import flatten
60
+ from sage.misc.lazy_import import lazy_import
61
+ from sage.misc.misc_c import prod
62
+ from sage.parallel.decorate import parallel
63
+ from sage.rings.complex_interval_field import ComplexIntervalField
64
+ from sage.rings.complex_mpfr import ComplexField
65
+ from sage.rings.integer_ring import ZZ
66
+ from sage.rings.number_field.number_field import NumberField
67
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
68
+ from sage.rings.qqbar import QQbar
69
+ from sage.rings.rational_field import QQ
70
+ from sage.rings.real_mpfr import RealField
71
+ from sage.schemes.curves.constructor import Curve
72
+
73
+ lazy_import('sage.libs.braiding', ['leftnormalform', 'rightnormalform'])
74
+
75
+ roots_interval_cache = {}
76
+
77
+
78
+ def braid_from_piecewise(strands):
79
+ r"""
80
+ Compute the braid corresponding to the piecewise linear curves strands.
81
+
82
+ INPUT:
83
+
84
+ - ``strands`` -- list of lists of tuples ``(t, c1, c2)``, where ``t``
85
+ is a number between 0 and 1, and ``c1`` and ``c2`` are rationals
86
+ or algebraic reals
87
+
88
+ OUTPUT: the braid formed by the piecewise linear strands
89
+
90
+ EXAMPLES::
91
+
92
+ sage: # needs sirocco
93
+ sage: from sage.schemes.curves.zariski_vankampen import braid_from_piecewise
94
+ sage: paths = [[(0, 0, 1), (0.2, -1, -0.5), (0.8, -1, 0), (1, 0, -1)],
95
+ ....: [(0, -1, 0), (0.5, 0, -1), (1, 1, 0)],
96
+ ....: [(0, 1, 0), (0.5, 1, 1), (1, 0, 1)]]
97
+ sage: braid_from_piecewise(paths)
98
+ s0*s1
99
+ """
100
+ L = strands
101
+ i = min(val[1][0] for val in L)
102
+ totalpoints = [[[a[0][1], a[0][2]]] for a in L]
103
+ indices = [1 for a in range(len(L))]
104
+ while i < 1:
105
+ for j, val in enumerate(L):
106
+ if val[indices[j]][0] > i:
107
+ xauxr = val[indices[j] - 1][1]
108
+ xauxi = val[indices[j] - 1][2]
109
+ yauxr = val[indices[j]][1]
110
+ yauxi = val[indices[j]][2]
111
+ aaux = val[indices[j] - 1][0]
112
+ baux = val[indices[j]][0]
113
+ interpolar = xauxr + (yauxr - xauxr)*(i - aaux) / (baux - aaux)
114
+ interpolai = xauxi + (yauxi - xauxi)*(i - aaux) / (baux - aaux)
115
+ totalpoints[j].append([interpolar, interpolai])
116
+ else:
117
+ totalpoints[j].append([val[indices[j]][1],
118
+ val[indices[j]][2]])
119
+ indices[j] = indices[j] + 1
120
+ i = min(val[indices[k]][0] for k, val in enumerate(L))
121
+
122
+ for j, val in enumerate(L):
123
+ totalpoints[j].append([val[-1][1], val[-1][2]])
124
+ braid = []
125
+ G = SymmetricGroup(len(totalpoints))
126
+
127
+ def sgn(x, y):
128
+ if x < y:
129
+ return 1
130
+ if x > y:
131
+ return -1
132
+ return 0
133
+
134
+ for i in range(len(totalpoints[0]) - 1):
135
+ l1 = [totalpoints[j][i] for j in range(len(L))]
136
+ l2 = [totalpoints[j][i + 1] for j in range(len(L))]
137
+ M = [[l1[s], l2[s]] for s in range(len(l1))]
138
+ M.sort()
139
+ l1 = [a[0] for a in M]
140
+ l2 = [a[1] for a in M]
141
+ cruces = []
142
+ for j, l2j in enumerate(l2):
143
+ l1j = l1[j]
144
+ for k in range(j):
145
+ if l2j < l2[k]:
146
+ t = (l1j[0] - l1[k][0]) / ((l2[k][0] - l2j[0]) + (l1j[0] - l1[k][0]))
147
+ s = sgn(l1[k][1] * (1 - t) + t * l2[k][1],
148
+ l1j[1] * (1 - t) + t * l2j[1])
149
+ cruces.append([t, k, j, s])
150
+ if cruces:
151
+ cruces.sort()
152
+ P = G(Permutation([]))
153
+ while cruces:
154
+ # we select the crosses in the same t
155
+ crucesl = [c for c in cruces if c[0] == cruces[0][0]]
156
+ crossesl = [(P(c[2] + 1) - P(c[1] + 1), c[1], c[2], c[3])
157
+ for c in crucesl]
158
+ cruces = cruces[len(crucesl):]
159
+ while crossesl:
160
+ crossesl.sort()
161
+ c = crossesl.pop(0)
162
+ braid.append(c[3] * min(map(P, [c[1] + 1, c[2] + 1])))
163
+ P = G(Permutation([(c[1] + 1, c[2] + 1)])) * P
164
+ crossesl = [(P(cr[2] + 1) - P(cr[1] + 1),
165
+ cr[1], cr[2], cr[3]) for cr in crossesl]
166
+
167
+ B = BraidGroup(len(L))
168
+ return B(braid)
169
+
170
+
171
+ def discrim(pols) -> tuple:
172
+ r"""
173
+ Return the points in the discriminant of the product of the polynomials
174
+ of a list or tuple ``pols``.
175
+
176
+ The result is the set of values of the first variable for which
177
+ two roots in the second variable coincide.
178
+
179
+ INPUT:
180
+
181
+ - ``pols`` -- list or tuple of polynomials in two variables with
182
+ coefficients in a number field with a fixed embedding in `\QQbar`
183
+
184
+ OUTPUT: a tuple with the roots of the discriminant in `\QQbar`
185
+
186
+ EXAMPLES::
187
+
188
+ sage: from sage.schemes.curves.zariski_vankampen import discrim
189
+ sage: R.<x, y> = QQ[]
190
+ sage: flist = (y^3 + x^3 - 1, 2 * x + y)
191
+ sage: sorted((discrim(flist)))
192
+ [-0.522757958574711?,
193
+ -0.500000000000000? - 0.866025403784439?*I,
194
+ -0.500000000000000? + 0.866025403784439?*I,
195
+ 0.2613789792873551? - 0.4527216721561923?*I,
196
+ 0.2613789792873551? + 0.4527216721561923?*I,
197
+ 1]
198
+ """
199
+ x, y = pols[0].parent().gens()
200
+ field = pols[0].base_ring()
201
+ pol_ring = PolynomialRing(field, (x,))
202
+
203
+ @parallel
204
+ def discrim_pairs(f, g):
205
+ if g is None:
206
+ return pol_ring(f.discriminant(y))
207
+ return pol_ring(f.resultant(g, y))
208
+
209
+ pairs = [(f, None) for f in pols] + [tuple(t) for t
210
+ in combinations(pols, 2)]
211
+ fdiscrim = discrim_pairs(pairs)
212
+ rts = ()
213
+ poly = 1
214
+ for u in fdiscrim:
215
+ h0 = u[1].radical()
216
+ h1 = h0 // h0.gcd(poly)
217
+ rts += tuple(h1.roots(QQbar, multiplicities=False))
218
+ poly = poly * h1
219
+ return rts
220
+
221
+
222
+ @cached_function
223
+ def corrected_voronoi_diagram(points):
224
+ r"""
225
+ Compute a Voronoi diagram of a set of points with rational coordinates.
226
+ The given points are granted to lie one in each bounded region.
227
+
228
+ INPUT:
229
+
230
+ - ``points`` -- tuple of complex numbers
231
+
232
+ OUTPUT:
233
+
234
+ A Voronoi diagram constructed from rational approximations of the points,
235
+ with the guarantee that each bounded region contains exactly one of the
236
+ input points.
237
+
238
+ EXAMPLES::
239
+
240
+ sage: from sage.schemes.curves.zariski_vankampen import corrected_voronoi_diagram
241
+ sage: points = (2, I, 0.000001, 0, 0.000001*I)
242
+ sage: V = corrected_voronoi_diagram(points)
243
+ sage: V
244
+ The Voronoi diagram of 9 points of dimension 2 in the Rational Field
245
+ sage: V.regions()
246
+ {P(-7, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices and 2 rays,
247
+ P(0, -7): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices and 2 rays,
248
+ P(0, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices,
249
+ P(0, 1): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 5 vertices,
250
+ P(0, 1/1000000): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices,
251
+ P(0, 7): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 2 rays,
252
+ P(1/1000000, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 5 vertices,
253
+ P(2, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 5 vertices,
254
+ P(7, 0): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 2 rays}
255
+ """
256
+ prec = 53
257
+ point_coordinates = [(p.real(), p.imag()) for p in points]
258
+ while True:
259
+ RF = RealField(prec)
260
+ apprpoints = {(QQ(RF(p[0])), QQ(RF(p[1]))): p
261
+ for p in point_coordinates}
262
+ added_points = 3 * max(map(abs, flatten(apprpoints))) + 1
263
+ configuration = list(apprpoints.keys()) + [(added_points, 0),
264
+ (-added_points, 0),
265
+ (0, added_points),
266
+ (0, -added_points)]
267
+ V = VoronoiDiagram(configuration)
268
+ valid = True
269
+ for r in V.regions().items():
270
+ if (not r[1].rays() and
271
+ not r[1].interior_contains(apprpoints[r[0].affine()])):
272
+ prec += 53
273
+ valid = False
274
+ break
275
+ if valid:
276
+ break
277
+ return V
278
+
279
+
280
+ def orient_circuit(circuit, convex=False, precision=53, verbose=False):
281
+ r"""
282
+ Reverse a circuit if it goes clockwise; otherwise leave it unchanged.
283
+
284
+ INPUT:
285
+
286
+ - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given
287
+ by a list of edges
288
+
289
+ - ``convex`` -- boolean (default: ``False``); if set to ``True`` a simpler
290
+ computation is made
291
+
292
+ - ``precision`` -- bits of precision (default: 53)
293
+
294
+ - ``verbose`` -- boolean (default: ``False``); for testing purposes
295
+
296
+ OUTPUT:
297
+
298
+ The same circuit if it goes counterclockwise, and its reversed otherwise,
299
+ given as the ordered list of vertices with identic extremities.
300
+
301
+ EXAMPLES::
302
+
303
+ sage: from sage.schemes.curves.zariski_vankampen import orient_circuit
304
+ sage: points = [(-4, 0), (4, 0), (0, 4), (0, -4), (0, 0)]
305
+ sage: V = VoronoiDiagram(points)
306
+ sage: E = Graph()
307
+ sage: for reg in V.regions().values():
308
+ ....: if reg.rays() or reg.lines():
309
+ ....: E = E.union(reg.vertex_graph())
310
+ sage: E.vertices(sort=True)
311
+ [A vertex at (-2, -2),
312
+ A vertex at (-2, 2),
313
+ A vertex at (2, -2),
314
+ A vertex at (2, 2)]
315
+ sage: cir = E.eulerian_circuit()
316
+ sage: cir
317
+ [(A vertex at (-2, -2), A vertex at (2, -2), None),
318
+ (A vertex at (2, -2), A vertex at (2, 2), None),
319
+ (A vertex at (2, 2), A vertex at (-2, 2), None),
320
+ (A vertex at (-2, 2), A vertex at (-2, -2), None)]
321
+ sage: cir_oriented = orient_circuit(cir); cir_oriented
322
+ (A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2),
323
+ A vertex at (-2, 2), A vertex at (-2, -2))
324
+ sage: cirinv = list(reversed([(c[1],c[0],c[2]) for c in cir]))
325
+ sage: cirinv
326
+ [(A vertex at (-2, -2), A vertex at (-2, 2), None),
327
+ (A vertex at (-2, 2), A vertex at (2, 2), None),
328
+ (A vertex at (2, 2), A vertex at (2, -2), None),
329
+ (A vertex at (2, -2), A vertex at (-2, -2), None)]
330
+ sage: orient_circuit(cirinv) == cir_oriented
331
+ True
332
+ sage: cir_oriented == orient_circuit(cir, convex=True)
333
+ True
334
+ sage: P0=[(1,1/2),(0,1),(1,1)]; P1=[(0,3/2),(-1,0)]
335
+ sage: Q=Polyhedron(P0).vertices()
336
+ sage: Q = [Q[2], Q[0], Q[1]] + [_ for _ in reversed(Polyhedron(P1).vertices())]
337
+ sage: Q
338
+ [A vertex at (1, 1/2), A vertex at (0, 1), A vertex at (1, 1),
339
+ A vertex at (0, 3/2), A vertex at (-1, 0)]
340
+ sage: E = Graph()
341
+ sage: for v, w in zip(Q, Q[1:] + [Q[0]]):
342
+ ....: E.add_edge((v, w))
343
+ sage: cir = orient_circuit(E.eulerian_circuit(), precision=1, verbose=True)
344
+ 2
345
+ sage: cir
346
+ (A vertex at (1, 1/2), A vertex at (0, 1), A vertex at (1, 1),
347
+ A vertex at (0, 3/2), A vertex at (-1, 0), A vertex at (1, 1/2))
348
+ """
349
+ vectors = [v[1].vector() - v[0].vector() for v in circuit]
350
+ circuit_vertex = (circuit[0][0],) + tuple(e[1] for e in circuit)
351
+ circuit_vertex = tuple(circuit_vertex)
352
+ if convex:
353
+ pr = matrix([vectors[0], vectors[1]]).determinant()
354
+ if pr > 0:
355
+ # return circuit
356
+ return circuit_vertex
357
+ elif pr < 0:
358
+ return tuple(reversed(circuit_vertex))
359
+ prec = precision
360
+ while True:
361
+ CIF = ComplexIntervalField(prec)
362
+ totalangle = sum((CIF(*vectors[i]) / CIF(*vectors[i - 1])).argument()
363
+ for i in range(len(vectors)))
364
+ if totalangle < 0:
365
+ return tuple(reversed(circuit_vertex))
366
+ if totalangle > 0:
367
+ return circuit_vertex
368
+ prec *= 2
369
+ if verbose:
370
+ print(prec)
371
+
372
+
373
+ def voronoi_cells(V, vertical_lines=frozenset()):
374
+ r"""
375
+ Compute the graph, the boundary graph, a base point, a positive orientation
376
+ of the boundary graph, and the dual graph of a corrected Voronoi diagram.
377
+
378
+ INPUT:
379
+
380
+ - ``V`` -- a corrected Voronoi diagram
381
+
382
+ - ``vertical_lines`` -- frozenset (default: ``frozenset()``); indices of the
383
+ vertical lines
384
+
385
+ OUTPUT:
386
+
387
+ - ``G`` -- the graph of the 1-skeleton of ``V``
388
+ - ``E`` -- the subgraph of the boundary
389
+ - ``p`` -- a vertex in ``E``
390
+ - ``EC`` -- list of vertices (representing a counterclockwise orientation
391
+ of ``E``) with identical first and last elements)
392
+ - ``DG`` -- the dual graph of ``V``, where the vertices are labelled
393
+ by the compact regions of ``V`` and the edges by their dual edges
394
+ - ``vertical_regions`` -- dictionary for the regions associated
395
+ with vertical lines
396
+
397
+ EXAMPLES::
398
+
399
+ sage: from sage.schemes.curves.zariski_vankampen import corrected_voronoi_diagram, voronoi_cells
400
+ sage: points = (2, I, 0.000001, 0, 0.000001*I)
401
+ sage: V = corrected_voronoi_diagram(points)
402
+ sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=frozenset((1,)))
403
+ sage: Gv = G.vertices(sort=True)
404
+ sage: Ge = G.edges(sort=True)
405
+ sage: len(Gv), len(Ge)
406
+ (12, 16)
407
+ sage: Ev = E.vertices(sort=True); Ev
408
+ [A vertex at (-4, 4),
409
+ A vertex at (-49000001/14000000, 1000001/2000000),
410
+ A vertex at (-7/2, -7/2),
411
+ A vertex at (-7/2, 1/2000000),
412
+ A vertex at (1/2000000, -7/2),
413
+ A vertex at (2000001/2000000, -24500001/7000000),
414
+ A vertex at (11/4, 4),
415
+ A vertex at (9/2, -9/2),
416
+ A vertex at (9/2, 9/2)]
417
+ sage: Ev.index(p)
418
+ 7
419
+ sage: EC
420
+ (A vertex at (9/2, -9/2),
421
+ A vertex at (9/2, 9/2),
422
+ A vertex at (11/4, 4),
423
+ A vertex at (-4, 4),
424
+ A vertex at (-49000001/14000000, 1000001/2000000),
425
+ A vertex at (-7/2, 1/2000000),
426
+ A vertex at (-7/2, -7/2),
427
+ A vertex at (1/2000000, -7/2),
428
+ A vertex at (2000001/2000000, -24500001/7000000),
429
+ A vertex at (9/2, -9/2))
430
+ sage: len(DG.vertices(sort=True)), len(DG.edges(sort=True))
431
+ (5, 7)
432
+ sage: edg = DG.edges(sort=True)[0]; edg
433
+ ((0,
434
+ (A vertex at (9/2, -9/2),
435
+ A vertex at (9/2, 9/2),
436
+ A vertex at (11/4, 4),
437
+ A vertex at (2000001/2000000, 500001/1000000),
438
+ A vertex at (2000001/2000000, -24500001/7000000),
439
+ A vertex at (9/2, -9/2))),
440
+ (1,
441
+ (A vertex at (-49000001/14000000, 1000001/2000000),
442
+ A vertex at (1000001/2000000, 1000001/2000000),
443
+ A vertex at (2000001/2000000, 500001/1000000),
444
+ A vertex at (11/4, 4),
445
+ A vertex at (-4, 4),
446
+ A vertex at (-49000001/14000000, 1000001/2000000))),
447
+ (A vertex at (2000001/2000000, 500001/1000000), A vertex at (11/4, 4), None))
448
+ sage: edg[-1] in Ge
449
+ True
450
+ sage: VR
451
+ {1: (A vertex at (-49000001/14000000, 1000001/2000000),
452
+ A vertex at (1000001/2000000, 1000001/2000000),
453
+ A vertex at (2000001/2000000, 500001/1000000),
454
+ A vertex at (11/4, 4),
455
+ A vertex at (-4, 4),
456
+ A vertex at (-49000001/14000000, 1000001/2000000))}
457
+ """
458
+ regions = V.regions()
459
+ points = [p for p in V.regions().keys() if V.regions()[p].is_compact()]
460
+ compact_regions = [regions[p] for p in points]
461
+ vertical_regions = {}
462
+ non_compact_regions = [reg for reg in V.regions().values()
463
+ if not reg.is_compact()]
464
+ G = Graph([u.vertices() for v in compact_regions for u in v.faces(1)],
465
+ format='list_of_edges')
466
+ E = Graph([u.vertices() for v in non_compact_regions for u in v.faces(1)
467
+ if u.is_compact()], format='list_of_edges')
468
+ p = next(E.vertex_iterator())
469
+ EC = orient_circuit(E.eulerian_circuit())
470
+ DG = Graph()
471
+ for i, reg in enumerate(compact_regions):
472
+ Greg0 = orient_circuit(reg.graph().eulerian_circuit(), convex=True)
473
+ if i in vertical_lines:
474
+ vertical_regions[i] = Greg0
475
+ DG.add_vertex((i, Greg0))
476
+ for e in G.edges(sort=True):
477
+ a, b = e[:2]
478
+ regs = [v for v in DG.vertices(sort=True) if a in v[1] and b in v[1]]
479
+ if len(regs) == 2:
480
+ DG.add_edge(regs[0], regs[1], e)
481
+ return (G, E, p, EC, DG, vertical_regions)
482
+
483
+
484
+ def followstrand(f, factors, x0, x1, y0a, prec=53) -> list:
485
+ r"""
486
+ Return a piecewise linear approximation of the homotopy continuation
487
+ of the root ``y0a`` from ``x0`` to ``x1``.
488
+
489
+ INPUT:
490
+
491
+ - ``f`` -- an irreducible polynomial in two variables
492
+ - ``factors`` -- list of irreducible polynomials in two variables
493
+ - ``x0`` -- a complex value, where the homotopy starts
494
+ - ``x1`` -- a complex value, where the homotopy ends
495
+ - ``y0a`` -- an approximate solution of the polynomial `F(y) = f(x_0, y)`
496
+ - ``prec`` -- the precision to use
497
+
498
+ OUTPUT:
499
+
500
+ A list of values `(t, y_{tr}, y_{ti})` such that:
501
+
502
+ - ``t`` is a real number between zero and one
503
+ - `f(t \cdot x_1 + (1-t) \cdot x_0, y_{tr} + I \cdot y_{ti})`
504
+ is zero (or a good enough approximation)
505
+ - the piecewise linear path determined by the points has a tubular
506
+ neighborhood where the actual homotopy continuation path lies, and
507
+ no other root of ``f``, nor any root of the polynomials in ``factors``,
508
+ intersects it.
509
+
510
+ EXAMPLES::
511
+
512
+ sage: # needs sirocco
513
+ sage: from sage.schemes.curves.zariski_vankampen import followstrand
514
+ sage: R.<x, y> = QQ[]
515
+ sage: f = x^2 + y^3
516
+ sage: x0 = CC(1, 0)
517
+ sage: x1 = CC(1, 0.5)
518
+ sage: followstrand(f, [], x0, x1, -1.0) # abs tol 1e-15
519
+ [(0.0, -1.0, 0.0),
520
+ (0.7500000000000001, -1.015090921153253, -0.24752813818386948),
521
+ (1.0, -1.026166099551513, -0.32768940253604323)]
522
+ sage: fup = f.subs({y: y - 1/10})
523
+ sage: fdown = f.subs({y: y + 1/10})
524
+ sage: followstrand(f, [fup, fdown], x0, x1, -1.0) # abs tol 1e-15
525
+ [(0.0, -1.0, 0.0),
526
+ (0.5303300858899107, -1.0076747107983448, -0.17588022709184917),
527
+ (0.7651655429449553, -1.015686131039112, -0.25243563967299404),
528
+ (1.0, -1.026166099551513, -0.3276894025360433)]
529
+ """
530
+ if f.degree() == 1:
531
+ CF = ComplexField(prec)
532
+ g = f.change_ring(CF)
533
+ (x, y) = g.parent().gens()
534
+ y0 = CF[y](g.subs({x: x0})).roots()[0][0]
535
+ y1 = CF[y](g.subs({x: x1})).roots()[0][0]
536
+ res = [(0.0, y0.real(), y0.imag()), (1.0, y1.real(), y1.imag())]
537
+ return res
538
+ CIF = ComplexIntervalField(prec)
539
+ CC = ComplexField(prec)
540
+ G = f.change_ring(QQbar).change_ring(CIF)
541
+ x, y = G.parent().gens()
542
+ g = G.subs({x: (1 - x) * CIF(x0) + x * CIF(x1)})
543
+ coefs = []
544
+ deg = g.total_degree()
545
+ for d in range(deg + 1):
546
+ for i in range(d + 1):
547
+ c = CIF(g.coefficient({x: d - i, y: i}))
548
+ cr = c.real()
549
+ ci = c.imag()
550
+ coefs += list(cr.endpoints())
551
+ coefs += list(ci.endpoints())
552
+ yr = CC(y0a).real()
553
+ yi = CC(y0a).imag()
554
+ coefsfactors = []
555
+ degsfactors = []
556
+ for fc in factors:
557
+ degfc = fc.degree()
558
+ degsfactors.append(degfc)
559
+ G = fc.change_ring(QQbar).change_ring(CIF)
560
+ g = G.subs({x: (1 - x) * CIF(x0) + x * CIF(x1)})
561
+ for d in range(degfc + 1):
562
+ for i in range(d + 1):
563
+ c = CIF(g.coefficient({x: d - i, y: i}))
564
+ cr = c.real()
565
+ ci = c.imag()
566
+ coefsfactors += list(cr.endpoints())
567
+ coefsfactors += list(ci.endpoints())
568
+ from sage.libs.sirocco import (contpath, contpath_mp, contpath_comps, contpath_mp_comps)
569
+ try:
570
+ if prec == 53:
571
+ if factors:
572
+ points = contpath_comps(deg, coefs, yr, yi, degsfactors, coefsfactors)
573
+ else:
574
+ points = contpath(deg, coefs, yr, yi)
575
+ else:
576
+ if factors:
577
+ points = contpath_mp_comps(deg, coefs, yr, yi, prec, degsfactors, coefsfactors)
578
+ else:
579
+ points = contpath_mp(deg, coefs, yr, yi, prec)
580
+ return points
581
+ except Exception:
582
+ return followstrand(f, factors, x0, x1, y0a, 2 * prec)
583
+
584
+
585
+ def newton(f, x0, i0):
586
+ r"""
587
+ Return the interval Newton operator.
588
+
589
+ INPUT:
590
+
591
+ - ``f`` -- a univariate polynomial
592
+ - ``x0`` -- a number
593
+ - ``I0`` -- an interval
594
+
595
+ OUTPUT:
596
+
597
+ The interval `x_0-\frac{f(x_0)}{f'(I_0)}`
598
+
599
+ EXAMPLES::
600
+
601
+ sage: from sage.schemes.curves.zariski_vankampen import newton
602
+ sage: R.<x> = QQbar[]
603
+ sage: f = x^3 + x
604
+ sage: x0 = 1/10
605
+ sage: I0 = RIF((-1/5,1/5))
606
+ sage: n = newton(f, x0, I0)
607
+ sage: n
608
+ 0.0?
609
+ sage: n.real().endpoints()
610
+ (-0.0147727272727274, 0.00982142857142862)
611
+ sage: n.imag().endpoints()
612
+ (0.000000000000000, -0.000000000000000)
613
+ """
614
+ return x0 - f(x0) / f.derivative()(i0)
615
+
616
+
617
+ def fieldI(field):
618
+ r"""
619
+ Return the (either double or trivial) extension of a number field which contains ``I``.
620
+
621
+ INPUT:
622
+
623
+ - ``field`` -- a number field with an embedding in `\QQbar`
624
+
625
+ OUTPUT: the extension ``F`` of ``field`` containing ``I`` with an embedding in `\QQbar`
626
+
627
+ EXAMPLES::
628
+
629
+ sage: from sage.schemes.curves.zariski_vankampen import fieldI
630
+ sage: x = polygen(QQ, 'x')
631
+ sage: p = QQ[x](x^5 + 2 * x + 1)
632
+ sage: a0 = p.roots(QQbar, multiplicities=False)[0]
633
+ sage: F0.<a> = NumberField(p, embedding=a0)
634
+ sage: fieldI(F0)
635
+ Number Field in prim with defining polynomial
636
+ x^10 + 5*x^8 + 14*x^6 - 2*x^5 - 10*x^4 + 20*x^3 - 11*x^2 - 14*x + 10
637
+ with prim = 0.4863890359345430? + 1.000000000000000?*I
638
+ sage: F0 = CyclotomicField(5)
639
+ sage: fieldI(F0)
640
+ Number Field in prim with defining polynomial
641
+ x^8 - 2*x^7 + 7*x^6 - 10*x^5 + 16*x^4 - 10*x^3 - 2*x^2 + 4*x + 1
642
+ with prim = -0.3090169943749474? + 0.04894348370484643?*I
643
+ sage: fieldI(QuadraticField(3))
644
+ Number Field in prim with defining polynomial x^4 - 4*x^2 + 16
645
+ with prim = -1.732050807568878? + 1.000000000000000?*I
646
+ sage: fieldI(QuadraticField(-3))
647
+ Number Field in prim with defining polynomial x^4 + 8*x^2 + 4
648
+ with prim = 0.?e-18 - 0.732050807568878?*I
649
+
650
+ If ``I`` is already in the field, the result is the field itself::
651
+
652
+ sage: from sage.schemes.curves.zariski_vankampen import fieldI
653
+ sage: p = QQ[x](x^4 + 1)
654
+ sage: a0 = p.roots(QQbar, multiplicities=False)[0]
655
+ sage: F0.<a> = NumberField(p, embedding=a0)
656
+ sage: F1 = fieldI(F0)
657
+ sage: F0 == F1
658
+ True
659
+ sage: QuadraticField(-1) == fieldI(QuadraticField(-1))
660
+ True
661
+ """
662
+ I0 = QQbar.gen()
663
+ if I0 in field:
664
+ return field
665
+ field_a = field[I0]
666
+ field_b = field_a.absolute_field('b0')
667
+ b0 = field_b.gen()
668
+ q = b0.minpoly()
669
+ qembd = field_b.embeddings(QQbar)
670
+ for h1 in qembd:
671
+ b1 = h1(b0)
672
+ b2 = h1(field_b(field_a.gen(0)))
673
+ b3 = QQbar(field.gen(0))
674
+ F1 = NumberField(q, 'prim', embedding=b1)
675
+ if b3 in F1 and b2.imag() > 0:
676
+ return F1
677
+
678
+
679
+ @parallel
680
+ def roots_interval(f, x0):
681
+ """
682
+ Find disjoint intervals that isolate the roots of a polynomial for a fixed
683
+ value of the first variable.
684
+
685
+ INPUT:
686
+
687
+ - ``f`` -- a bivariate squarefree polynomial
688
+ - ``x0`` -- a Gauss rational number corresponding to the first coordinate
689
+
690
+ The intervals are taken as big as possible to be able to detect when two
691
+ approximate roots of `f(x_0, y)` correspond to the same exact root, where
692
+ `f` is the product of the polynomials in `flist`.
693
+
694
+ The result is given as a dictionary, where the keys are
695
+ approximations to the roots with rational real and imaginary
696
+ parts, and the values are intervals containing them.
697
+
698
+ EXAMPLES::
699
+
700
+ sage: from sage.schemes.curves.zariski_vankampen import roots_interval, fieldI
701
+ sage: R.<x, y> = QQ[]
702
+ sage: K = fieldI(QQ)
703
+ sage: f = y^3 - x^2
704
+ sage: f = f.change_ring(K)
705
+ sage: ri = roots_interval(f, 1)
706
+ sage: ri
707
+ {-138907099/160396102*I - 1/2: -1.? - 1.?*I,
708
+ 138907099/160396102*I - 1/2: -1.? + 1.?*I,
709
+ 1: 1.? + 0.?*I}
710
+ sage: [r.endpoints() for r in ri.values()]
711
+ [(0.566987298107781 - 0.433012701892219*I,
712
+ 1.43301270189222 + 0.433012701892219*I,
713
+ 0.566987298107781 + 0.433012701892219*I,
714
+ 1.43301270189222 - 0.433012701892219*I),
715
+ (-0.933012701892219 - 1.29903810567666*I,
716
+ -0.0669872981077806 - 0.433012701892219*I,
717
+ -0.933012701892219 - 0.433012701892219*I,
718
+ -0.0669872981077806 - 1.29903810567666*I),
719
+ (-0.933012701892219 + 0.433012701892219*I,
720
+ -0.0669872981077806 + 1.29903810567666*I,
721
+ -0.933012701892219 + 1.29903810567666*I,
722
+ -0.0669872981077806 + 0.433012701892219*I)]
723
+ """
724
+ F1 = f.base_ring()
725
+ x, y = f.parent().gens()
726
+ fx = F1[y](f.subs({x: F1(x0)}))
727
+ roots = fx.roots(QQbar, multiplicities=False)
728
+ result = {}
729
+ for i, r in enumerate(roots):
730
+ prec = 53
731
+ IF = ComplexIntervalField(prec)
732
+ CF = ComplexField(prec)
733
+ divisor = 4
734
+ diam = min((CF(r) - CF(r0)).abs()
735
+ for r0 in roots[:i] + roots[i + 1:]) / divisor
736
+ envelop = IF(diam) * IF((-1, 1), (-1, 1))
737
+ while newton(fx, r, r + envelop) not in r + envelop:
738
+ prec += 53
739
+ IF = ComplexIntervalField(prec)
740
+ CF = ComplexField(prec)
741
+ divisor *= 2
742
+ diam = min((CF(r) - CF(r0)).abs()
743
+ for r0 in roots[:i] + roots[i + 1:]) / divisor
744
+ envelop = IF(diam) * IF((-1, 1), (-1, 1))
745
+ qapr = QQ(CF(r).real()) + QQbar.gen() * QQ(CF(r).imag())
746
+ if qapr not in r + envelop:
747
+ raise ValueError("could not approximate roots with exact values")
748
+ result[qapr] = r + envelop
749
+ return result
750
+
751
+
752
+ def roots_interval_cached(f, x0):
753
+ r"""
754
+ Cached version of :func:`roots_interval`.
755
+
756
+ TESTS::
757
+
758
+ sage: from sage.schemes.curves.zariski_vankampen import roots_interval, roots_interval_cached, roots_interval_cache, fieldI
759
+ sage: R.<x, y> = QQ[]
760
+ sage: K = fieldI(QQ)
761
+ sage: f = y^3 - x^2
762
+ sage: f = f.change_ring(K)
763
+ sage: (f, 1) in roots_interval_cache
764
+ False
765
+ sage: ri = roots_interval_cached(f, 1)
766
+ sage: ri
767
+ {-138907099/160396102*I - 1/2: -1.? - 1.?*I,
768
+ 138907099/160396102*I - 1/2: -1.? + 1.?*I,
769
+ 1: 1.? + 0.?*I}
770
+ sage: (f, 1) in roots_interval_cache
771
+ True
772
+ """
773
+ global roots_interval_cache
774
+ try:
775
+ return roots_interval_cache[(f, x0)]
776
+ except KeyError:
777
+ result = roots_interval(f, x0)
778
+ roots_interval_cache[(f, x0)] = result
779
+ return result
780
+
781
+
782
+ def populate_roots_interval_cache(inputs):
783
+ r"""
784
+ Call :func:`roots_interval` to the inputs that have not been
785
+ computed previously, and cache them.
786
+
787
+ INPUT:
788
+
789
+ - ``inputs`` -- list of tuples ``(f, x0)``
790
+
791
+ EXAMPLES::
792
+
793
+ sage: from sage.schemes.curves.zariski_vankampen import populate_roots_interval_cache, roots_interval_cache, fieldI
794
+ sage: R.<x,y> = QQ[]
795
+ sage: K=fieldI(QQ)
796
+ sage: f = y^5 - x^2
797
+ sage: f = f.change_ring(K)
798
+ sage: (f, 3) in roots_interval_cache
799
+ False
800
+ sage: populate_roots_interval_cache([(f, 3)])
801
+ sage: (f, 3) in roots_interval_cache
802
+ True
803
+ sage: roots_interval_cache[(f, 3)]
804
+ {-1.255469441943070? - 0.9121519421827974?*I: -2.? - 1.?*I,
805
+ -1.255469441943070? + 0.9121519421827974?*I: -2.? + 1.?*I,
806
+ 0.4795466549853897? - 1.475892845355996?*I: 1.? - 2.?*I,
807
+ 0.4795466549853897? + 1.475892845355996?*I: 1.? + 2.?*I,
808
+ 14421467174121563/9293107134194871: 2.? + 0.?*I}
809
+ """
810
+ global roots_interval_cache
811
+ tocompute = [inp for inp in inputs if inp not in roots_interval_cache]
812
+ problem_par = True
813
+ while problem_par: # hack to deal with random fails in parallelization
814
+ try:
815
+ result = roots_interval(tocompute)
816
+ for r in result:
817
+ roots_interval_cache[r[0][0]] = r[1]
818
+ problem_par = False
819
+ except TypeError:
820
+ pass
821
+
822
+
823
+ @parallel
824
+ def braid_in_segment(glist, x0, x1, precision={}):
825
+ """
826
+ Return the braid formed by the `y` roots of ``f`` when `x` moves
827
+ from ``x0`` to ``x1``.
828
+
829
+ INPUT:
830
+
831
+ - ``glist`` -- tuple of polynomials in two variables
832
+ - ``x0`` -- a Gauss rational
833
+ - ``x1`` -- a Gauss rational
834
+ - ``precision`` -- dictionary (default: `{}`) which assigns a number
835
+ precision bits to each element of ``glist``
836
+
837
+ OUTPUT: a braid
838
+
839
+ EXAMPLES::
840
+
841
+ sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment, fieldI
842
+ sage: R.<x, y> = QQ[]
843
+ sage: K = fieldI(QQ)
844
+ sage: f = x^2 + y^3
845
+ sage: f = f.change_ring(K)
846
+ sage: x0 = 1
847
+ sage: x1 = 1 + I / 2
848
+ sage: braid_in_segment(tuple(_[0] for _ in f.factor()), x0, x1) # needs sirocco
849
+ s1
850
+
851
+ TESTS:
852
+
853
+ Check that :issue:`26503` is fixed::
854
+
855
+ sage: # needs sage.rings.real_mpfr sage.symbolic
856
+ sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0]
857
+ sage: Kw.<wp> = NumberField(wp.minpoly(), embedding=wp)
858
+ sage: R.<x, y> = Kw[]
859
+ sage: z = -wp - 1
860
+ sage: f = y * (y + z) * x * (x - 1) * (x - y) * (x + z * y - 1) * (x + z * y + wp)
861
+ sage: from sage.schemes.curves.zariski_vankampen import fieldI, braid_in_segment
862
+ sage: Kw1 = fieldI(Kw)
863
+ sage: g = f.subs({x: x + 2 * y})
864
+ sage: g = g.change_ring(Kw1)
865
+ sage: p1 = QQbar(sqrt(-1/3))
866
+ sage: p1a = CC(p1)
867
+ sage: p1b = QQ(p1a.real()) + I*QQ(p1a.imag())
868
+ sage: p2 = QQbar(1/2 + sqrt(-1/3)/2)
869
+ sage: p2a = CC(p2)
870
+ sage: p2b = QQ(p2a.real()) + I*QQ(p2a.imag())
871
+ sage: glist = tuple([_[0] for _ in g.factor()])
872
+ sage: B = braid_in_segment(glist, p1b, p2b); B # needs sirocco
873
+ s5*s3^-1
874
+ """
875
+ precision1 = precision.copy()
876
+ g = prod(glist)
877
+ F1 = g.base_ring()
878
+ x, y = g.parent().gens()
879
+ intervals = {}
880
+ if not precision1:
881
+ precision1 = {f: 53 for f in glist}
882
+ y0s = []
883
+ for f in glist:
884
+ if f.variables() == (y,):
885
+ f0 = F1[y](f)
886
+ else:
887
+ f0 = F1[y](f.subs({x: F1(x0)}))
888
+ y0sf = f0.roots(QQbar, multiplicities=False)
889
+ y0s += list(y0sf)
890
+ while True:
891
+ CIFp = ComplexIntervalField(precision1[f])
892
+ intervals[f] = [r.interval(CIFp) for r in y0sf]
893
+ if not any(a.overlaps(b) for a, b in
894
+ itertools.combinations(intervals[f], 2)):
895
+ break
896
+ precision1[f] *= 2
897
+ strands = []
898
+ for f in glist:
899
+ for i in intervals[f]:
900
+ aux = followstrand(f, [p for p in glist if p != f],
901
+ x0, x1, i.center(), precision1[f])
902
+ strands.append(aux)
903
+ complexstrands = [[(QQ(a[0]), QQ(a[1]), QQ(a[2])) for a in b]
904
+ for b in strands]
905
+ centralbraid = braid_from_piecewise(complexstrands)
906
+ initialstrands = []
907
+ finalstrands = []
908
+ initialintervals = roots_interval_cached(g, x0)
909
+ finalintervals = roots_interval_cached(g, x1)
910
+ I1 = QQbar.gen()
911
+ for cs in complexstrands:
912
+ ip = cs[0][1] + I1 * cs[0][2]
913
+ fp = cs[-1][1] + I1 * cs[-1][2]
914
+ matched = 0
915
+ for center, interval in initialintervals.items():
916
+ if ip in interval:
917
+ initialstrands.append([(0, center.real(), center.imag()),
918
+ (1, cs[0][1], cs[0][2])])
919
+ matched += 1
920
+ if matched != 1:
921
+ precision1 = {f: precision1[f] * 2 for f in glist}
922
+ return braid_in_segment(glist, x0, x1, precision=precision1)
923
+
924
+ matched = 0
925
+ for center, interval in finalintervals.items():
926
+ if fp in interval:
927
+ finalstrands.append([(0, cs[-1][1], cs[-1][2]),
928
+ (1, center.real(), center.imag())])
929
+ matched += 1
930
+ if matched != 1:
931
+ precision1 = {f: precision1[f] * 2 for f in glist}
932
+ return braid_in_segment(glist, x0, x1, precision=precision1)
933
+ initialbraid = braid_from_piecewise(initialstrands)
934
+ finalbraid = braid_from_piecewise(finalstrands)
935
+
936
+ return initialbraid * centralbraid * finalbraid
937
+
938
+
939
+ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}) -> list:
940
+ r"""
941
+ Return a geometric basis, based on a vertex.
942
+
943
+ INPUT:
944
+
945
+ - ``G`` -- a graph with the bounded edges of a Voronoi Diagram
946
+
947
+ - ``E`` -- a subgraph of ``G`` which is a cycle containing the bounded
948
+ edges touching an unbounded region of a Voronoi Diagram
949
+
950
+ - ``EC0`` -- a counterclockwise orientation of the vertices of ``E``
951
+
952
+ - ``p`` -- a vertex of ``E``
953
+
954
+ - ``dual_graph`` -- a dual graph for a plane embedding of ``G`` such that
955
+ ``E`` is the boundary of the non-bounded component of the complement.
956
+ The edges are labelled as the dual edges and the vertices are labelled
957
+ by a tuple whose first element is the an integer for the position and the
958
+ second one is the cyclic ordered list of vertices in the region
959
+
960
+ - ``vertical_regions`` -- dictionary (default: `{}`); its keys are
961
+ the vertices of ``dual_graph`` to fix regions associated with
962
+ vertical lines
963
+
964
+ OUTPUT: a geometric basis and a dictionary
965
+
966
+ The geometric basis is formed by a list of sequences of paths. Each path is a
967
+ ist of vertices, that form a closed path in ``G``, based at ``p``, that goes
968
+ to a region, surrounds it, and comes back by the same path it came. The
969
+ concatenation of all these paths is equivalent to ``E``.
970
+
971
+ The dictionary associates to each vertical line the index of the generator
972
+ of the geometric basis associated to it.
973
+
974
+ EXAMPLES::
975
+
976
+ sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, corrected_voronoi_diagram, voronoi_cells
977
+ sage: points = (0, -1, I, 1, -I)
978
+ sage: V = corrected_voronoi_diagram(points)
979
+ sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=frozenset((0 .. 4)))
980
+ sage: gb, vd = geometric_basis(G, E, EC, p, DG, vertical_regions=VR)
981
+ sage: gb
982
+ [[A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (-5/2, 5/2),
983
+ A vertex at (-1/2, 1/2), A vertex at (-1/2, -1/2), A vertex at (1/2, -1/2),
984
+ A vertex at (1/2, 1/2), A vertex at (-1/2, 1/2), A vertex at (-5/2, 5/2),
985
+ A vertex at (5/2, 5/2), A vertex at (5/2, -5/2)],
986
+ [A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (-5/2, 5/2),
987
+ A vertex at (-1/2, 1/2), A vertex at (1/2, 1/2), A vertex at (5/2, 5/2),
988
+ A vertex at (5/2, -5/2)],
989
+ [A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (1/2, 1/2),
990
+ A vertex at (1/2, -1/2), A vertex at (5/2, -5/2)], [A vertex at (5/2, -5/2),
991
+ A vertex at (1/2, -1/2), A vertex at (-1/2, -1/2), A vertex at (-1/2, 1/2),
992
+ A vertex at (-5/2, 5/2), A vertex at (-5/2, -5/2), A vertex at (-1/2, -1/2),
993
+ A vertex at (1/2, -1/2), A vertex at (5/2, -5/2)],
994
+ [A vertex at (5/2, -5/2), A vertex at (1/2, -1/2), A vertex at (-1/2, -1/2),
995
+ A vertex at (-5/2, -5/2), A vertex at (5/2, -5/2)]]
996
+ sage: vd
997
+ {0: 0, 1: 3, 2: 1, 3: 2, 4: 4}
998
+ """
999
+ i = EC0.index(p)
1000
+ EC = EC0[i:-1] + EC0[:i + 1]
1001
+ # A counterclockwise eulerian circuit on the boundary,
1002
+ # starting and ending at p
1003
+ if G.size() == E.size():
1004
+ if E.is_cycle():
1005
+ j = next(dual_graph.vertex_iterator())[0]
1006
+ if j in vertical_regions:
1007
+ vd = {j: 0}
1008
+ else:
1009
+ vd = {}
1010
+ return [EC], vd
1011
+ edges_E = E.edges(sort=True)
1012
+ InternalEdges = [e for e in G.edges(sort=True) if e not in edges_E]
1013
+ InternalVertices = [v for e in InternalEdges for v in e[:2]]
1014
+ Internal = G.subgraph(vertices=InternalVertices, edges=InternalEdges)
1015
+ for i, ECi in enumerate(EC): # q and r are the points we will cut through
1016
+ if ECi in Internal:
1017
+ EI = [v for v in E if v in
1018
+ Internal.connected_component_containing_vertex(ECi, sort=True)
1019
+ and v != ECi]
1020
+ if EI:
1021
+ q = ECi
1022
+ connecting_path = list(EC[:i])
1023
+ break
1024
+ if EC[-i] in Internal:
1025
+ EI = [v for v in E if v in
1026
+ Internal.connected_component_containing_vertex(EC[-i], sort=True)
1027
+ and v != EC[-i]]
1028
+ if EI:
1029
+ q = EC[-i]
1030
+ connecting_path = list(reversed(EC[-i:]))
1031
+ break
1032
+ # Precompute distances from q in E and I
1033
+ E_dist_q = E.shortest_path_lengths(q)
1034
+ I_dist_q = Internal.shortest_path_lengths(q)
1035
+ distancequotients = [(E_dist_q[v]**2 / I_dist_q[v], v) for v in EI]
1036
+ r = max(distancequotients)[1]
1037
+ cutpath = Internal.shortest_path(q, r)
1038
+ for i, v in enumerate(cutpath):
1039
+ if i > 0 and v in EC:
1040
+ r = v
1041
+ cutpath = cutpath[:i+1]
1042
+ break
1043
+ qi = EC.index(q)
1044
+ ri = EC.index(r)
1045
+ Ecut = copy(E)
1046
+ Ecut.delete_vertices([q, r])
1047
+ subgraphs = Ecut.connected_components_subgraphs()
1048
+ if len(subgraphs) == 2:
1049
+ E1, E2 = subgraphs
1050
+ if EC[qi + 1] in E2:
1051
+ E1, E2 = E2, E1
1052
+ elif len(subgraphs) == 1:
1053
+ E1 = subgraphs[0]
1054
+ E2 = Graph()
1055
+ E2.add_edge(q, r, None)
1056
+ if r == EC[qi + 1]:
1057
+ E1, E2 = E2, E1
1058
+ for v in [q, r]:
1059
+ for n in E.neighbor_iterator(v):
1060
+ if n in E1 and n not in (q, r):
1061
+ E1.add_edge(v, n, None)
1062
+ if n in E2 and n not in (q, r):
1063
+ E2.add_edge(v, n, None)
1064
+
1065
+ for i in range(len(cutpath) - 1):
1066
+ E1.add_edge(cutpath[i], cutpath[i + 1], None)
1067
+ E2.add_edge(cutpath[i], cutpath[i + 1], None)
1068
+ Gd = copy(dual_graph)
1069
+ to_delete = [e for e in Gd.edges(sort=True) if e[2][0] in cutpath and
1070
+ e[2][1] in cutpath]
1071
+ Gd.delete_edges(to_delete)
1072
+ Gd1, Gd2 = Gd.connected_components_subgraphs()
1073
+ edges_2 = []
1074
+ vertices_2 = []
1075
+ for reg in Gd2.vertices(sort=True):
1076
+ vertices_2 += reg[1][:-1]
1077
+ reg_circuit = reg[1]
1078
+ edges_2 += [(v1, reg_circuit[i + 1])
1079
+ for i, v1 in enumerate(reg_circuit[:-1])]
1080
+ edges_2 += [(v1, reg_circuit[i - 1])
1081
+ for i, v1 in enumerate(reg_circuit[1:])]
1082
+ G2 = G.subgraph(vertices=vertices_2, edges=edges_2)
1083
+ edges_1 = []
1084
+ vertices_1 = []
1085
+ for reg in Gd1.vertices(sort=True):
1086
+ vertices_1 += reg[1]
1087
+ reg_circuit = reg[1] + (reg[1][0],)
1088
+ edges_1 += [(v1, reg_circuit[i + 1])
1089
+ for i, v1 in enumerate(reg_circuit[:-1])]
1090
+ edges_1 += [(v1, reg_circuit[i - 1])
1091
+ for i, v1 in enumerate(reg_circuit[1:])]
1092
+ G1 = G.subgraph(vertices=vertices_1, edges=edges_1)
1093
+ if EC[qi + 1] in G2:
1094
+ G1, G2 = G2, G1
1095
+ Gd1, Gd2 = Gd2, Gd1
1096
+
1097
+ if qi < ri:
1098
+ EC1 = [EC[j] for j in range(qi, ri)] + list(reversed(cutpath))
1099
+ EC2 = cutpath + list(EC[ri + 1: -1] + EC[: qi + 1])
1100
+ else:
1101
+ EC1 = list(EC[qi:] + EC[1:ri]) + list(reversed(cutpath))
1102
+ EC2 = cutpath + list(EC[ri + 1:qi + 1])
1103
+
1104
+ gb1, vd1 = geometric_basis(G1, E1, EC1, q, Gd1, vertical_regions=vertical_regions)
1105
+ gb2, vd2 = geometric_basis(G2, E2, EC2, q, Gd2, vertical_regions=vertical_regions)
1106
+
1107
+ vd = {j: vd1[j] for j in vd1}
1108
+ m = len(gb1)
1109
+ for j in vd2.keys():
1110
+ vd[j] = vd2[j] + m
1111
+ reverse_connecting = list(reversed(connecting_path))
1112
+ resul = [connecting_path + path + reverse_connecting
1113
+ for path in gb1 + gb2]
1114
+ for r in resul:
1115
+ i = 0
1116
+ while i < len(r) - 2:
1117
+ if r[i] == r[i + 2]:
1118
+ r.pop(i)
1119
+ r.pop(i)
1120
+ if i:
1121
+ i -= 1
1122
+ else:
1123
+ i += 1
1124
+ return (resul, vd)
1125
+
1126
+
1127
+ def vertical_lines_in_braidmon(pols) -> list:
1128
+ r"""
1129
+ Return the vertical lines in ``pols``, unless
1130
+ one of the other components has a vertical asymptote.
1131
+
1132
+ INPUT:
1133
+
1134
+ - ``pols`` -- a list of polynomials with two variables whose
1135
+ product equals ``f``
1136
+
1137
+ OUTPUT:
1138
+
1139
+ A list with the indices of the vertical lines in ``flist`` if there is
1140
+ no other component with vertical asymptote; otherwise it returns an empty
1141
+ list.
1142
+
1143
+ EXAMPLES::
1144
+
1145
+ sage: from sage.schemes.curves.zariski_vankampen import vertical_lines_in_braidmon
1146
+ sage: R.<x, y> = QQ[]
1147
+ sage: flist = [x^2 - y^3, x, x + 3 * y - 5, 1 - x]
1148
+ sage: vertical_lines_in_braidmon(flist)
1149
+ [1, 3]
1150
+ sage: flist += [x * y - 1]
1151
+ sage: vertical_lines_in_braidmon(flist)
1152
+ []
1153
+ sage: vertical_lines_in_braidmon([])
1154
+ []
1155
+ """
1156
+ if not pols:
1157
+ return []
1158
+ res = []
1159
+ for j, f in enumerate(pols):
1160
+ C = Curve(f)
1161
+ vertical_asymptote = C.has_vertical_asymptote()
1162
+ if vertical_asymptote:
1163
+ return []
1164
+ if C.is_vertical_line():
1165
+ res.append(j)
1166
+ return res
1167
+
1168
+
1169
+ def strand_components(f, pols, p1):
1170
+ r"""
1171
+ Compute only the assignment from strands to elements of ``flist``.
1172
+
1173
+ INPUT:
1174
+
1175
+ - ``f`` -- a reduced polynomial with two variables, over a number field
1176
+ with an embedding in the complex numbers
1177
+
1178
+ - ``pols`` -- a list of polynomials with two variables whose
1179
+ product equals ``f``
1180
+
1181
+ - ``p1`` -- a Gauss rational
1182
+
1183
+ OUTPUT:
1184
+
1185
+ - A list and a dictionary. The first one is an ordered list of pairs
1186
+ consisting of ``(z,i)`` where ``z`` is a root of ``f(p_1,y)``
1187
+ and `i` is the position of the polynomial in the list whose root
1188
+ is ``z``. The second one attaches a number `i` (strand) to a
1189
+ number `j` (a polynomial in the list).
1190
+
1191
+ EXAMPLES::
1192
+
1193
+ sage: from sage.schemes.curves.zariski_vankampen import strand_components
1194
+ sage: R.<x, y> = QQ[]
1195
+ sage: flist = [x^2 - y^3, x + 3 * y - 5]
1196
+ sage: strand_components(prod(flist), flist, 1)
1197
+ ([(-0.500000000000000? - 0.866025403784439?*I, 0),
1198
+ (-0.500000000000000? + 0.866025403784439?*I, 0),
1199
+ (1, 0), (1.333333333333334?, 1)], {0: 0, 1: 0, 2: 0, 3: 1})
1200
+ """
1201
+ x, y = f.parent().gens()
1202
+ F = pols[0].base_ring()
1203
+ strands = {}
1204
+ roots_base = []
1205
+ for i, h in enumerate(pols):
1206
+ h0 = h.subs({x: p1})
1207
+ h1 = F[y](h0)
1208
+ rt = h1.roots(QQbar, multiplicities=False)
1209
+ roots_base += [(r, i) for r in rt]
1210
+ roots_base.sort()
1211
+ strands = {i: par[1] for i, par in enumerate(roots_base)}
1212
+ return (roots_base, strands)
1213
+
1214
+
1215
+ def braid_monodromy(f, arrangement=(), vertical=False):
1216
+ r"""
1217
+ Compute the braid monodromy of a projection of the curve defined by
1218
+ a polynomial.
1219
+
1220
+ INPUT:
1221
+
1222
+ - ``f`` -- a polynomial with two variables, over a number field
1223
+ with an embedding in the complex numbers
1224
+
1225
+ - ``arrangement`` -- tuple (default: ``()``); an optional tuple
1226
+ of polynomials whose product equals ``f``
1227
+
1228
+ - ``vertical`` -- boolean (default: ``False``); if set to ``True``,
1229
+ ``arrangements`` contains more than one polynomial, some of them
1230
+ are of degree `1` in `x` and degree `0` in `y`, and none of
1231
+ the other components have vertical asymptotes, then these
1232
+ components are marked as *vertical* and not used for the computation
1233
+ of the braid monodromy. The other ones are marked as *horizontal*. If
1234
+ a vertical component does not pass through a singular points of the
1235
+ projection of the horizontal components a trivial braid is added
1236
+ to the list.
1237
+
1238
+ OUTPUT:
1239
+
1240
+ - A list of braids, images by the braid monodromy of a geometric
1241
+ basis of the complement of the discriminant of `f` in `\CC`.
1242
+
1243
+ - A dictionary: ``i``, index of a strand is sent to the index of
1244
+ the corresponding factor in ``arrangement``.
1245
+
1246
+ - Another dictionary ``dv``, only relevant if ``vertical`` is ``True``.
1247
+ If ``j`` is the index
1248
+ of a braid corresponding to a vertical line with index ``i``
1249
+ in ``arrangement``, then ``dv[j] = i``.
1250
+
1251
+ - A nonnegative integer: the number of strands of the braids,
1252
+ only necessary if the list of braids is empty.
1253
+
1254
+ .. NOTE::
1255
+
1256
+ The projection over the `x` axis is used if there are no vertical
1257
+ asymptotes. Otherwise, a linear change of variables is done to fall
1258
+ into the previous case except if the only vertical asymptotes are lines
1259
+ and ``vertical=True``.
1260
+
1261
+ EXAMPLES::
1262
+
1263
+ sage: # needs sirocco
1264
+ sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy
1265
+ sage: R.<x, y> = QQ[]
1266
+ sage: f = (x^2 - y^3) * (x + 3*y - 5)
1267
+ sage: bm = braid_monodromy(f); bm
1268
+ ([s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1,
1269
+ s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1,
1270
+ s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1,
1271
+ s1*s0*s2*s0^-1*s2*s1^-1], {0: 0, 1: 0, 2: 0, 3: 0}, {}, 4)
1272
+ sage: flist = (x^2 - y^3, x + 3*y - 5)
1273
+ sage: bm1 = braid_monodromy(f, arrangement=flist)
1274
+ sage: bm1[0] == bm[0]
1275
+ True
1276
+ sage: bm1[1]
1277
+ {0: 0, 1: 1, 2: 0, 3: 0}
1278
+ sage: braid_monodromy(R(1))
1279
+ ([], {}, {}, 0)
1280
+ sage: braid_monodromy(x*y^2 - 1)
1281
+ ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0}, {}, 3)
1282
+ sage: L = [x, y, x - 1, x -y]
1283
+ sage: braid_monodromy(prod(L), arrangement=L, vertical=True)
1284
+ ([s^2, 1], {0: 1, 1: 3}, {0: 0, 1: 2}, 2)
1285
+ """
1286
+ global roots_interval_cache
1287
+ F = fieldI(f.base_ring())
1288
+ I1 = F(QQbar.gen())
1289
+ f = f.change_ring(F)
1290
+ if not arrangement:
1291
+ arrangement1 = (f,)
1292
+ else:
1293
+ arrangement1 = tuple(g.change_ring(F) for g in arrangement)
1294
+ x, y = f.parent().gens()
1295
+ if vertical:
1296
+ indices_v = vertical_lines_in_braidmon(arrangement1)
1297
+ else:
1298
+ indices_v = []
1299
+ arrangement_h = tuple(f0 for j, f0 in enumerate(arrangement1)
1300
+ if j not in indices_v)
1301
+ arrangement_v = tuple(f0 for j, f0 in enumerate(arrangement1)
1302
+ if j in indices_v)
1303
+ glist = tuple(fc[0] for f0 in arrangement_h for fc in f0.factor())
1304
+ g = f.parent()(prod(glist))
1305
+ d = g.degree(y)
1306
+ if not arrangement_v: # change of coordinates only if indices_v is empty
1307
+ while g.coefficient(y**d) not in F:
1308
+ g = g.subs({x: x + y})
1309
+ d = g.degree(y)
1310
+ arrangement_h = tuple(f1.subs({x: x + y}) for f1 in arrangement_h)
1311
+ arrangement1 = arrangement_h
1312
+ glist = tuple(f1.subs({x: x + y}) for f1 in glist)
1313
+ if d > 0:
1314
+ disc = discrim(glist)
1315
+ else:
1316
+ disc = []
1317
+ vertical_braid = {}
1318
+ transversal = {}
1319
+ vl = []
1320
+ for f0 in arrangement_v:
1321
+ pt = [j for j, t in enumerate(disc) if f0.subs({x: t}) == 0]
1322
+ if pt:
1323
+ vertical_braid[f0] = (pt[0], arrangement1.index(f0))
1324
+ vl.append(pt[0])
1325
+ else:
1326
+ transversal[f0] = arrangement1.index(f0)
1327
+ vl.sort()
1328
+ vl = frozenset(vl)
1329
+ if not disc:
1330
+ vertical_braids = {i: transversal[f0]
1331
+ for i, f0 in enumerate(transversal)}
1332
+ if d > 1:
1333
+ result = [BraidGroup(d).one() for p in transversal]
1334
+ else:
1335
+ G = FreeGroup(0) / []
1336
+ result = [G.one() for p in transversal]
1337
+ p1 = F(0)
1338
+ if d > 0:
1339
+ roots_base, strands = strand_components(g, arrangement_h, p1)
1340
+ strands1 = {}
1341
+ for j in range(d):
1342
+ i = strands[j]
1343
+ k = arrangement1.index(arrangement_h[i])
1344
+ strands1[j] = k
1345
+ else:
1346
+ strands1 = {}
1347
+ return (result, strands1, vertical_braids, d)
1348
+ V = corrected_voronoi_diagram(tuple(disc))
1349
+ G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=vl)
1350
+ p0 = (p[0], p[1])
1351
+ p1 = p0[0] + I1 * p0[1]
1352
+ roots_base, strands = strand_components(g, arrangement_h, p1)
1353
+ strands1 = {}
1354
+ for j in range(d):
1355
+ i = strands[j]
1356
+ k = arrangement1.index(arrangement_h[i])
1357
+ strands1[j] = k
1358
+ geombasis, vd = geometric_basis(G, E, EC, p, DG, vertical_regions=VR)
1359
+ segs = set()
1360
+ for p in geombasis:
1361
+ for s in zip(p[:-1], p[1:]):
1362
+ if (s[1], s[0]) not in segs:
1363
+ segs.add((s[0], s[1]))
1364
+ I0 = QQbar.gen()
1365
+ segs = [(a[0] + I0 * a[1], b[0] + I0 * b[1]) for a, b in segs]
1366
+ vertices = list(set(flatten(segs)))
1367
+ tocacheverts = tuple([(g, v) for v in vertices])
1368
+ populate_roots_interval_cache(tocacheverts)
1369
+ end_braid_computation = False
1370
+ while not end_braid_computation:
1371
+ try:
1372
+ braidscomputed = (braid_in_segment([(glist, seg[0], seg[1])
1373
+ for seg in segs]))
1374
+ segsbraids = {}
1375
+ for braidcomputed in braidscomputed:
1376
+ seg = (braidcomputed[0][0][1], braidcomputed[0][0][2])
1377
+ beginseg = (QQ(seg[0].real()), QQ(seg[0].imag()))
1378
+ endseg = (QQ(seg[1].real()), QQ(seg[1].imag()))
1379
+ b = braidcomputed[1]
1380
+ segsbraids[(beginseg, endseg)] = b
1381
+ segsbraids[(endseg, beginseg)] = b.inverse()
1382
+ end_braid_computation = True
1383
+ except ChildProcessError: # hack to deal with random fails first time
1384
+ pass
1385
+ B = BraidGroup(d)
1386
+ result = []
1387
+ for path in geombasis:
1388
+ braidpath = B.one()
1389
+ for i in range(len(path) - 1):
1390
+ x0 = tuple(path[i].vector())
1391
+ x1 = tuple(path[i + 1].vector())
1392
+ braidpath = braidpath * segsbraids[(x0, x1)]
1393
+ result.append(braidpath)
1394
+ vertical_braids = {}
1395
+ r = len(result)
1396
+ t = 0
1397
+ for f0 in arrangement_v:
1398
+ if f0 in vertical_braid.keys():
1399
+ k, j = vertical_braid[f0]
1400
+ vertical_braids[vd[k]] = j
1401
+ else:
1402
+ vertical_braids[r + t] = transversal[f0]
1403
+ t += 1
1404
+ result.append(B.one())
1405
+ return (result, strands1, vertical_braids, d)
1406
+
1407
+
1408
+ def conjugate_positive_form(braid):
1409
+ r"""
1410
+ For a ``braid`` which is conjugate to a product of *disjoint* positive
1411
+ braids a list of such decompositions is given.
1412
+
1413
+ INPUT:
1414
+
1415
+ - ``braid`` -- a braid `\sigma`
1416
+
1417
+ OUTPUT:
1418
+
1419
+ A list of `r` lists. Each such list is another list with two elements, a
1420
+ positive braid `\alpha_i` and a list of permutation braids
1421
+ `\gamma_{1}^{i},\dots,\gamma_{n_i}^{i}` such that if
1422
+ `\gamma_i=\prod_{j=1}^{n_i} \gamma_j^i` then the braids
1423
+ `\tau_i=\gamma_i\alpha_i\gamma_i^{-1}` pairwise commute
1424
+ and `\alpha=\prod_{i=1}^{r} \tau_i`.
1425
+
1426
+ EXAMPLES::
1427
+
1428
+ sage: # needs sage.libs.braiding
1429
+ sage: from sage.schemes.curves.zariski_vankampen import conjugate_positive_form
1430
+ sage: B = BraidGroup(4)
1431
+ sage: t = B((1, 3, 2, -3, 1, 1))
1432
+ sage: cpf = conjugate_positive_form(t); cpf
1433
+ [[(s0*s1)^2, [s0*s2*s1*s0]]]
1434
+ sage: t == prod(prod(b) * a / prod(b) for a, b in cpf)
1435
+ True
1436
+ sage: B = BraidGroup(5)
1437
+ sage: t = B((1, 2, 3, 4, -1, -2, 3, 3, 2, -4))
1438
+ sage: L = conjugate_positive_form(t); L
1439
+ [[s0^2, [s0*s1*s2*s1*s3*s2*s1*s0]], [s3*s2, [s0*s1*s2*s1*s3*s2*s1*s0]]]
1440
+ sage: t == prod(prod(b) * a / prod(b) for a, b in L)
1441
+ True
1442
+ sage: s1 = B.gen(1)^3
1443
+ sage: conjugate_positive_form(s1)
1444
+ [[s1^3, []]]
1445
+ """
1446
+ B = braid.parent()
1447
+ d = B.strands()
1448
+ rnf = rightnormalform(braid)
1449
+ ex = rnf[-1][0]
1450
+ if ex >= 0:
1451
+ A1 = [B(a) for a in rnf[:-1]]
1452
+ braid1 = prod(A1, B.delta() ** ex)
1453
+ sg0 = B.one()
1454
+ else:
1455
+ braid1, sg0 = braid.super_summit_set_element()
1456
+ if ex > 0:
1457
+ blocks = [list(braid1.Tietze())]
1458
+ else:
1459
+ L1 = braid1.Tietze()
1460
+ gns = set(L1)
1461
+ cuts = [j for j in range(d + 1) if j not in gns]
1462
+ blocks = []
1463
+ for i in range(len(cuts) - 1):
1464
+ block = [j for j in L1 if cuts[i] < j < cuts[i + 1]]
1465
+ if block:
1466
+ blocks.append(block)
1467
+ shorts = []
1468
+ for a in blocks:
1469
+ if sg0 == B.one():
1470
+ res0 = [B(a), []]
1471
+ else:
1472
+ bra = sg0 * B(a) / sg0
1473
+ br1, sg = bra.super_summit_set_element()
1474
+ A1 = rightnormalform(sg)
1475
+ par = A1[-1][0] % 2
1476
+ A1 = [B(a0) for a0 in A1[:-1]]
1477
+ res = [br1, A1, par]
1478
+ if res[2]:
1479
+ r0 = res[0].Tietze()
1480
+ res[0] = B([d - i for i in r0])
1481
+ res0 = res[:2]
1482
+ shorts.append(res0)
1483
+ return shorts
1484
+
1485
+
1486
+ @parallel
1487
+ def conjugate_positive_form_p(braid):
1488
+ return conjugate_positive_form(braid)
1489
+
1490
+
1491
+ def braid2rels(L):
1492
+ r"""
1493
+ Return a minimal set of relations of the group
1494
+ ``F / [(b * F([j])) / F([j]) for j in (1..d)]`` where ``F = FreeGroup(d)``
1495
+ and ``b`` is a conjugate of a positive braid . One starts from the
1496
+ non-trivial relations determined by the positive braid and transform
1497
+ them in relations determined by ``b``.
1498
+
1499
+ INPUT:
1500
+
1501
+ - ``L`` -- tuple whose first element is a positive braid and the second
1502
+ element is a list of permutation braids
1503
+
1504
+ OUTPUT:
1505
+
1506
+ A list of Tietze words for a minimal set of relations of
1507
+ ``F / [(g * b) / g for g in F.gens()]``.
1508
+
1509
+ EXAMPLES::
1510
+
1511
+ sage: from sage.schemes.curves.zariski_vankampen import braid2rels
1512
+ sage: B.<s0, s1, s2> = BraidGroup(4)
1513
+ sage: L = ((s1*s0)^2, [s2])
1514
+ sage: braid2rels(L)
1515
+ [(4, 1, -2, -1), (2, -4, -2, 1)]
1516
+ """
1517
+ br = L[0]
1518
+ L1 = L[1]
1519
+ B = br.parent()
1520
+ d = B.strands()
1521
+ F = FreeGroup(d)
1522
+ T = br.Tietze()
1523
+ T1 = set(T)
1524
+ m = len(T1) + 1
1525
+ k = min(T1) - 1
1526
+ B0 = BraidGroup(m)
1527
+ F0 = FreeGroup(m)
1528
+ br0 = B0([j - k for j in T])
1529
+ br0_left = leftnormalform(br0)
1530
+ q, r = ZZ(br0_left[0][0]).quo_rem(2)
1531
+ br1 = B0.delta()**r * prod(map(B0, br0_left[1:]), B0.one())
1532
+ cox = prod(F0.gens())
1533
+ U0 = [cox**q * (f0 * br1) / cox**q / f0 for f0 in F0.gens()[:-1]]
1534
+ U = [tuple(sign(k1) * (abs(k1) + k) for k1 in br.Tietze()) for br in U0]
1535
+ pasos = [B.one()]
1536
+ pasos.extend(reversed(L1))
1537
+ for C in pasos:
1538
+ U = [(F(a) * C.inverse()).Tietze() for a in U]
1539
+ ga = F / U
1540
+ P = ga.gap().PresentationFpGroup()
1541
+ dic = P.TzOptions().sage()
1542
+ dic['protected'] = d
1543
+ dic['printLevel'] = 0
1544
+ P.SetTzOptions(dic)
1545
+ P.TzGoGo()
1546
+ P.TzGoGo()
1547
+ gb = P.FpGroupPresentation().sage()
1548
+ U = [rel.Tietze() for rel in gb.relations()]
1549
+ return U
1550
+
1551
+
1552
+ @parallel
1553
+ def braid2rels_p(L):
1554
+ return braid2rels(L)
1555
+
1556
+
1557
+ @parallel
1558
+ def relation(x, b):
1559
+ return x * b / x
1560
+
1561
+
1562
+ def fundamental_group_from_braid_mon(bm, degree=None,
1563
+ simplified=True, projective=False,
1564
+ puiseux=True, vertical=[]):
1565
+ r"""
1566
+ Return a presentation of the fundamental group computed from
1567
+ a braid monodromy.
1568
+
1569
+ INPUT:
1570
+
1571
+ - ``bm`` -- list of braids
1572
+
1573
+ - ``degree`` -- integer (default: ``None``); only needed if the braid
1574
+ monodromy is an empty list
1575
+
1576
+ - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the
1577
+ presentation will be simplified (see below)
1578
+
1579
+ - ``projective`` -- boolean (default: ``False``); if set to ``True``,
1580
+ the fundamental group of the complement of the projective completion
1581
+ of the curve will be computed, otherwise, the fundamental group of
1582
+ the complement in the affine plane will be computed
1583
+
1584
+ - ``puiseux`` -- boolean (default: ``True``); if set to ``True``
1585
+ a presentation of the fundamental group with the homotopy type
1586
+ of the complement of the affine curve will be computed, adding
1587
+ one relation if ``projective`` is set to ``True``.
1588
+
1589
+ - ``vertical`` -- list of integers (default: ``[]``); the indices in
1590
+ ``[0 .. r - 1]`` of the braids that surround a vertical line
1591
+
1592
+ If ``projective`` is ``False`` and ``puiseux`` is
1593
+ ``True``, a Zariski-VanKampen presentation is returned.
1594
+
1595
+ OUTPUT:
1596
+
1597
+ A presentation of the fundamental group of the complement of the
1598
+ union of the curve with some vertical lines from its braid monodromy.
1599
+
1600
+ EXAMPLES::
1601
+
1602
+ sage: from sage.schemes.curves.zariski_vankampen import fundamental_group_from_braid_mon
1603
+ sage: B.<s0, s1, s2> = BraidGroup(4)
1604
+ sage: bm = [s1*s2*s0*s1*s0^-1*s1^-1*s0^-1,
1605
+ ....: s0*s1^2*s0*s2*s1*(s0^-1*s1^-1)^2*s0^-1,
1606
+ ....: (s0*s1)^2]
1607
+ sage: g = fundamental_group_from_braid_mon(bm, projective=True) # needs sirocco
1608
+ sage: g.sorted_presentation() # needs sirocco
1609
+ Finitely presented group
1610
+ < x0, x1 | x1^-2*x0^-2, x1^-1*(x0^-1*x1)^2*x0 >
1611
+ sage: print(g.order(), g.abelian_invariants()) # needs sirocco
1612
+ 12 (4,)
1613
+ sage: B2 = BraidGroup(2)
1614
+ sage: bm = [B2(3 * [1])]
1615
+ sage: g = fundamental_group_from_braid_mon(bm, vertical=[0]); g # needs sirocco
1616
+ Finitely presented group
1617
+ < x0, x1, x2 | x2*x0*x1*x2^-1*x1^-1*x0^-1,
1618
+ x2*x0*x1*x0*x1^-1*x0^-1*x2^-1*x1^-1 >
1619
+ sage: fundamental_group_from_braid_mon([]) is None # needs sirocco
1620
+ True
1621
+ sage: fundamental_group_from_braid_mon([], degree=2) # needs sirocco
1622
+ Finitely presented group < x0, x1 | >
1623
+ sage: fundamental_group_from_braid_mon([SymmetricGroup(1).one()]) # needs sirocco
1624
+ Finitely presented group < x | >
1625
+ """
1626
+ vertical0 = sorted(vertical)
1627
+ v = len(vertical0)
1628
+ if not bm:
1629
+ d = degree
1630
+ elif bm[0].parent().order() == 1:
1631
+ d = 1
1632
+ else:
1633
+ d = bm[0].parent().strands()
1634
+ if d is None:
1635
+ return None
1636
+ F = FreeGroup(d)
1637
+ Fv = FreeGroup(d + v)
1638
+ if d == 0:
1639
+ return Fv / []
1640
+ if d == 1:
1641
+ return Fv / [(1, j, -1, -j) for j in range(2, d + v + 1)]
1642
+ bmh = [br for j, br in enumerate(bm) if j not in vertical0]
1643
+ if not puiseux:
1644
+ relations_h = (relation([(x, b) for x in F.gens() for b in bmh]))
1645
+ rel_h = [r[1] for r in relations_h]
1646
+ else:
1647
+ conjugate_desc = conjugate_positive_form_p(bmh)
1648
+ trenzas_desc = [b1[-1] for b1 in conjugate_desc]
1649
+ trenzas_desc_1 = flatten(trenzas_desc, max_level=1)
1650
+ relations_h = braid2rels_p(trenzas_desc_1)
1651
+ rel_h = [r[1] for r in relations_h]
1652
+ rel_h = flatten(rel_h, max_level=1)
1653
+ rel_v = []
1654
+ for j, k in enumerate(vertical0):
1655
+ l1 = d + j + 1
1656
+ br = bm[k]
1657
+ for gen in F.gens():
1658
+ j0 = gen.Tietze()[0]
1659
+ rl = (l1,) + (gen * br).Tietze() + (-l1, -j0)
1660
+ rel_v.append(rl)
1661
+ rel = rel_h + rel_v
1662
+ if projective:
1663
+ rel.append(prod(Fv.gens()).Tietze())
1664
+ G = Fv / rel
1665
+ if simplified:
1666
+ return G.simplified()
1667
+ return G
1668
+
1669
+
1670
+ def fundamental_group(f, simplified=True, projective=False, puiseux=True):
1671
+ r"""
1672
+ Return a presentation of the fundamental group of the complement of
1673
+ the algebraic set defined by the polynomial ``f``.
1674
+
1675
+ INPUT:
1676
+
1677
+ - ``f`` -- a polynomial in two variables, with coefficients in either
1678
+ the rationals or a number field with a fixed embedding in `\QQbar`
1679
+
1680
+ - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the
1681
+ presentation will be simplified (see below)
1682
+
1683
+ - ``projective`` -- boolean (default: ``False``); if set to ``True``,
1684
+ the fundamental group of the complement of the projective completion
1685
+ of the curve will be computed, otherwise, the fundamental group of
1686
+ the complement in the affine plane will be computed
1687
+
1688
+ - ``puiseux`` -- boolean (default: ``True``); if set to ``True``,
1689
+ a presentation of the fundamental group with the homotopy type
1690
+ of the complement of the affine curve is computed. If the Euler
1691
+ characteristic does not match, the homotopy type is obtained
1692
+ with a wedge of 2-spheres. One relation is added if ``projective``
1693
+ is set to ``True``.
1694
+
1695
+ If ``projective`` is ``False`` and ``puiseux`` is
1696
+ ``True``, a Zariski-VanKampen presentation is returned.
1697
+
1698
+ OUTPUT:
1699
+
1700
+ A presentation of the fundamental group of the complement of the
1701
+ curve defined by ``f``.
1702
+
1703
+ EXAMPLES::
1704
+
1705
+ sage: # needs sirocco
1706
+ sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy
1707
+ sage: R.<x, y> = QQ[]
1708
+ sage: f = x^2 + y^3
1709
+ sage: fundamental_group(f).sorted_presentation()
1710
+ Finitely presented group < x0, x1 | x1^-1*x0^-1*x1^-1*x0*x1*x0 >
1711
+ sage: fundamental_group(f, simplified=False, puiseux=False).sorted_presentation()
1712
+ Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x0*x1,
1713
+ x2^-1*x0*x1*x0^-1,
1714
+ x1^-1*x0^-1*x1^-1*x0*x1*x0 >
1715
+ sage: fundamental_group(f, projective=True)
1716
+ Finitely presented group < x0 | x0^3 >
1717
+ sage: fundamental_group(f).sorted_presentation()
1718
+ Finitely presented group < x0, x1 | x1^-1*x0^-1*x1^-1*x0*x1*x0 >
1719
+
1720
+ ::
1721
+
1722
+ sage: # needs sirocco
1723
+ sage: from sage.schemes.curves.zariski_vankampen import fundamental_group
1724
+ sage: R.<x, y> = QQ[]
1725
+ sage: f = y^3 + x^3
1726
+ sage: fundamental_group(f).sorted_presentation()
1727
+ Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x0^-1*x2*x0*x1,
1728
+ x2^-1*x1^-1*x2*x0*x1*x0^-1 >
1729
+
1730
+ It is also possible to have coefficients in a number field with a
1731
+ fixed embedding in `\QQbar`::
1732
+
1733
+ sage: from sage.schemes.curves.zariski_vankampen import fundamental_group
1734
+ sage: zeta = QQbar['x']('x^2 + x+ 1').roots(multiplicities=False)[0]
1735
+ sage: zeta
1736
+ -0.50000000000000000? - 0.866025403784439?*I
1737
+ sage: F = NumberField(zeta.minpoly(), 'zeta', embedding=zeta)
1738
+ sage: F.inject_variables()
1739
+ Defining zeta
1740
+ sage: R.<x, y> = F[]
1741
+ sage: f = y^3 + x^3 + zeta * x + 1
1742
+ sage: fundamental_group(f) # needs sirocco
1743
+ Finitely presented group < x0 | >
1744
+
1745
+ We compute the fundamental group of the complement of a
1746
+ quartic using the ``puiseux`` option::
1747
+
1748
+ sage: # optional - sirocco
1749
+ sage: from sage.schemes.curves.zariski_vankampen import fundamental_group
1750
+ sage: R.<x, y> = QQ[]
1751
+ sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1)
1752
+ sage: g = fundamental_group(f); g.sorted_presentation()
1753
+ Finitely presented group < x0, x1 | x1^-2*x0^2, (x1^-1*x0)^3 >
1754
+ sage: g = fundamental_group(f, projective=True)
1755
+ sage: g.order(), g.abelian_invariants()
1756
+ (12, (4,))
1757
+ sage: fundamental_group(y * (y - 1))
1758
+ Finitely presented group < x0, x1 | >
1759
+ """
1760
+ g = f
1761
+ x, y = g.parent().gens()
1762
+ F = g.parent().base_ring()
1763
+ d = g.degree(y)
1764
+ while g.coefficient(y**d) not in F:
1765
+ g = g.subs({x: x + y})
1766
+ d = g.degree(y)
1767
+ if projective:
1768
+ while g.degree(y) < g.degree():
1769
+ g = g.subs({x: x + y})
1770
+ bm = braid_monodromy(g)[0]
1771
+ if not bm:
1772
+ d = g.degree(y)
1773
+ else:
1774
+ d = bm[0].parent().strands()
1775
+ return fundamental_group_from_braid_mon(bm, degree=d,
1776
+ simplified=simplified,
1777
+ projective=projective,
1778
+ puiseux=puiseux)
1779
+
1780
+
1781
+ def fundamental_group_arrangement(flist, simplified=True, projective=False,
1782
+ puiseux=True, vertical=False,
1783
+ braid_data=None):
1784
+ r"""
1785
+ Compute the fundamental group of the complement of a curve
1786
+ defined by a list of polynomials with the extra information
1787
+ about the correspondence of the generators
1788
+ and meridians of the elements of the list.
1789
+
1790
+ INPUT:
1791
+
1792
+ - ``flist`` -- a tuple of polynomial with two variables, over a number
1793
+ field with an embedding in the complex numbers
1794
+
1795
+ - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the
1796
+ presentation will be simplified (see below)
1797
+
1798
+ - ``projective`` -- boolean (default: ``False``); if set to ``True``,
1799
+ the fundamental group of the complement of the projective completion
1800
+ of the curve will be computed, otherwise, the fundamental group of
1801
+ the complement in the affine plane will be computed
1802
+
1803
+ - ``puiseux`` -- boolean (default: ``True``); if set to ``True``
1804
+ a presentation of the fundamental group with the homotopy type
1805
+ of the complement of the affine curve will be computed, adding
1806
+ one relation if ``projective`` is set to ``True``.
1807
+
1808
+ - ``vertical`` -- boolean (default: ``False``); if set to ``True``,
1809
+ whenever no curve has vertical asymptotes the computation of braid
1810
+ monodromy is simpler if some lines are vertical
1811
+
1812
+ - ``braid_data`` -- tuple (default: ``None``); if it is not the default
1813
+ it is the output of ``fundamental_group_from_braid_mon`` previously
1814
+ computed
1815
+
1816
+ OUTPUT:
1817
+
1818
+ - A list of braids. The braids correspond to paths based in the same point;
1819
+ each of this paths is the conjugated of a loop around one of the points
1820
+ in the discriminant of the projection of ``f``.
1821
+
1822
+ - A dictionary attaching to ``j`` a tuple a list of elements
1823
+ of the group which are meridians of the curve in position ``j``.
1824
+ If ``projective`` is ``False`` and the `y`-degree of the horizontal
1825
+ components coincide with the total degree, another key is added
1826
+ to give a meridian of the line at infinity.
1827
+
1828
+ EXAMPLES::
1829
+
1830
+ sage: # needs sirocco
1831
+ sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy
1832
+ sage: from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement
1833
+ sage: R.<x, y> = QQ[]
1834
+ sage: flist = [x^2 - y^3, x + 3 * y - 5]
1835
+ sage: g, dic = fundamental_group_arrangement(flist)
1836
+ sage: g.sorted_presentation()
1837
+ Finitely presented group
1838
+ < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2^-1*x0*x2*x0,
1839
+ x1^-1*x0^-1*x1*x0 >
1840
+ sage: dic
1841
+ {0: [x0, x2], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]}
1842
+ sage: g, dic = fundamental_group_arrangement(flist, simplified=False, puiseux=False)
1843
+ sage: g.sorted_presentation(), dic
1844
+ (Finitely presented group
1845
+ < x0, x1, x2, x3 | 1, 1, 1, 1, 1, 1, 1,
1846
+ x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2,
1847
+ x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x3*x2,
1848
+ x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1*x2,
1849
+ x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2,
1850
+ x3^-1*x1^-1*x0*x1,
1851
+ x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0,
1852
+ x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0 >,
1853
+ {0: [x0, x2, x3], 1: [x1], 2: [x3^-1*x2^-1*x1^-1*x0^-1]})
1854
+ sage: fundamental_group_arrangement(flist, projective=True)
1855
+ (Finitely presented group < x | >, {0: [x], 1: [x^-3]})
1856
+ sage: fundamental_group_arrangement([])
1857
+ (Finitely presented group < | >, {})
1858
+ sage: g, dic = fundamental_group_arrangement([x * y])
1859
+ sage: g.sorted_presentation(), dic
1860
+ (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >,
1861
+ {0: [x0, x1], 1: [x1^-1*x0^-1]})
1862
+ sage: fundamental_group_arrangement([y + x^2])
1863
+ (Finitely presented group < x | >, {0: [x]})
1864
+ sage: fundamental_group_arrangement([y^2 + x], projective=True)
1865
+ (Finitely presented group < x | x^2 >, {0: [x]})
1866
+ sage: L = [x, y, x - 1, x -y]
1867
+ sage: G, dic =fundamental_group_arrangement(L)
1868
+ sage: G.sorted_presentation()
1869
+ Finitely presented group
1870
+ < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x0^-1*x1*x3*x0,
1871
+ x3^-1*x1^-1*x0^-1*x3*x0*x1, x2^-1*x0^-1*x2*x0 >
1872
+ sage: dic
1873
+ {0: [x1], 1: [x3], 2: [x2], 3: [x0], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}
1874
+ sage: fundamental_group_arrangement(L, vertical=True)
1875
+ (Finitely presented group
1876
+ < x0, x1, x2, x3 | x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1,
1877
+ x1*x2*x0*x2^-1*x1^-1*x0^-1,
1878
+ x1*x2*x0*x1^-1*x0^-1*x2^-1 >,
1879
+ {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]})
1880
+ """
1881
+ if flist:
1882
+ f = prod(flist)
1883
+ R = f.parent()
1884
+ else:
1885
+ R = PolynomialRing(QQ, ('x', 'y'))
1886
+ f = R(1)
1887
+ x, y = R.gens()
1888
+ flist1 = tuple(flist)
1889
+ if vertical and vertical_lines_in_braidmon(flist1):
1890
+ infinity = all(Curve(g).is_vertical_line() or
1891
+ g.degree(y) == g.degree() for g in flist1)
1892
+ else:
1893
+ infinity = any(Curve(g).has_vertical_asymptote() or
1894
+ Curve(g).is_vertical_line() for g in flist1)
1895
+ if not infinity:
1896
+ infinity = all(g.degree(y) == g.degree() for g in flist1)
1897
+ if braid_data:
1898
+ bm, dic, dv, d1 = braid_data
1899
+ elif not flist:
1900
+ bm = []
1901
+ dic = {}
1902
+ dv = {j: j for j, f in flist1}
1903
+ d1 = 0
1904
+ else:
1905
+ bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical)
1906
+ vert_lines = list(dv)
1907
+ vert_lines.sort()
1908
+ for i, j in enumerate(vert_lines):
1909
+ dic[d1 + i] = dv[j]
1910
+ g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False,
1911
+ projective=projective,
1912
+ puiseux=puiseux,
1913
+ vertical=vert_lines)
1914
+ if simplified:
1915
+ hom = g.simplification_isomorphism()
1916
+ else:
1917
+ hom = g.hom(codomain=g, im_gens=list(g.gens()), check=False)
1918
+ g1 = hom.codomain()
1919
+ if not flist:
1920
+ return (g1, {})
1921
+ dic1 = {}
1922
+ for i in range(len(flist1)):
1923
+ L = [j1 for j1 in dic if dic[j1] == i]
1924
+ dic1[i] = [hom(g.gen(j)) for j in L]
1925
+ if not projective and infinity:
1926
+ t = prod(hom(a) for a in g.gens()).inverse()
1927
+ dic1[len(flist1)] = [t]
1928
+ n = g1.ngens()
1929
+ rels = [rel.Tietze() for rel in g1.relations()]
1930
+ g1 = FreeGroup(n) / rels
1931
+ dic1 = {i: list({g1(el.Tietze()) for el in dic1[i]}) for i in dic1}
1932
+ return (g1, dic1)