passagemath-schemes 10.6.47__cp312-cp312-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.
- passagemath_schemes/.dylibs/libflint.22.0.dylib +0 -0
- passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
- passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
- passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
- passagemath_schemes/__init__.py +3 -0
- passagemath_schemes-10.6.47.dist-info/METADATA +204 -0
- passagemath_schemes-10.6.47.dist-info/METADATA.bak +205 -0
- passagemath_schemes-10.6.47.dist-info/RECORD +311 -0
- passagemath_schemes-10.6.47.dist-info/WHEEL +6 -0
- passagemath_schemes-10.6.47.dist-info/top_level.txt +3 -0
- sage/all__sagemath_schemes.py +23 -0
- sage/databases/all__sagemath_schemes.py +7 -0
- sage/databases/cremona.py +1723 -0
- sage/dynamics/all__sagemath_schemes.py +2 -0
- sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
- sage/dynamics/arithmetic_dynamics/all.py +14 -0
- sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
- sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
- sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
- sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
- sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
- sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
- sage/dynamics/arithmetic_dynamics/projective_ds.py +9558 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-312-darwin.so +0 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
- sage/dynamics/arithmetic_dynamics/wehlerK3.py +2576 -0
- sage/lfunctions/all.py +18 -0
- sage/lfunctions/dokchitser.py +745 -0
- sage/lfunctions/pari.py +818 -0
- sage/lfunctions/zero_sums.cpython-312-darwin.so +0 -0
- sage/lfunctions/zero_sums.pyx +1847 -0
- sage/modular/abvar/abvar.py +5135 -0
- sage/modular/abvar/abvar_ambient_jacobian.py +413 -0
- sage/modular/abvar/abvar_newform.py +244 -0
- sage/modular/abvar/all.py +8 -0
- sage/modular/abvar/constructor.py +186 -0
- sage/modular/abvar/cuspidal_subgroup.py +371 -0
- sage/modular/abvar/finite_subgroup.py +896 -0
- sage/modular/abvar/homology.py +720 -0
- sage/modular/abvar/homspace.py +998 -0
- sage/modular/abvar/lseries.py +415 -0
- sage/modular/abvar/morphism.py +935 -0
- sage/modular/abvar/torsion_point.py +274 -0
- sage/modular/abvar/torsion_subgroup.py +740 -0
- sage/modular/all.py +43 -0
- sage/modular/arithgroup/all.py +20 -0
- sage/modular/arithgroup/arithgroup_element.cpython-312-darwin.so +0 -0
- sage/modular/arithgroup/arithgroup_element.pyx +474 -0
- sage/modular/arithgroup/arithgroup_generic.py +1402 -0
- sage/modular/arithgroup/arithgroup_perm.py +2692 -0
- sage/modular/arithgroup/congroup.cpython-312-darwin.so +0 -0
- sage/modular/arithgroup/congroup.pyx +334 -0
- sage/modular/arithgroup/congroup_gamma.py +363 -0
- sage/modular/arithgroup/congroup_gamma0.py +692 -0
- sage/modular/arithgroup/congroup_gamma1.py +653 -0
- sage/modular/arithgroup/congroup_gammaH.py +1469 -0
- sage/modular/arithgroup/congroup_generic.py +628 -0
- sage/modular/arithgroup/congroup_sl2z.py +267 -0
- sage/modular/arithgroup/farey_symbol.cpython-312-darwin.so +0 -0
- sage/modular/arithgroup/farey_symbol.pyx +1066 -0
- sage/modular/arithgroup/tests.py +418 -0
- sage/modular/btquotients/all.py +4 -0
- sage/modular/btquotients/btquotient.py +3753 -0
- sage/modular/btquotients/pautomorphicform.py +2570 -0
- sage/modular/buzzard.py +100 -0
- sage/modular/congroup.py +29 -0
- sage/modular/congroup_element.py +13 -0
- sage/modular/cusps.py +1109 -0
- sage/modular/cusps_nf.py +1270 -0
- sage/modular/dims.py +569 -0
- sage/modular/dirichlet.py +3310 -0
- sage/modular/drinfeld_modform/all.py +2 -0
- sage/modular/drinfeld_modform/element.py +446 -0
- sage/modular/drinfeld_modform/ring.py +773 -0
- sage/modular/drinfeld_modform/tutorial.py +236 -0
- sage/modular/etaproducts.py +1065 -0
- sage/modular/hecke/algebra.py +746 -0
- sage/modular/hecke/all.py +20 -0
- sage/modular/hecke/ambient_module.py +1019 -0
- sage/modular/hecke/degenmap.py +119 -0
- sage/modular/hecke/element.py +325 -0
- sage/modular/hecke/hecke_operator.py +780 -0
- sage/modular/hecke/homspace.py +206 -0
- sage/modular/hecke/module.py +1767 -0
- sage/modular/hecke/morphism.py +174 -0
- sage/modular/hecke/submodule.py +989 -0
- sage/modular/hypergeometric_misc.cpython-312-darwin.so +0 -0
- sage/modular/hypergeometric_misc.pxd +4 -0
- sage/modular/hypergeometric_misc.pyx +166 -0
- sage/modular/hypergeometric_motive.py +2017 -0
- sage/modular/local_comp/all.py +2 -0
- sage/modular/local_comp/liftings.py +292 -0
- sage/modular/local_comp/local_comp.py +1071 -0
- sage/modular/local_comp/smoothchar.py +1825 -0
- sage/modular/local_comp/type_space.py +748 -0
- sage/modular/modform/all.py +30 -0
- sage/modular/modform/ambient.py +815 -0
- sage/modular/modform/ambient_R.py +177 -0
- sage/modular/modform/ambient_eps.py +306 -0
- sage/modular/modform/ambient_g0.py +124 -0
- sage/modular/modform/ambient_g1.py +204 -0
- sage/modular/modform/constructor.py +545 -0
- sage/modular/modform/cuspidal_submodule.py +708 -0
- sage/modular/modform/defaults.py +14 -0
- sage/modular/modform/eis_series.py +505 -0
- sage/modular/modform/eisenstein_submodule.py +663 -0
- sage/modular/modform/element.py +4131 -0
- sage/modular/modform/find_generators.py +59 -0
- sage/modular/modform/half_integral.py +154 -0
- sage/modular/modform/hecke_operator_on_qexp.py +247 -0
- sage/modular/modform/j_invariant.py +47 -0
- sage/modular/modform/l_series_gross_zagier.py +133 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.cpython-312-darwin.so +0 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
- sage/modular/modform/notes.py +45 -0
- sage/modular/modform/numerical.py +514 -0
- sage/modular/modform/periods.py +14 -0
- sage/modular/modform/ring.py +1257 -0
- sage/modular/modform/space.py +1860 -0
- sage/modular/modform/submodule.py +118 -0
- sage/modular/modform/tests.py +64 -0
- sage/modular/modform/theta.py +110 -0
- sage/modular/modform/vm_basis.py +381 -0
- sage/modular/modform/weight1.py +220 -0
- sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
- sage/modular/modform_hecketriangle/abstract_space.py +2528 -0
- sage/modular/modform_hecketriangle/all.py +30 -0
- sage/modular/modform_hecketriangle/analytic_type.py +590 -0
- sage/modular/modform_hecketriangle/constructor.py +416 -0
- sage/modular/modform_hecketriangle/element.py +351 -0
- sage/modular/modform_hecketriangle/functors.py +752 -0
- sage/modular/modform_hecketriangle/graded_ring.py +541 -0
- sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
- sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3352 -0
- sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1432 -0
- sage/modular/modform_hecketriangle/readme.py +1214 -0
- sage/modular/modform_hecketriangle/series_constructor.py +580 -0
- sage/modular/modform_hecketriangle/space.py +1037 -0
- sage/modular/modform_hecketriangle/subspace.py +423 -0
- sage/modular/modsym/all.py +17 -0
- sage/modular/modsym/ambient.py +3846 -0
- sage/modular/modsym/boundary.py +1420 -0
- sage/modular/modsym/element.py +336 -0
- sage/modular/modsym/g1list.py +178 -0
- sage/modular/modsym/ghlist.py +182 -0
- sage/modular/modsym/hecke_operator.py +73 -0
- sage/modular/modsym/manin_symbol.cpython-312-darwin.so +0 -0
- sage/modular/modsym/manin_symbol.pxd +5 -0
- sage/modular/modsym/manin_symbol.pyx +497 -0
- sage/modular/modsym/manin_symbol_list.py +1295 -0
- sage/modular/modsym/modsym.py +400 -0
- sage/modular/modsym/modular_symbols.py +384 -0
- sage/modular/modsym/p1list_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-312-darwin.so +0 -0
- sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
- sage/modular/modsym/space.py +2468 -0
- sage/modular/modsym/subspace.py +455 -0
- sage/modular/modsym/tests.py +375 -0
- sage/modular/multiple_zeta.py +2632 -0
- sage/modular/multiple_zeta_F_algebra.py +786 -0
- sage/modular/overconvergent/all.py +6 -0
- sage/modular/overconvergent/genus0.py +1878 -0
- sage/modular/overconvergent/hecke_series.py +1187 -0
- sage/modular/overconvergent/weightspace.py +778 -0
- sage/modular/pollack_stevens/all.py +4 -0
- sage/modular/pollack_stevens/distributions.py +874 -0
- sage/modular/pollack_stevens/fund_domain.py +1572 -0
- sage/modular/pollack_stevens/manin_map.py +859 -0
- sage/modular/pollack_stevens/modsym.py +1593 -0
- sage/modular/pollack_stevens/padic_lseries.py +417 -0
- sage/modular/pollack_stevens/sigma0.py +534 -0
- sage/modular/pollack_stevens/space.py +1076 -0
- sage/modular/quasimodform/all.py +3 -0
- sage/modular/quasimodform/element.py +845 -0
- sage/modular/quasimodform/ring.py +828 -0
- sage/modular/quatalg/all.py +3 -0
- sage/modular/quatalg/brandt.py +1642 -0
- sage/modular/ssmod/all.py +8 -0
- sage/modular/ssmod/ssmod.py +827 -0
- sage/rings/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/binary_form_reduce.py +585 -0
- sage/schemes/all.py +41 -0
- sage/schemes/berkovich/all.py +6 -0
- sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
- sage/schemes/berkovich/berkovich_space.py +748 -0
- sage/schemes/curves/affine_curve.py +2928 -0
- sage/schemes/curves/all.py +33 -0
- sage/schemes/curves/closed_point.py +434 -0
- sage/schemes/curves/constructor.py +381 -0
- sage/schemes/curves/curve.py +542 -0
- sage/schemes/curves/plane_curve_arrangement.py +1283 -0
- sage/schemes/curves/point.py +463 -0
- sage/schemes/curves/projective_curve.py +3026 -0
- sage/schemes/curves/zariski_vankampen.py +1932 -0
- sage/schemes/cyclic_covers/all.py +2 -0
- sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
- sage/schemes/cyclic_covers/constructor.py +137 -0
- sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
- sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
- sage/schemes/elliptic_curves/BSD.py +1036 -0
- sage/schemes/elliptic_curves/Qcurves.py +592 -0
- sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
- sage/schemes/elliptic_curves/all.py +49 -0
- sage/schemes/elliptic_curves/cardinality.py +609 -0
- sage/schemes/elliptic_curves/cm.py +1102 -0
- sage/schemes/elliptic_curves/constructor.py +1552 -0
- sage/schemes/elliptic_curves/ec_database.py +175 -0
- sage/schemes/elliptic_curves/ell_curve_isogeny.py +3972 -0
- sage/schemes/elliptic_curves/ell_egros.py +459 -0
- sage/schemes/elliptic_curves/ell_field.py +2836 -0
- sage/schemes/elliptic_curves/ell_finite_field.py +3359 -0
- sage/schemes/elliptic_curves/ell_generic.py +3760 -0
- sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
- sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
- sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
- sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
- sage/schemes/elliptic_curves/ell_point.py +4787 -0
- sage/schemes/elliptic_curves/ell_rational_field.py +7368 -0
- sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
- sage/schemes/elliptic_curves/ell_torsion.py +436 -0
- sage/schemes/elliptic_curves/ell_wp.py +352 -0
- sage/schemes/elliptic_curves/formal_group.py +760 -0
- sage/schemes/elliptic_curves/gal_reps.py +1459 -0
- sage/schemes/elliptic_curves/gal_reps_number_field.py +1669 -0
- sage/schemes/elliptic_curves/gp_simon.py +152 -0
- sage/schemes/elliptic_curves/heegner.py +7335 -0
- sage/schemes/elliptic_curves/height.py +2109 -0
- sage/schemes/elliptic_curves/hom.py +1406 -0
- sage/schemes/elliptic_curves/hom_composite.py +934 -0
- sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
- sage/schemes/elliptic_curves/hom_scalar.py +531 -0
- sage/schemes/elliptic_curves/hom_sum.py +682 -0
- sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
- sage/schemes/elliptic_curves/homset.py +271 -0
- sage/schemes/elliptic_curves/isogeny_class.py +1521 -0
- sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
- sage/schemes/elliptic_curves/jacobian.py +237 -0
- sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
- sage/schemes/elliptic_curves/kraus.py +1014 -0
- sage/schemes/elliptic_curves/lseries_ell.py +943 -0
- sage/schemes/elliptic_curves/mod5family.py +105 -0
- sage/schemes/elliptic_curves/mod_poly.py +197 -0
- sage/schemes/elliptic_curves/mod_sym_num.cpython-312-darwin.so +0 -0
- sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
- sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
- sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
- sage/schemes/elliptic_curves/padics.py +1816 -0
- sage/schemes/elliptic_curves/period_lattice.py +2234 -0
- sage/schemes/elliptic_curves/period_lattice_region.cpython-312-darwin.so +0 -0
- sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
- sage/schemes/elliptic_curves/saturation.py +715 -0
- sage/schemes/elliptic_curves/sha_tate.py +1158 -0
- sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
- sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
- sage/schemes/hyperelliptic_curves/all.py +6 -0
- sage/schemes/hyperelliptic_curves/constructor.py +291 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1914 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +954 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
- sage/schemes/hyperelliptic_curves/invariants.py +410 -0
- sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +315 -0
- sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
- sage/schemes/hyperelliptic_curves/jacobian_generic.py +419 -0
- sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
- sage/schemes/hyperelliptic_curves/jacobian_morphism.py +875 -0
- sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
- sage/schemes/hyperelliptic_curves/mestre.py +302 -0
- sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3871 -0
- sage/schemes/jacobians/abstract_jacobian.py +277 -0
- sage/schemes/jacobians/all.py +2 -0
- sage/schemes/overview.py +161 -0
- sage/schemes/plane_conics/all.py +22 -0
- sage/schemes/plane_conics/con_field.py +1296 -0
- sage/schemes/plane_conics/con_finite_field.py +158 -0
- sage/schemes/plane_conics/con_number_field.py +456 -0
- sage/schemes/plane_conics/con_rational_field.py +406 -0
- sage/schemes/plane_conics/con_rational_function_field.py +580 -0
- sage/schemes/plane_conics/constructor.py +249 -0
- sage/schemes/plane_quartics/all.py +2 -0
- sage/schemes/plane_quartics/quartic_constructor.py +71 -0
- sage/schemes/plane_quartics/quartic_generic.py +73 -0
- sage/schemes/riemann_surfaces/all.py +1 -0
- sage/schemes/riemann_surfaces/riemann_surface.py +4117 -0
- sage_wheels/share/cremona/cremona_mini.db +0 -0
- sage_wheels/share/ellcurves/rank0 +30427 -0
- sage_wheels/share/ellcurves/rank1 +31871 -0
- sage_wheels/share/ellcurves/rank10 +6 -0
- sage_wheels/share/ellcurves/rank11 +6 -0
- sage_wheels/share/ellcurves/rank12 +1 -0
- sage_wheels/share/ellcurves/rank14 +1 -0
- sage_wheels/share/ellcurves/rank15 +1 -0
- sage_wheels/share/ellcurves/rank17 +1 -0
- sage_wheels/share/ellcurves/rank19 +1 -0
- sage_wheels/share/ellcurves/rank2 +2388 -0
- sage_wheels/share/ellcurves/rank20 +1 -0
- sage_wheels/share/ellcurves/rank21 +1 -0
- sage_wheels/share/ellcurves/rank22 +1 -0
- sage_wheels/share/ellcurves/rank23 +1 -0
- sage_wheels/share/ellcurves/rank24 +1 -0
- sage_wheels/share/ellcurves/rank28 +1 -0
- sage_wheels/share/ellcurves/rank3 +836 -0
- sage_wheels/share/ellcurves/rank4 +10 -0
- sage_wheels/share/ellcurves/rank5 +5 -0
- sage_wheels/share/ellcurves/rank6 +5 -0
- sage_wheels/share/ellcurves/rank7 +5 -0
- sage_wheels/share/ellcurves/rank8 +6 -0
- 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)
|