passagemath-schemes 10.8.1a4__cp314-cp314t-macosx_13_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. passagemath_schemes/.dylibs/libflint.22.0.dylib +0 -0
  2. passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
  3. passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
  4. passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
  5. passagemath_schemes/__init__.py +3 -0
  6. passagemath_schemes-10.8.1a4.dist-info/METADATA +203 -0
  7. passagemath_schemes-10.8.1a4.dist-info/METADATA.bak +204 -0
  8. passagemath_schemes-10.8.1a4.dist-info/RECORD +312 -0
  9. passagemath_schemes-10.8.1a4.dist-info/WHEEL +6 -0
  10. passagemath_schemes-10.8.1a4.dist-info/top_level.txt +3 -0
  11. sage/all__sagemath_schemes.py +23 -0
  12. sage/databases/all__sagemath_schemes.py +7 -0
  13. sage/databases/cremona.py +1723 -0
  14. sage/dynamics/all__sagemath_schemes.py +2 -0
  15. sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
  16. sage/dynamics/arithmetic_dynamics/all.py +14 -0
  17. sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
  18. sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
  19. sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
  20. sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
  21. sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
  22. sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
  23. sage/dynamics/arithmetic_dynamics/projective_ds.py +9556 -0
  24. sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314t-darwin.so +0 -0
  25. sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
  26. sage/dynamics/arithmetic_dynamics/wehlerK3.py +2578 -0
  27. sage/lfunctions/all.py +18 -0
  28. sage/lfunctions/dokchitser.py +727 -0
  29. sage/lfunctions/pari.py +971 -0
  30. sage/lfunctions/zero_sums.cpython-314t-darwin.so +0 -0
  31. sage/lfunctions/zero_sums.pyx +1847 -0
  32. sage/modular/abvar/abvar.py +5132 -0
  33. sage/modular/abvar/abvar_ambient_jacobian.py +414 -0
  34. sage/modular/abvar/abvar_newform.py +246 -0
  35. sage/modular/abvar/all.py +8 -0
  36. sage/modular/abvar/constructor.py +187 -0
  37. sage/modular/abvar/cuspidal_subgroup.py +371 -0
  38. sage/modular/abvar/finite_subgroup.py +896 -0
  39. sage/modular/abvar/homology.py +721 -0
  40. sage/modular/abvar/homspace.py +989 -0
  41. sage/modular/abvar/lseries.py +415 -0
  42. sage/modular/abvar/morphism.py +935 -0
  43. sage/modular/abvar/torsion_point.py +274 -0
  44. sage/modular/abvar/torsion_subgroup.py +741 -0
  45. sage/modular/all.py +43 -0
  46. sage/modular/arithgroup/all.py +20 -0
  47. sage/modular/arithgroup/arithgroup_element.cpython-314t-darwin.so +0 -0
  48. sage/modular/arithgroup/arithgroup_element.pyx +474 -0
  49. sage/modular/arithgroup/arithgroup_generic.py +1406 -0
  50. sage/modular/arithgroup/arithgroup_perm.py +2692 -0
  51. sage/modular/arithgroup/congroup.cpython-314t-darwin.so +0 -0
  52. sage/modular/arithgroup/congroup.pyx +334 -0
  53. sage/modular/arithgroup/congroup_gamma.py +361 -0
  54. sage/modular/arithgroup/congroup_gamma0.py +692 -0
  55. sage/modular/arithgroup/congroup_gamma1.py +659 -0
  56. sage/modular/arithgroup/congroup_gammaH.py +1491 -0
  57. sage/modular/arithgroup/congroup_generic.py +630 -0
  58. sage/modular/arithgroup/congroup_sl2z.py +266 -0
  59. sage/modular/arithgroup/farey_symbol.cpython-314t-darwin.so +0 -0
  60. sage/modular/arithgroup/farey_symbol.pyx +1067 -0
  61. sage/modular/arithgroup/tests.py +425 -0
  62. sage/modular/btquotients/all.py +4 -0
  63. sage/modular/btquotients/btquotient.py +3736 -0
  64. sage/modular/btquotients/pautomorphicform.py +2564 -0
  65. sage/modular/buzzard.py +100 -0
  66. sage/modular/congroup.py +29 -0
  67. sage/modular/congroup_element.py +13 -0
  68. sage/modular/cusps.py +1107 -0
  69. sage/modular/cusps_nf.py +1270 -0
  70. sage/modular/dims.py +571 -0
  71. sage/modular/dirichlet.py +3310 -0
  72. sage/modular/drinfeld_modform/all.py +2 -0
  73. sage/modular/drinfeld_modform/element.py +446 -0
  74. sage/modular/drinfeld_modform/ring.py +773 -0
  75. sage/modular/drinfeld_modform/tutorial.py +236 -0
  76. sage/modular/etaproducts.py +1076 -0
  77. sage/modular/hecke/algebra.py +725 -0
  78. sage/modular/hecke/all.py +19 -0
  79. sage/modular/hecke/ambient_module.py +994 -0
  80. sage/modular/hecke/degenmap.py +119 -0
  81. sage/modular/hecke/element.py +302 -0
  82. sage/modular/hecke/hecke_operator.py +736 -0
  83. sage/modular/hecke/homspace.py +185 -0
  84. sage/modular/hecke/module.py +1744 -0
  85. sage/modular/hecke/morphism.py +139 -0
  86. sage/modular/hecke/submodule.py +970 -0
  87. sage/modular/hypergeometric_misc.cpython-314t-darwin.so +0 -0
  88. sage/modular/hypergeometric_misc.pxd +4 -0
  89. sage/modular/hypergeometric_misc.pyx +166 -0
  90. sage/modular/hypergeometric_motive.py +2020 -0
  91. sage/modular/local_comp/all.py +2 -0
  92. sage/modular/local_comp/liftings.py +292 -0
  93. sage/modular/local_comp/local_comp.py +1070 -0
  94. sage/modular/local_comp/smoothchar.py +1825 -0
  95. sage/modular/local_comp/type_space.py +748 -0
  96. sage/modular/modform/all.py +30 -0
  97. sage/modular/modform/ambient.py +817 -0
  98. sage/modular/modform/ambient_R.py +177 -0
  99. sage/modular/modform/ambient_eps.py +306 -0
  100. sage/modular/modform/ambient_g0.py +120 -0
  101. sage/modular/modform/ambient_g1.py +199 -0
  102. sage/modular/modform/constructor.py +545 -0
  103. sage/modular/modform/cuspidal_submodule.py +708 -0
  104. sage/modular/modform/defaults.py +14 -0
  105. sage/modular/modform/eis_series.py +487 -0
  106. sage/modular/modform/eisenstein_submodule.py +663 -0
  107. sage/modular/modform/element.py +4105 -0
  108. sage/modular/modform/half_integral.py +154 -0
  109. sage/modular/modform/hecke_operator_on_qexp.py +247 -0
  110. sage/modular/modform/j_invariant.py +47 -0
  111. sage/modular/modform/l_series_gross_zagier.py +127 -0
  112. sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314t-darwin.so +0 -0
  113. sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
  114. sage/modular/modform/notes.py +45 -0
  115. sage/modular/modform/numerical.py +514 -0
  116. sage/modular/modform/periods.py +14 -0
  117. sage/modular/modform/ring.py +1257 -0
  118. sage/modular/modform/space.py +1859 -0
  119. sage/modular/modform/submodule.py +118 -0
  120. sage/modular/modform/tests.py +64 -0
  121. sage/modular/modform/theta.py +110 -0
  122. sage/modular/modform/vm_basis.py +380 -0
  123. sage/modular/modform/weight1.py +221 -0
  124. sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
  125. sage/modular/modform_hecketriangle/abstract_space.py +2527 -0
  126. sage/modular/modform_hecketriangle/all.py +30 -0
  127. sage/modular/modform_hecketriangle/analytic_type.py +590 -0
  128. sage/modular/modform_hecketriangle/constructor.py +416 -0
  129. sage/modular/modform_hecketriangle/element.py +351 -0
  130. sage/modular/modform_hecketriangle/functors.py +752 -0
  131. sage/modular/modform_hecketriangle/graded_ring.py +541 -0
  132. sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
  133. sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3349 -0
  134. sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1426 -0
  135. sage/modular/modform_hecketriangle/readme.py +1214 -0
  136. sage/modular/modform_hecketriangle/series_constructor.py +580 -0
  137. sage/modular/modform_hecketriangle/space.py +1037 -0
  138. sage/modular/modform_hecketriangle/subspace.py +423 -0
  139. sage/modular/modsym/all.py +17 -0
  140. sage/modular/modsym/ambient.py +3844 -0
  141. sage/modular/modsym/boundary.py +1420 -0
  142. sage/modular/modsym/element.py +336 -0
  143. sage/modular/modsym/g1list.py +178 -0
  144. sage/modular/modsym/ghlist.py +182 -0
  145. sage/modular/modsym/hecke_operator.py +73 -0
  146. sage/modular/modsym/manin_symbol.cpython-314t-darwin.so +0 -0
  147. sage/modular/modsym/manin_symbol.pxd +5 -0
  148. sage/modular/modsym/manin_symbol.pyx +497 -0
  149. sage/modular/modsym/manin_symbol_list.py +1291 -0
  150. sage/modular/modsym/modsym.py +400 -0
  151. sage/modular/modsym/modular_symbols.py +384 -0
  152. sage/modular/modsym/p1list_nf.py +1241 -0
  153. sage/modular/modsym/relation_matrix.py +591 -0
  154. sage/modular/modsym/relation_matrix_pyx.cpython-314t-darwin.so +0 -0
  155. sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
  156. sage/modular/modsym/space.py +2468 -0
  157. sage/modular/modsym/subspace.py +455 -0
  158. sage/modular/modsym/tests.py +376 -0
  159. sage/modular/multiple_zeta.py +2635 -0
  160. sage/modular/multiple_zeta_F_algebra.py +789 -0
  161. sage/modular/overconvergent/all.py +6 -0
  162. sage/modular/overconvergent/genus0.py +1879 -0
  163. sage/modular/overconvergent/hecke_series.py +1187 -0
  164. sage/modular/overconvergent/weightspace.py +776 -0
  165. sage/modular/pollack_stevens/all.py +4 -0
  166. sage/modular/pollack_stevens/distributions.py +874 -0
  167. sage/modular/pollack_stevens/fund_domain.py +1572 -0
  168. sage/modular/pollack_stevens/manin_map.py +856 -0
  169. sage/modular/pollack_stevens/modsym.py +1590 -0
  170. sage/modular/pollack_stevens/padic_lseries.py +417 -0
  171. sage/modular/pollack_stevens/sigma0.py +534 -0
  172. sage/modular/pollack_stevens/space.py +1078 -0
  173. sage/modular/quasimodform/all.py +3 -0
  174. sage/modular/quasimodform/element.py +846 -0
  175. sage/modular/quasimodform/ring.py +826 -0
  176. sage/modular/quatalg/all.py +3 -0
  177. sage/modular/quatalg/brandt.py +1642 -0
  178. sage/modular/ssmod/all.py +8 -0
  179. sage/modular/ssmod/ssmod.py +827 -0
  180. sage/rings/all__sagemath_schemes.py +1 -0
  181. sage/rings/polynomial/all__sagemath_schemes.py +1 -0
  182. sage/rings/polynomial/binary_form_reduce.py +585 -0
  183. sage/schemes/all.py +41 -0
  184. sage/schemes/berkovich/all.py +6 -0
  185. sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
  186. sage/schemes/berkovich/berkovich_space.py +700 -0
  187. sage/schemes/curves/affine_curve.py +2924 -0
  188. sage/schemes/curves/all.py +33 -0
  189. sage/schemes/curves/closed_point.py +434 -0
  190. sage/schemes/curves/constructor.py +397 -0
  191. sage/schemes/curves/curve.py +542 -0
  192. sage/schemes/curves/plane_curve_arrangement.py +1283 -0
  193. sage/schemes/curves/point.py +463 -0
  194. sage/schemes/curves/projective_curve.py +3203 -0
  195. sage/schemes/curves/weighted_projective_curve.py +106 -0
  196. sage/schemes/curves/zariski_vankampen.py +1931 -0
  197. sage/schemes/cyclic_covers/all.py +2 -0
  198. sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
  199. sage/schemes/cyclic_covers/constructor.py +137 -0
  200. sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
  201. sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
  202. sage/schemes/elliptic_curves/BSD.py +991 -0
  203. sage/schemes/elliptic_curves/Qcurves.py +592 -0
  204. sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
  205. sage/schemes/elliptic_curves/all.py +49 -0
  206. sage/schemes/elliptic_curves/cardinality.py +609 -0
  207. sage/schemes/elliptic_curves/cm.py +1103 -0
  208. sage/schemes/elliptic_curves/constructor.py +1530 -0
  209. sage/schemes/elliptic_curves/ec_database.py +175 -0
  210. sage/schemes/elliptic_curves/ell_curve_isogeny.py +3971 -0
  211. sage/schemes/elliptic_curves/ell_egros.py +457 -0
  212. sage/schemes/elliptic_curves/ell_field.py +2837 -0
  213. sage/schemes/elliptic_curves/ell_finite_field.py +3249 -0
  214. sage/schemes/elliptic_curves/ell_generic.py +3760 -0
  215. sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
  216. sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
  217. sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
  218. sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
  219. sage/schemes/elliptic_curves/ell_point.py +4944 -0
  220. sage/schemes/elliptic_curves/ell_rational_field.py +7184 -0
  221. sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
  222. sage/schemes/elliptic_curves/ell_torsion.py +436 -0
  223. sage/schemes/elliptic_curves/ell_wp.py +352 -0
  224. sage/schemes/elliptic_curves/formal_group.py +760 -0
  225. sage/schemes/elliptic_curves/gal_reps.py +1459 -0
  226. sage/schemes/elliptic_curves/gal_reps_number_field.py +1663 -0
  227. sage/schemes/elliptic_curves/gp_simon.py +152 -0
  228. sage/schemes/elliptic_curves/heegner.py +7328 -0
  229. sage/schemes/elliptic_curves/height.py +2108 -0
  230. sage/schemes/elliptic_curves/hom.py +1788 -0
  231. sage/schemes/elliptic_curves/hom_composite.py +1084 -0
  232. sage/schemes/elliptic_curves/hom_fractional.py +544 -0
  233. sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
  234. sage/schemes/elliptic_curves/hom_scalar.py +531 -0
  235. sage/schemes/elliptic_curves/hom_sum.py +681 -0
  236. sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
  237. sage/schemes/elliptic_curves/homset.py +271 -0
  238. sage/schemes/elliptic_curves/isogeny_class.py +1523 -0
  239. sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
  240. sage/schemes/elliptic_curves/jacobian.py +247 -0
  241. sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
  242. sage/schemes/elliptic_curves/kraus.py +1014 -0
  243. sage/schemes/elliptic_curves/lseries_ell.py +915 -0
  244. sage/schemes/elliptic_curves/mod5family.py +105 -0
  245. sage/schemes/elliptic_curves/mod_poly.py +197 -0
  246. sage/schemes/elliptic_curves/mod_sym_num.cpython-314t-darwin.so +0 -0
  247. sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
  248. sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
  249. sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
  250. sage/schemes/elliptic_curves/padics.py +1816 -0
  251. sage/schemes/elliptic_curves/period_lattice.py +2234 -0
  252. sage/schemes/elliptic_curves/period_lattice_region.cpython-314t-darwin.so +0 -0
  253. sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
  254. sage/schemes/elliptic_curves/saturation.py +716 -0
  255. sage/schemes/elliptic_curves/sha_tate.py +1158 -0
  256. sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
  257. sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
  258. sage/schemes/hyperelliptic_curves/all.py +6 -0
  259. sage/schemes/hyperelliptic_curves/constructor.py +369 -0
  260. sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1948 -0
  261. sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
  262. sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +936 -0
  263. sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
  264. sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
  265. sage/schemes/hyperelliptic_curves/invariants.py +410 -0
  266. sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +312 -0
  267. sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
  268. sage/schemes/hyperelliptic_curves/jacobian_generic.py +437 -0
  269. sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
  270. sage/schemes/hyperelliptic_curves/jacobian_morphism.py +878 -0
  271. sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
  272. sage/schemes/hyperelliptic_curves/mestre.py +302 -0
  273. sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3863 -0
  274. sage/schemes/jacobians/abstract_jacobian.py +277 -0
  275. sage/schemes/jacobians/all.py +2 -0
  276. sage/schemes/overview.py +161 -0
  277. sage/schemes/plane_conics/all.py +22 -0
  278. sage/schemes/plane_conics/con_field.py +1296 -0
  279. sage/schemes/plane_conics/con_finite_field.py +158 -0
  280. sage/schemes/plane_conics/con_number_field.py +456 -0
  281. sage/schemes/plane_conics/con_rational_field.py +406 -0
  282. sage/schemes/plane_conics/con_rational_function_field.py +581 -0
  283. sage/schemes/plane_conics/constructor.py +249 -0
  284. sage/schemes/plane_quartics/all.py +2 -0
  285. sage/schemes/plane_quartics/quartic_constructor.py +71 -0
  286. sage/schemes/plane_quartics/quartic_generic.py +53 -0
  287. sage/schemes/riemann_surfaces/all.py +1 -0
  288. sage/schemes/riemann_surfaces/riemann_surface.py +4177 -0
  289. sage_wheels/share/cremona/cremona_mini.db +0 -0
  290. sage_wheels/share/ellcurves/rank0 +30427 -0
  291. sage_wheels/share/ellcurves/rank1 +31871 -0
  292. sage_wheels/share/ellcurves/rank10 +6 -0
  293. sage_wheels/share/ellcurves/rank11 +6 -0
  294. sage_wheels/share/ellcurves/rank12 +1 -0
  295. sage_wheels/share/ellcurves/rank14 +1 -0
  296. sage_wheels/share/ellcurves/rank15 +1 -0
  297. sage_wheels/share/ellcurves/rank17 +1 -0
  298. sage_wheels/share/ellcurves/rank19 +1 -0
  299. sage_wheels/share/ellcurves/rank2 +2388 -0
  300. sage_wheels/share/ellcurves/rank20 +1 -0
  301. sage_wheels/share/ellcurves/rank21 +1 -0
  302. sage_wheels/share/ellcurves/rank22 +1 -0
  303. sage_wheels/share/ellcurves/rank23 +1 -0
  304. sage_wheels/share/ellcurves/rank24 +1 -0
  305. sage_wheels/share/ellcurves/rank28 +1 -0
  306. sage_wheels/share/ellcurves/rank3 +836 -0
  307. sage_wheels/share/ellcurves/rank4 +10 -0
  308. sage_wheels/share/ellcurves/rank5 +5 -0
  309. sage_wheels/share/ellcurves/rank6 +5 -0
  310. sage_wheels/share/ellcurves/rank7 +5 -0
  311. sage_wheels/share/ellcurves/rank8 +6 -0
  312. sage_wheels/share/ellcurves/rank9 +7 -0
@@ -0,0 +1,3203 @@
1
+ # sage_setup: distribution = sagemath-schemes
2
+ # sage.doctest: needs sage.libs.singular
3
+ r"""
4
+ Projective curves
5
+
6
+ Projective curves in Sage are curves in a projective space or a projective plane.
7
+
8
+ EXAMPLES:
9
+
10
+ We can construct curves in either a projective plane::
11
+
12
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
13
+ sage: C = Curve([y*z^2 - x^3], P); C
14
+ Projective Plane Curve over Rational Field defined by -x^3 + y*z^2
15
+
16
+ or in higher dimensional projective spaces::
17
+
18
+ sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
19
+ sage: C = Curve([y*w^3 - x^4, z*w^3 - x^4], P); C
20
+ Projective Curve over Rational Field defined by -x^4 + y*w^3, -x^4 + z*w^3
21
+
22
+ Integral projective curves over finite fields
23
+ ---------------------------------------------
24
+
25
+ If the curve is defined over a finite field and integral, that is reduced and
26
+ irreducible, its function field is tightly coupled with the curve so that
27
+ advanced computations based on Sage's global function field machinery are
28
+ available.
29
+
30
+ EXAMPLES::
31
+
32
+ sage: k = GF(2)
33
+ sage: P.<x,y,z> = ProjectiveSpace(k, 2)
34
+ sage: C = Curve(x^2*z - y^3, P)
35
+ sage: C.genus()
36
+ 0
37
+ sage: C.function_field()
38
+ Function field in z defined by z + y^3
39
+
40
+ Closed points of arbitrary degree can be computed::
41
+
42
+ sage: C.closed_points()
43
+ [Point (x, y), Point (y, z), Point (x + z, y + z)]
44
+ sage: C.closed_points(2)
45
+ [Point (y^2 + y*z + z^2, x + z)]
46
+ sage: C.closed_points(3)
47
+ [Point (y^3 + y^2*z + z^3, x + y + z),
48
+ Point (x^2 + y*z + z^2, x*y + x*z + y*z, y^2 + x*z + y*z + z^2)]
49
+
50
+ All singular closed points can be found::
51
+
52
+ sage: C.singular_closed_points()
53
+ [Point (x, y)]
54
+ sage: p = _[0]
55
+ sage: p.places() # a unibranch singularity, that is, a cusp
56
+ [Place (1/y)]
57
+ sage: pls = _[0]
58
+ sage: C.place_to_closed_point(pls)
59
+ Point (x, y)
60
+
61
+ It is easy to transit to and from the function field of the curve::
62
+
63
+ sage: fx = C(x/z)
64
+ sage: fy = C(y/z)
65
+ sage: fx^2 - fy^3
66
+ 0
67
+ sage: fx.divisor()
68
+ 3*Place (1/y)
69
+ - 3*Place (y)
70
+ sage: p, = fx.poles()
71
+ sage: p
72
+ Place (y)
73
+ sage: C.place_to_closed_point(p)
74
+ Point (y, z)
75
+ sage: _.rational_point()
76
+ (1 : 0 : 0)
77
+ sage: _.closed_point()
78
+ Point (y, z)
79
+ sage: _.place()
80
+ Place (y)
81
+
82
+ Integral projective curves over `\QQ`
83
+ -------------------------------------
84
+
85
+ An integral curve over `\QQ` is also equipped with the function field. Unlike
86
+ over finite fields, it is not possible to enumerate closed points.
87
+
88
+ EXAMPLES::
89
+
90
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
91
+ sage: C = Curve(x^2*z^2 - x^4 - y^4, P)
92
+ sage: C.singular_closed_points()
93
+ [Point (x, y)]
94
+ sage: p, = _
95
+ sage: p.places()
96
+ [Place (1/y, 1/y^2*z - 1), Place (1/y, 1/y^2*z + 1)]
97
+ sage: fy = C.function(y/z)
98
+ sage: fy.divisor()
99
+ Place (1/y, 1/y^2*z - 1)
100
+ + Place (1/y, 1/y^2*z + 1)
101
+ + Place (y, z - 1)
102
+ + Place (y, z + 1)
103
+ - Place (y^4 + 1, z)
104
+ sage: supp = _.support()
105
+ sage: pl = supp[0]
106
+ sage: C.place_to_closed_point(pl)
107
+ Point (x, y)
108
+ sage: pl = supp[1]
109
+ sage: C.place_to_closed_point(pl)
110
+ Point (x, y)
111
+ sage: _.rational_point()
112
+ (0 : 0 : 1)
113
+ sage: _ in C
114
+ True
115
+
116
+ AUTHORS:
117
+
118
+ - William Stein (2005-11-13)
119
+
120
+ - David Joyner (2005-11-13)
121
+
122
+ - David Kohel (2006-01)
123
+
124
+ - Moritz Minzlaff (2010-11)
125
+
126
+ - Grayson Jorgenson (2016-08)
127
+
128
+ - Kwankyu Lee (2019-05): added integral projective curves
129
+ """
130
+ # ****************************************************************************
131
+ # Copyright (C) 2005 William Stein <wstein@gmail.com>
132
+ #
133
+ # Distributed under the terms of the GNU General Public License (GPL)
134
+ #
135
+ # The full text of the GPL is available at:
136
+ #
137
+ # https://www.gnu.org/licenses/
138
+ # ****************************************************************************
139
+
140
+ from builtins import sum as add
141
+
142
+ from sage.categories.fields import Fields
143
+ from sage.categories.homset import End, Hom, hom
144
+ from sage.categories.number_fields import NumberFields
145
+ from sage.libs.singular.function import (
146
+ get_printlevel,
147
+ set_printlevel,
148
+ singular_function,
149
+ )
150
+ from sage.libs.singular.function import lib as singular_lib
151
+ from sage.matrix.constructor import matrix
152
+ from sage.misc.cachefunc import cached_method
153
+ from sage.misc.lazy_attribute import lazy_attribute
154
+ from sage.misc.lazy_import import lazy_import
155
+ from sage.misc.persist import register_unpickle_override
156
+ from sage.rings.integer import Integer
157
+ from sage.rings.integer_ring import IntegerRing
158
+ from sage.rings.polynomial.multi_polynomial_element import (
159
+ degree_lowest_rational_function,
160
+ )
161
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
162
+ from sage.rings.rational_field import RationalField
163
+ from sage.schemes.projective.projective_space import (
164
+ ProjectiveSpace,
165
+ ProjectiveSpace_ring,
166
+ )
167
+ from sage.schemes.projective.projective_subscheme import (
168
+ AlgebraicScheme_subscheme_projective,
169
+ AlgebraicScheme_subscheme_projective_field,
170
+ )
171
+
172
+ lazy_import('sage.interfaces.singular', 'singular')
173
+ lazy_import('sage.misc.sage_eval', 'sage_eval')
174
+ lazy_import('sage.rings.number_field.number_field', 'NumberField')
175
+
176
+ from .closed_point import IntegralProjectiveCurveClosedPoint
177
+ from .curve import Curve_generic
178
+ from .point import (
179
+ IntegralProjectiveCurvePoint,
180
+ IntegralProjectiveCurvePoint_finite_field,
181
+ IntegralProjectivePlaneCurvePoint,
182
+ IntegralProjectivePlaneCurvePoint_finite_field,
183
+ ProjectiveCurvePoint_field,
184
+ ProjectivePlaneCurvePoint_field,
185
+ ProjectivePlaneCurvePoint_finite_field,
186
+ )
187
+
188
+
189
+ class ProjectiveCurve(Curve_generic, AlgebraicScheme_subscheme_projective):
190
+ """
191
+ Curves in projective spaces.
192
+
193
+ INPUT:
194
+
195
+ - ``A`` -- ambient projective space
196
+
197
+ - ``X`` -- list of multivariate polynomials; defining equations of the curve
198
+
199
+ EXAMPLES::
200
+
201
+ sage: P.<x,y,z,w,u> = ProjectiveSpace(GF(7), 4)
202
+ sage: C = Curve([y*u^2 - x^3, z*u^2 - x^3, w*u^2 - x^3, y^3 - x^3], P); C
203
+ Projective Curve over Finite Field of size 7 defined
204
+ by -x^3 + y*u^2, -x^3 + z*u^2, -x^3 + w*u^2, -x^3 + y^3
205
+
206
+ ::
207
+
208
+ sage: # needs sage.rings.number_field
209
+ sage: K.<u> = CyclotomicField(11)
210
+ sage: P.<x,y,z,w> = ProjectiveSpace(K, 3)
211
+ sage: C = Curve([y*w - u*z^2 - x^2, x*w - 3*u^2*z*w], P); C
212
+ Projective Curve over Cyclotomic Field of order 11 and degree 10 defined
213
+ by -x^2 + (-u)*z^2 + y*w, x*w + (-3*u^2)*z*w
214
+ """
215
+
216
+ def __init__(self, A, X, category=None):
217
+ """
218
+ Initialize.
219
+
220
+ EXAMPLES::
221
+
222
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
223
+ sage: C = Curve(x*y^2*z^7 - x^10 - x^2*z^8)
224
+ sage: loads(dumps(C)) == C
225
+ True
226
+ """
227
+ if not isinstance(A, ProjectiveSpace_ring):
228
+ raise TypeError("A (=%s) must be a projective space" % A)
229
+
230
+ Curve_generic.__init__(self, A, X, category=category)
231
+
232
+ def _repr_type(self):
233
+ r"""
234
+ Return a string representation of the type of this curve.
235
+
236
+ EXAMPLES::
237
+
238
+ sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
239
+ sage: C = Curve([y^3 - z^3 - w^3, z*x^3 - y^4])
240
+ sage: C._repr_type()
241
+ 'Projective'
242
+ """
243
+ return "Projective"
244
+
245
+ def affine_patch(self, i, AA=None):
246
+ r"""
247
+ Return the `i`-th affine patch of this projective curve.
248
+
249
+ INPUT:
250
+
251
+ - ``i`` -- affine coordinate chart of the projective ambient space of
252
+ this curve to compute affine patch with respect to
253
+
254
+ - ``AA`` -- (default: ``None``) ambient affine space, this is constructed
255
+ if it is not given
256
+
257
+ OUTPUT: a curve in affine space
258
+
259
+ EXAMPLES::
260
+
261
+ sage: P.<x,y,z,w> = ProjectiveSpace(CC, 3)
262
+ sage: C = Curve([y*z - x^2, w^2 - x*y], P)
263
+ sage: C.affine_patch(0)
264
+ Affine Curve over Complex Field with 53 bits of precision defined
265
+ by y*z - 1.00000000000000, w^2 - y
266
+
267
+ ::
268
+
269
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
270
+ sage: C = Curve(x^3 - x^2*y + y^3 - x^2*z, P)
271
+ sage: C.affine_patch(1)
272
+ Affine Plane Curve over Rational Field defined by x^3 - x^2*z - x^2 + 1
273
+
274
+ ::
275
+
276
+ sage: A.<x,y> = AffineSpace(QQ, 2)
277
+ sage: P.<u,v,w> = ProjectiveSpace(QQ, 2)
278
+ sage: C = Curve([u^2 - v^2], P)
279
+ sage: C.affine_patch(1, A).ambient_space() == A
280
+ True
281
+ """
282
+ from .constructor import Curve
283
+ return Curve(AlgebraicScheme_subscheme_projective.affine_patch(self, i, AA))
284
+
285
+ def projection(self, P=None, PS=None):
286
+ r"""
287
+ Return a projection of this curve into projective space of dimension
288
+ one less than the dimension of the ambient space of this curve.
289
+
290
+ This curve must not already be a plane curve. Over finite fields, if
291
+ this curve contains all points in its ambient space, then an error will
292
+ be returned.
293
+
294
+ INPUT:
295
+
296
+ - ``P`` -- (default: ``None``) a point not on this curve that will be used
297
+ to define the projection map; this is constructed if not specified
298
+
299
+ - ``PS`` -- (default: ``None``) the projective space the projected curve
300
+ will be defined in. This space must be defined over the same base ring
301
+ as this curve, and must have dimension one less than that of the
302
+ ambient space of this curve. This space will be constructed if not
303
+ specified.
304
+
305
+ OUTPUT: a tuple of
306
+
307
+ - a scheme morphism from this curve into a projective space of
308
+ dimension one less than that of the ambient space of this curve
309
+
310
+ - the projective curve that is the image of that morphism
311
+
312
+ EXAMPLES::
313
+
314
+ sage: # needs sage.rings.number_field
315
+ sage: K.<a> = CyclotomicField(3)
316
+ sage: P.<x,y,z,w> = ProjectiveSpace(K, 3)
317
+ sage: C = Curve([y*w - x^2, z*w^2 - a*x^3], P)
318
+ sage: L.<a,b,c> = ProjectiveSpace(K, 2)
319
+ sage: proj1 = C.projection(PS=L)
320
+ sage: proj1
321
+ (Scheme morphism:
322
+ From: Projective Curve over Cyclotomic Field of order 3 and degree 2
323
+ defined by -x^2 + y*w, (-a)*x^3 + z*w^2
324
+ To: Projective Space of dimension 2
325
+ over Cyclotomic Field of order 3 and degree 2
326
+ Defn: Defined on coordinates by sending (x : y : z : w) to
327
+ (x : y : -z + w),
328
+ Projective Plane Curve over Cyclotomic Field of order 3 and degree 2
329
+ defined by a^6 + (-a)*a^3*b^3 - a^4*b*c)
330
+ sage: proj1[1].ambient_space() is L
331
+ True
332
+ sage: proj2 = C.projection()
333
+ sage: proj2[1].ambient_space() is L
334
+ False
335
+
336
+ ::
337
+
338
+ sage: P.<x,y,z,w,a,b,c> = ProjectiveSpace(QQ, 6)
339
+ sage: C = Curve([y - x, z - a - b, w^2 - c^2, z - x - a, x^2 - w*z], P)
340
+ sage: C.projection()
341
+ (Scheme morphism:
342
+ From: Projective Curve over Rational Field
343
+ defined by -x + y, z - a - b, w^2 - c^2, -x + z - a, x^2 - z*w
344
+ To: Projective Space of dimension 5 over Rational Field
345
+ Defn: Defined on coordinates by sending (x : y : z : w : a : b : c)
346
+ to (x : y : -z + w : a : b : c),
347
+ Projective Curve over Rational Field defined by x1 - x4, x0 - x4, x2*x3
348
+ + x3^2 + x2*x4 + 2*x3*x4, x2^2 - x3^2 - 2*x3*x4 + x4^2 - x5^2, x2*x4^2 +
349
+ x3*x4^2 + x4^3 - x3*x5^2 - x4*x5^2, x4^4 - x3^2*x5^2 - 2*x3*x4*x5^2 -
350
+ x4^2*x5^2)
351
+
352
+ ::
353
+
354
+ sage: P.<x,y,z,w> = ProjectiveSpace(GF(2), 3)
355
+ sage: C = P.curve([(x - y)*(x - z)*(x - w)*(y - z)*(y - w),
356
+ ....: x*y*z*w*(x + y + z + w)])
357
+ sage: C.projection()
358
+ Traceback (most recent call last):
359
+ ...
360
+ NotImplementedError: this curve contains all points of its ambient space
361
+
362
+ ::
363
+
364
+ sage: P.<x,y,z,w,u> = ProjectiveSpace(GF(7), 4)
365
+ sage: C = P.curve([x^3 - y*z*u, w^2 - u^2 + 2*x*z, 3*x*w - y^2])
366
+ sage: L.<a,b,c,d> = ProjectiveSpace(GF(7), 3)
367
+ sage: C.projection(PS=L)
368
+ (Scheme morphism:
369
+ From: Projective Curve over Finite Field of size 7
370
+ defined by x^3 - y*z*u, 2*x*z + w^2 - u^2, -y^2 + 3*x*w
371
+ To: Projective Space of dimension 3 over Finite Field of size 7
372
+ Defn: Defined on coordinates by sending (x : y : z : w : u) to
373
+ (x : y : z : w),
374
+ Projective Curve over Finite Field of size 7 defined by b^2 - 3*a*d,
375
+ a^5*b + a*b*c^3*d - 3*b*c^2*d^3, a^6 + a^2*c^3*d - 3*a*c^2*d^3)
376
+ sage: Q.<a,b,c> = ProjectiveSpace(GF(7), 2)
377
+ sage: C.projection(PS=Q)
378
+ Traceback (most recent call last):
379
+ ...
380
+ TypeError: (=Projective Space of dimension 2 over Finite Field of
381
+ size 7) must have dimension (=3)
382
+
383
+
384
+ ::
385
+
386
+ sage: PP.<x,y,z,w> = ProjectiveSpace(QQ, 3)
387
+ sage: C = PP.curve([x^3 - z^2*y, w^2 - z*x])
388
+ sage: Q = PP([1,0,1,1])
389
+ sage: C.projection(P=Q)
390
+ (Scheme morphism:
391
+ From: Projective Curve over Rational Field
392
+ defined by x^3 - y*z^2, -x*z + w^2
393
+ To: Projective Space of dimension 2 over Rational Field
394
+ Defn: Defined on coordinates by sending (x : y : z : w) to
395
+ (y : -x + z : -x + w),
396
+ Projective Plane Curve over Rational Field defined by x0*x1^5 -
397
+ 6*x0*x1^4*x2 + 14*x0*x1^3*x2^2 - 16*x0*x1^2*x2^3 + 9*x0*x1*x2^4 -
398
+ 2*x0*x2^5 - x2^6)
399
+ sage: LL.<a,b,c> = ProjectiveSpace(QQ, 2)
400
+ sage: Q = PP([0,0,0,1])
401
+ sage: C.projection(PS=LL, P=Q)
402
+ (Scheme morphism:
403
+ From: Projective Curve over Rational Field
404
+ defined by x^3 - y*z^2, -x*z + w^2
405
+ To: Projective Space of dimension 2 over Rational Field
406
+ Defn: Defined on coordinates by sending (x : y : z : w) to
407
+ (x : y : z),
408
+ Projective Plane Curve over Rational Field defined by a^3 - b*c^2)
409
+ sage: Q = PP([0,0,1,0])
410
+ sage: C.projection(P=Q)
411
+ Traceback (most recent call last):
412
+ ...
413
+ TypeError: (=(0 : 0 : 1 : 0)) must be a point not on this curve
414
+
415
+ ::
416
+
417
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
418
+ sage: C = P.curve(y^2 - x^2 + z^2)
419
+ sage: C.projection()
420
+ Traceback (most recent call last):
421
+ ...
422
+ TypeError: this curve is already a plane curve
423
+ """
424
+ PP = self.ambient_space()
425
+ n = PP.dimension_relative()
426
+ if n == 2:
427
+ raise TypeError("this curve is already a plane curve")
428
+ if self.base_ring() not in Fields():
429
+ raise TypeError("this curve must be defined over a field")
430
+ if PS is not None:
431
+ if not isinstance(PS, ProjectiveSpace_ring):
432
+ raise TypeError("(=%s) must be a projective space" % PS)
433
+ if PS.dimension_relative() != n - 1:
434
+ raise TypeError("(=%s) must have dimension (=%s)" % (PS, n - 1))
435
+ if PS.base_ring() != PP.base_ring():
436
+ raise TypeError("(=%s) must be defined over the same base field as this curve" % PS)
437
+ if P is None:
438
+ # find a point not on the curve if not given
439
+ if self.base_ring().characteristic() == 0:
440
+ # when working over a characteristic 0 field, we can construct a point not on the curve.
441
+ # we do this by constructing a point on which at least one nonzero element of the defining ideal of
442
+ # this curve does not vanish
443
+ F = 0
444
+ # find a nonzero element
445
+ for i in range(len(self.defining_polynomials())):
446
+ if self.defining_polynomials()[i] != 0:
447
+ F = self.defining_polynomials()[i]
448
+ # find a point on which it doesn't vanish
449
+ l = list(PP.gens())
450
+ for i in range(n + 1):
451
+ l[i] = 0
452
+ while F(l) == 0:
453
+ l[i] += 1
454
+ Q = PP(l) # will be a point not on the curve
455
+ else:
456
+ # if the base ring is a finite field, iterate over all points in the ambient space and check which
457
+ # are on this curve
458
+ Q = None
459
+ for P in PP.rational_points():
460
+ try:
461
+ self(P)
462
+ except TypeError:
463
+ Q = P
464
+ break
465
+ if Q is None:
466
+ raise NotImplementedError("this curve contains all points of its ambient space")
467
+ else:
468
+ # make sure the given point is in the ambient space of the curve, but not on the curve
469
+ Q = None
470
+ try:
471
+ Q = self(P)
472
+ except TypeError:
473
+ pass
474
+ if Q is not None:
475
+ raise TypeError("(=%s) must be a point not on this curve" % P)
476
+ try:
477
+ Q = self.ambient_space()(P)
478
+ except TypeError:
479
+ raise TypeError("(=%s) must be a point in the ambient space of this curve" % P)
480
+ # in order to create the change of coordinates map, need to find a coordinate of Q that is nonzero
481
+ j = 0
482
+ while Q[j] == 0:
483
+ j = j + 1
484
+ # use this Q to project. Apply a change of coordinates to move Q to (0:...:0:1:0:...:0)
485
+ # where 1 is in the jth coordinate
486
+ if PS is None:
487
+ PP2 = ProjectiveSpace(self.base_ring(), n - 1)
488
+ else:
489
+ PP2 = PS
490
+ H = Hom(self, PP2)
491
+ coords = [PP.gens()[i] - Q[i]/Q[j]*PP.gens()[j] for i in range(n + 1)]
492
+ coords.pop(j)
493
+ psi = H(coords)
494
+ # compute image of psi via elimination
495
+ # first construct the image of this curve by the change of coordinates. This can be found by composing the
496
+ # defining polynomials of this curve with the polynomials defining the inverse of the change of coordinates
497
+ invcoords = [Q[i]*PP.gens()[j] + PP.gens()[i] for i in range(n + 1)]
498
+ invcoords[j] = Q[j]*PP.gens()[j]
499
+ id = PP.coordinate_ring().ideal([f(invcoords) for f in self.defining_polynomials()])
500
+ J = id.elimination_ideal(PP.gens()[j])
501
+ K = Hom(PP.coordinate_ring(), PP2.coordinate_ring())
502
+ ll = list(PP2.gens())
503
+ ll.insert(j, 0)
504
+ phi = K(ll)
505
+ G = [phi(f) for f in J.gens()]
506
+ C = PP2.curve(G)
507
+ return (psi, C)
508
+
509
+ def plane_projection(self, PP=None):
510
+ r"""
511
+ Return a projection of this curve into a projective plane.
512
+
513
+ INPUT:
514
+
515
+ - ``PP`` -- (default: ``None``) the projective plane the projected curve
516
+ will be defined in. This space must be defined over the same base field
517
+ as this curve, and must have dimension two. This space is constructed
518
+ if not specified.
519
+
520
+ OUTPUT: a tuple of
521
+
522
+ - a scheme morphism from this curve into a projective plane
523
+
524
+ - the projective curve that is the image of that morphism
525
+
526
+ EXAMPLES::
527
+
528
+ sage: P.<x,y,z,w,u,v> = ProjectiveSpace(QQ, 5)
529
+ sage: C = P.curve([x*u - z*v, w - y, w*y - x^2, y^3*u*2*z - w^4*w])
530
+ sage: L.<a,b,c> = ProjectiveSpace(QQ, 2)
531
+ sage: proj1 = C.plane_projection(PP=L)
532
+ sage: proj1
533
+ (Scheme morphism:
534
+ From: Projective Curve over Rational Field
535
+ defined by x*u - z*v, -y + w, -x^2 + y*w, -w^5 + 2*y^3*z*u
536
+ To: Projective Space of dimension 2 over Rational Field
537
+ Defn: Defined on coordinates by sending (x : y : z : w : u : v) to
538
+ (x : -z + u : -z + v),
539
+ Projective Plane Curve over Rational Field defined by a^8 + 6*a^7*b +
540
+ 4*a^5*b^3 - 4*a^7*c - 2*a^6*b*c - 4*a^5*b^2*c + 2*a^6*c^2)
541
+ sage: proj1[1].ambient_space() is L
542
+ True
543
+ sage: proj2 = C.projection()
544
+ sage: proj2[1].ambient_space() is L
545
+ False
546
+
547
+ ::
548
+
549
+ sage: P.<x,y,z,w,u> = ProjectiveSpace(GF(7), 4)
550
+ sage: C = P.curve([x^2 - 6*y^2, w*z*u - y^3 + 4*y^2*z, u^2 - x^2])
551
+ sage: C.plane_projection()
552
+ (Scheme morphism:
553
+ From: Projective Curve over Finite Field of size 7
554
+ defined by x^2 + y^2, -y^3 - 3*y^2*z + z*w*u, -x^2 + u^2
555
+ To: Projective Space of dimension 2 over Finite Field of size 7
556
+ Defn: Defined on coordinates by sending (x : y : z : w : u) to
557
+ (x : z : -y + w),
558
+ Projective Plane Curve over Finite Field of size 7
559
+ defined by x0^10 + 2*x0^8*x1^2 + 2*x0^6*x1^4 - 3*x0^6*x1^3*x2
560
+ + 2*x0^6*x1^2*x2^2 - 2*x0^4*x1^4*x2^2 + x0^2*x1^4*x2^4)
561
+
562
+ ::
563
+
564
+ sage: P.<x,y,z> = ProjectiveSpace(GF(17), 2)
565
+ sage: C = P.curve(x^2 - y*z - z^2)
566
+ sage: C.plane_projection()
567
+ Traceback (most recent call last):
568
+ ...
569
+ TypeError: this curve is already a plane curve
570
+ """
571
+ PS = self.ambient_space()
572
+ n = PS.dimension_relative()
573
+ if n == 2:
574
+ raise TypeError("this curve is already a plane curve")
575
+ C = self
576
+ H = Hom(PS, PS)
577
+ phi = H([PS.gens()[i] for i in range(n + 1)])
578
+ for i in range(n - 2):
579
+ if i == n - 3:
580
+ L = C.projection(PS=PP)
581
+ else:
582
+ L = C.projection()
583
+ C = L[1]
584
+ # compose the scheme morphisms that are created
585
+ K = Hom(phi.codomain().coordinate_ring(), PS.coordinate_ring())
586
+ psi = K(phi.defining_polynomials())
587
+ H = Hom(self, L[1].ambient_space())
588
+ phi = H([psi(L[0].defining_polynomials()[i]) for i in range(len(L[0].defining_polynomials()))])
589
+ return (phi, C)
590
+
591
+
592
+ class ProjectivePlaneCurve(ProjectiveCurve):
593
+ r"""
594
+ Curves in projective planes.
595
+
596
+ INPUT:
597
+
598
+ - ``A`` -- projective plane
599
+
600
+ - ``f`` -- homogeneous polynomial in the homogeneous coordinate ring of the plane
601
+
602
+ EXAMPLES:
603
+
604
+ A projective plane curve defined over an algebraic closure of `\QQ`::
605
+
606
+ sage: # needs sage.rings.number_field
607
+ sage: P.<x,y,z> = ProjectiveSpace(QQbar, 2)
608
+ sage: set_verbose(-1) # suppress warnings for slow computation
609
+ sage: C = Curve([y*z - x^2 - QQbar.gen()*z^2], P); C
610
+ Projective Plane Curve over Algebraic Field
611
+ defined by -x^2 + y*z + (-I)*z^2
612
+
613
+ A projective plane curve defined over a finite field::
614
+
615
+ sage: # needs sage.rings.finite_rings
616
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5^2, 'v'), 2)
617
+ sage: C = Curve([y^2*z - x*z^2 - z^3], P); C
618
+ Projective Plane Curve over Finite Field in v of size 5^2
619
+ defined by y^2*z - x*z^2 - z^3
620
+ """
621
+
622
+ def __init__(self, A, f, category=None):
623
+ """
624
+ Initialize.
625
+
626
+ EXAMPLES::
627
+
628
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
629
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
630
+ sage: loads(dumps(C)) == C
631
+ True
632
+ """
633
+ if not (isinstance(A, ProjectiveSpace_ring) and A.dimension != 2):
634
+ raise TypeError("the ambient space is not a projective plane")
635
+
636
+ super().__init__(A, [f], category=category)
637
+
638
+ def _repr_type(self):
639
+ r"""
640
+ Return a string representation of the type of this curve.
641
+
642
+ EXAMPLES::
643
+
644
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
645
+ sage: C = Curve([y*z^3 - 5/7*x^4 + 4*x^3*z - 9*z^4], P)
646
+ sage: C._repr_type()
647
+ 'Projective Plane'
648
+ """
649
+ return "Projective Plane"
650
+
651
+ def divisor_of_function(self, r):
652
+ """
653
+ Return the divisor of a function on a curve.
654
+
655
+ INPUT:
656
+
657
+ - ``r`` is a rational function on X
658
+
659
+ OUTPUT: list; the divisor of r represented as a list of coefficients
660
+ and points. (TODO: This will change to a more structural output in the
661
+ future.)
662
+
663
+ EXAMPLES::
664
+
665
+ sage: FF = FiniteField(5)
666
+ sage: P2 = ProjectiveSpace(2, FF, names=['x','y','z'])
667
+ sage: R = P2.coordinate_ring()
668
+ sage: x, y, z = R.gens()
669
+ sage: f = y^2*z^7 - x^9 - x*z^8
670
+ sage: C = Curve(f)
671
+ sage: K = FractionField(R)
672
+ sage: r = 1/x
673
+ sage: C.divisor_of_function(r) # todo: not implemented !!!!
674
+ [[-1, (0, 0, 1)]]
675
+ sage: r = 1/x^3
676
+ sage: C.divisor_of_function(r) # todo: not implemented !!!!
677
+ [[-3, (0, 0, 1)]]
678
+ """
679
+ F = self.base_ring()
680
+ f = self.defining_polynomial()
681
+ x, y, z = f.parent().gens()
682
+ pnts = self.rational_points()
683
+ divf = []
684
+ for P in pnts:
685
+ if P[2] != F(0):
686
+ # What is the '5' in this line and the 'r()' in the next???
687
+ lcs = self.local_coordinates(P, 5)
688
+ ldg = degree_lowest_rational_function(r(lcs[0], lcs[1]), z)
689
+ if ldg != 0:
690
+ divf.append([ldg, P])
691
+ return divf
692
+
693
+ def local_coordinates(self, pt, n):
694
+ r"""
695
+ Return local coordinates to precision n at the given point.
696
+
697
+ Behaviour is flaky - some choices of `n` are worse than
698
+ others.
699
+
700
+ INPUT:
701
+
702
+ - ``pt`` -- a rational point on X which is not a point of ramification
703
+ for the projection `(x,y) \to x`
704
+
705
+ - ``n`` -- the number of terms desired
706
+
707
+ OUTPUT: `x = x0 + t`, `y = y0` + power series in `t`
708
+
709
+ EXAMPLES::
710
+
711
+ sage: FF = FiniteField(5)
712
+ sage: P2 = ProjectiveSpace(2, FF, names=['x','y','z'])
713
+ sage: x, y, z = P2.coordinate_ring().gens()
714
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
715
+ sage: pt = C([2,3,1])
716
+ sage: C.local_coordinates(pt,9) # todo: not implemented !!!!
717
+ [2 + t,
718
+ 3 + 3*t^2 + t^3 + 3*t^4 + 3*t^6 + 3*t^7 + t^8 + 2*t^9 + 3*t^11 + 3*t^12]
719
+ """
720
+
721
+ f = self.defining_polynomial()
722
+ R = f.parent()
723
+ F = self.base_ring()
724
+ p = F.characteristic()
725
+ x0 = F(pt[0])
726
+ y0 = F(pt[1])
727
+ astr = ["a"+str(i) for i in range(1, 2*n)]
728
+ x, y = R.gens()
729
+ R0 = PolynomialRing(F, 2 * n + 2, names=[str(x), str(y), "t"] + astr)
730
+ vars0 = R0.gens()
731
+ t = vars0[2]
732
+ yt = y0*t**0 + add([vars0[i]*t**(i-2) for i in range(3, 2*n+2)])
733
+ xt = x0+t
734
+ ft = f(xt, yt)
735
+ S = singular
736
+ S.eval('ring s = '+str(p)+','+str(R0.gens())+',lp;')
737
+ S.eval('poly f = '+str(ft))
738
+ cmd = 'matrix c = coeffs ('+str(ft)+',t)'
739
+ S.eval(cmd)
740
+ N = int(S.eval('size(c)'))
741
+ b = ','.join("c[{},1]".format(i) for i in range(2, N//2 - 4))
742
+ cmd = 'ideal I = ' + b
743
+ S.eval(cmd)
744
+ c = S.eval('slimgb(I)')
745
+ d = c.split("=")
746
+ d = d[1:]
747
+ d[len(d)-1] += "\n"
748
+ e = [xx[:xx.index("\n")] for xx in d]
749
+ vals = []
750
+ for x in e:
751
+ for y in vars0:
752
+ if str(y) in x:
753
+ if x.replace(str(y), ""):
754
+ i = x.find("-")
755
+ if i > 0:
756
+ vals.append([eval(x[1:i]), x[:i], F(eval(x[i+1:]))])
757
+ i = x.find("+")
758
+ if i > 0:
759
+ vals.append([eval(x[1:i]), x[:i], -F(eval(x[i+1:]))])
760
+ else:
761
+ vals.append([eval(str(y)[1:]), str(y), F(0)])
762
+ vals.sort()
763
+ return [x0 + t, y0 + add(v[2] * t**(j + 1) for j, v in enumerate(vals))]
764
+
765
+ def plot(self, *args, **kwds):
766
+ """
767
+ Plot the real points of an affine patch of this projective
768
+ plane curve.
769
+
770
+ INPUT:
771
+
772
+ - ``self`` -- an affine plane curve
773
+
774
+ - ``patch`` -- (optional) the affine patch to be plotted; if not
775
+ specified, the patch corresponding to the last projective
776
+ coordinate being nonzero
777
+
778
+ - ``*args`` -- (optional) tuples (variable, minimum, maximum) for
779
+ plotting dimensions
780
+
781
+ - ``**kwds`` -- optional keyword arguments passed on to ``implicit_plot``
782
+
783
+ EXAMPLES:
784
+
785
+ A cuspidal curve::
786
+
787
+ sage: R.<x, y, z> = QQ[]
788
+ sage: C = Curve(x^3 - y^2*z)
789
+ sage: C.plot() # needs sage.plot
790
+ Graphics object consisting of 1 graphics primitive
791
+
792
+ The other affine patches of the same curve::
793
+
794
+ sage: # needs sage.plot
795
+ sage: C.plot(patch=0)
796
+ Graphics object consisting of 1 graphics primitive
797
+ sage: C.plot(patch=1)
798
+ Graphics object consisting of 1 graphics primitive
799
+
800
+ An elliptic curve::
801
+
802
+ sage: # needs sage.plot
803
+ sage: E = EllipticCurve('101a')
804
+ sage: C = Curve(E)
805
+ sage: C.plot()
806
+ Graphics object consisting of 1 graphics primitive
807
+ sage: C.plot(patch=0)
808
+ Graphics object consisting of 1 graphics primitive
809
+ sage: C.plot(patch=1)
810
+ Graphics object consisting of 1 graphics primitive
811
+
812
+ A hyperelliptic curve::
813
+
814
+ sage: # needs sage.plot
815
+ sage: P.<x> = QQ[]
816
+ sage: f = 4*x^5 - 30*x^3 + 45*x - 22
817
+ sage: C = HyperellipticCurve(f)
818
+ sage: C.plot()
819
+ Graphics object consisting of 1 graphics primitive
820
+ sage: C.plot(patch=0)
821
+ Graphics object consisting of 1 graphics primitive
822
+ sage: C.plot(patch=1)
823
+ Graphics object consisting of 1 graphics primitive
824
+ """
825
+ # if user has not specified a favorite affine patch, take the
826
+ # one avoiding "infinity", i.e. the one corresponding to the
827
+ # last projective coordinate being nonzero
828
+ patch = kwds.pop('patch', self.ngens() - 1)
829
+ from .constructor import Curve
830
+ C = Curve(self.affine_patch(patch))
831
+ return C.plot(*args, **kwds)
832
+
833
+ def is_singular(self, P=None) -> bool:
834
+ r"""
835
+ Return whether this curve is singular or not, or if a point ``P`` is
836
+ provided, whether ``P`` is a singular point of this curve.
837
+
838
+ INPUT:
839
+
840
+ - ``P`` -- (default: ``None``) a point on this curve
841
+
842
+ OUTPUT:
843
+
844
+ If no point ``P`` is provided, return ``True`` or ``False`` depending
845
+ on whether this curve is singular or not. If a point ``P`` is provided,
846
+ return ``True`` or ``False`` depending on whether ``P`` is or is not a
847
+ singular point of this curve.
848
+
849
+ EXAMPLES:
850
+
851
+ Over `\QQ`::
852
+
853
+ sage: F = QQ
854
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
855
+ sage: C = Curve(X^3 - Y^2*Z)
856
+ sage: C.is_singular()
857
+ True
858
+
859
+ Over a finite field::
860
+
861
+ sage: F = GF(19)
862
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
863
+ sage: C = Curve(X^3 + Y^3 + Z^3)
864
+ sage: C.is_singular()
865
+ False
866
+ sage: D = Curve(X^4 - X*Z^3)
867
+ sage: D.is_singular()
868
+ True
869
+ sage: E = Curve(X^5 + 19*Y^5 + Z^5)
870
+ sage: E.is_singular()
871
+ True
872
+ sage: E = Curve(X^5 + 9*Y^5 + Z^5)
873
+ sage: E.is_singular()
874
+ False
875
+
876
+ Over `\CC`::
877
+
878
+ sage: # needs sage.rings.function_field
879
+ sage: F = CC
880
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
881
+ sage: C = Curve(X)
882
+ sage: C.is_singular()
883
+ False
884
+ sage: D = Curve(Y^2*Z - X^3)
885
+ sage: D.is_singular()
886
+ True
887
+ sage: E = Curve(Y^2*Z - X^3 + Z^3)
888
+ sage: E.is_singular()
889
+ False
890
+
891
+ Showing that :issue:`12187` is fixed::
892
+
893
+ sage: F.<X,Y,Z> = GF(2)[]
894
+ sage: G = Curve(X^2 + Y*Z)
895
+ sage: G.is_singular()
896
+ False
897
+
898
+ ::
899
+
900
+ sage: # needs sage.fings.function_field
901
+ sage: P.<x,y,z> = ProjectiveSpace(CC, 2)
902
+ sage: C = Curve([y^4 - x^3*z], P)
903
+ sage: Q = P([0,0,1])
904
+ sage: C.is_singular()
905
+ True
906
+ """
907
+ if P is None:
908
+ poly = self.defining_polynomial()
909
+ return poly.parent().ideal(poly.gradient()+[poly]).dimension() > 0
910
+ else:
911
+ return not self.is_smooth(P)
912
+
913
+ def degree(self):
914
+ r"""
915
+ Return the degree of this projective curve.
916
+
917
+ For a plane curve, this is just the degree of its defining polynomial.
918
+
919
+ OUTPUT: integer
920
+
921
+ EXAMPLES::
922
+
923
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
924
+ sage: C = P.curve([y^7 - x^2*z^5 + 7*z^7])
925
+ sage: C.degree()
926
+ 7
927
+ """
928
+ return self.defining_polynomial().degree()
929
+
930
+ def tangents(self, P, factor=True):
931
+ r"""
932
+ Return the tangents of this projective plane curve at the point ``P``.
933
+
934
+ These are found by homogenizing the tangents of an affine patch of this
935
+ curve containing ``P``. The point ``P`` must be a point on this curve.
936
+
937
+ INPUT:
938
+
939
+ - ``P`` -- a point on this curve
940
+
941
+ - ``factor`` -- boolean (default: ``True``); whether to attempt computing the
942
+ polynomials of the individual tangent lines over the base field of this
943
+ curve, or to just return the polynomial corresponding to the union of
944
+ the tangent lines (which requires fewer computations)
945
+
946
+ OUTPUT:
947
+
948
+ A list of polynomials in the coordinate ring of the ambient space of
949
+ this curve.
950
+
951
+ EXAMPLES::
952
+
953
+ sage: # needs sage.rings.number_field
954
+ sage: set_verbose(-1)
955
+ sage: P.<x,y,z> = ProjectiveSpace(QQbar, 2)
956
+ sage: C = Curve([x^3*y + 2*x^2*y^2 + x*y^3 + x^3*z
957
+ ....: + 7*x^2*y*z + 14*x*y^2*z + 9*y^3*z], P)
958
+ sage: Q = P([0,0,1])
959
+ sage: C.tangents(Q)
960
+ [x + 4.147899035704788?*y,
961
+ x + (1.426050482147607? + 0.3689894074818041?*I)*y,
962
+ x + (1.426050482147607? - 0.3689894074818041?*I)*y]
963
+ sage: C.tangents(Q, factor=False)
964
+ [6*x^3 + 42*x^2*y + 84*x*y^2 + 54*y^3]
965
+
966
+ ::
967
+
968
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
969
+ sage: C = P.curve([x^2*y^3*z^4 - y^6*z^3 - 4*x^2*y^4*z^3 - 4*x^4*y^2*z^3
970
+ ....: + 3*y^7*z^2 + 10*x^2*y^5*z^2 + 9*x^4*y^3*z^2 + 5*x^6*y*z^2
971
+ ....: - 3*y^8*z - 9*x^2*y^6*z - 11*x^4*y^4*z - 7*x^6*y^2*z
972
+ ....: - 2*x^8*z + y^9 + 2*x^2*y^7 + 3*x^4*y^5 + 4*x^6*y^3 + 2*x^8*y])
973
+ sage: Q = P([0,1,1])
974
+ sage: C.tangents(Q)
975
+ [-y + z, 3*x^2 - y^2 + 2*y*z - z^2]
976
+
977
+ ::
978
+
979
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
980
+ sage: C = P.curve([z^3*x + y^4 - x^2*z^2])
981
+ sage: Q = P([1,1,1])
982
+ sage: C.tangents(Q)
983
+ Traceback (most recent call last):
984
+ ...
985
+ TypeError: (=(1 : 1 : 1)) is not a point on (=Projective Plane Curve
986
+ over Rational Field defined by y^4 - x^2*z^2 + x*z^3)
987
+ """
988
+ PP = self.ambient_space()
989
+ # Check whether P is a point on this curve
990
+ try:
991
+ P = self(P)
992
+ except TypeError:
993
+ raise TypeError("(=%s) is not a point on (=%s)" % (P, self))
994
+
995
+ # Find an affine chart of the ambient space of self that contains P
996
+ i = 0
997
+ while P[i] == 0:
998
+ i += 1
999
+ C = self.affine_patch(i)
1000
+ L = C.tangents(C(P.dehomogenize(i)), factor)
1001
+ R = PP.coordinate_ring()
1002
+ H = Hom(C.ambient_space().coordinate_ring(), R)
1003
+ G = list(R.gens())
1004
+ x = G.pop(i)
1005
+ phi = H(G)
1006
+ return [phi(g).homogenize(x) for g in L]
1007
+
1008
+ def is_ordinary_singularity(self, P) -> bool:
1009
+ r"""
1010
+ Return whether the singular point ``P`` of this projective plane curve is an ordinary singularity.
1011
+
1012
+ The point ``P`` is an ordinary singularity of this curve if it is a singular point, and
1013
+ if the tangents of this curve at ``P`` are distinct.
1014
+
1015
+ INPUT:
1016
+
1017
+ - ``P`` -- a point on this curve
1018
+
1019
+ OUTPUT:
1020
+
1021
+ boolean; ``True`` or ``False`` depending on whether ``P`` is or is not
1022
+ an ordinary singularity of this curve, respectively. An error is raised
1023
+ if ``P`` is not a singular point of this curve.
1024
+
1025
+ EXAMPLES::
1026
+
1027
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1028
+ sage: C = Curve([y^2*z^3 - x^5], P)
1029
+ sage: Q = P([0,0,1])
1030
+ sage: C.is_ordinary_singularity(Q)
1031
+ False
1032
+
1033
+ ::
1034
+
1035
+ sage: # needs sage.rings.number_field
1036
+ sage: R.<a> = QQ[]
1037
+ sage: K.<b> = NumberField(a^2 - 3)
1038
+ sage: P.<x,y,z> = ProjectiveSpace(K, 2)
1039
+ sage: C = P.curve([x^2*y^3*z^4 - y^6*z^3 - 4*x^2*y^4*z^3 - 4*x^4*y^2*z^3
1040
+ ....: + 3*y^7*z^2 + 10*x^2*y^5*z^2 + 9*x^4*y^3*z^2
1041
+ ....: + 5*x^6*y*z^2 - 3*y^8*z - 9*x^2*y^6*z - 11*x^4*y^4*z
1042
+ ....: - 7*x^6*y^2*z - 2*x^8*z + y^9 + 2*x^2*y^7 + 3*x^4*y^5
1043
+ ....: + 4*x^6*y^3 + 2*x^8*y])
1044
+ sage: Q = P([0,1,1])
1045
+ sage: C.is_ordinary_singularity(Q)
1046
+ True
1047
+
1048
+ ::
1049
+
1050
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1051
+ sage: C = P.curve([z^5 - y^5 + x^5 + x*y^2*z^2])
1052
+ sage: Q = P([0,1,1])
1053
+ sage: C.is_ordinary_singularity(Q)
1054
+ Traceback (most recent call last):
1055
+ ...
1056
+ TypeError: (=(0 : 1 : 1)) is not a singular point of (=Projective Plane
1057
+ Curve over Rational Field defined by x^5 - y^5 + x*y^2*z^2 + z^5)
1058
+ """
1059
+ r = self.multiplicity(P)
1060
+ if r < 2:
1061
+ raise TypeError("(=%s) is not a singular point of (=%s)" % (P, self))
1062
+
1063
+ # Find an affine chart of the ambient space of self that contains P
1064
+ i = 0
1065
+ while P[i] == 0:
1066
+ i += 1
1067
+ C = self.affine_patch(i)
1068
+ return C.is_ordinary_singularity(C(P.dehomogenize(i)))
1069
+
1070
+ def quadratic_transform(self):
1071
+ r"""
1072
+ Return a birational map from this curve to the proper transform of this curve with respect to the standard
1073
+ Cremona transformation.
1074
+
1075
+ The standard Cremona transformation is the birational automorphism of `\mathbb{P}^{2}` defined
1076
+ `(x : y : z)\mapsto (yz : xz : xy)`.
1077
+
1078
+ OUTPUT:
1079
+
1080
+ - a scheme morphism representing the restriction of the standard Cremona transformation from this curve
1081
+ to the proper transform.
1082
+
1083
+ EXAMPLES::
1084
+
1085
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1086
+ sage: C = Curve(x^3*y - z^4 - z^2*x^2, P)
1087
+ sage: C.quadratic_transform()
1088
+ Scheme morphism:
1089
+ From: Projective Plane Curve over Rational Field
1090
+ defined by x^3*y - x^2*z^2 - z^4
1091
+ To: Projective Plane Curve over Rational Field
1092
+ defined by -x^3*y - x*y*z^2 + z^4
1093
+ Defn: Defined on coordinates by sending (x : y : z) to
1094
+ (y*z : x*z : x*y)
1095
+
1096
+ ::
1097
+
1098
+ sage: P.<x,y,z> = ProjectiveSpace(GF(17), 2)
1099
+ sage: C = P.curve([y^7*z^2 - 16*x^9 + x*y*z^7 + 2*z^9])
1100
+ sage: C.quadratic_transform()
1101
+ Scheme morphism:
1102
+ From: Projective Plane Curve over Finite Field of size 17
1103
+ defined by x^9 + y^7*z^2 + x*y*z^7 + 2*z^9
1104
+ To: Projective Plane Curve over Finite Field of size 17
1105
+ defined by 2*x^9*y^7 + x^8*y^6*z^2 + x^9*z^7 + y^7*z^9
1106
+ Defn: Defined on coordinates by sending (x : y : z) to
1107
+ (y*z : x*z : x*y)
1108
+ """
1109
+ PP = self.ambient_space()
1110
+ R = PP.coordinate_ring()
1111
+ L = R.gens()
1112
+ coords = [L[1]*L[2], L[0]*L[2], L[0]*L[1]]
1113
+ G = self.defining_polynomial()(coords)
1114
+ # remove the component of the curve corresponding to the exceptional divisor
1115
+ degs = [G.degree()]*len(L)
1116
+ for F in G.monomials():
1117
+ for i in range(len(L)):
1118
+ degs[i] = min(F.degree(L[i]), degs[i])
1119
+ T = []
1120
+ for item in G.monomial_coefficients().items():
1121
+ tup = tuple([item[0][i] - degs[i] for i in range(len(L))])
1122
+ T.append((tup, item[1]))
1123
+ G = R(dict(T))
1124
+ H = Hom(self, PP.curve(G))
1125
+ phi = H(coords)
1126
+ return phi
1127
+
1128
+ def excellent_position(self, Q):
1129
+ r"""
1130
+ Return a transformation of this curve into one in excellent position with respect to the point ``Q``.
1131
+
1132
+ Here excellent position is defined as in [Ful1989]_. A curve `C` of degree `d` containing the point
1133
+ `(0 : 0 : 1)` with multiplicity `r` is said to be in excellent position if none of the coordinate lines
1134
+ are tangent to `C` at any of the fundamental points `(1 : 0 : 0)`, `(0 : 1 : 0)`, and `(0 : 0 : 1)`, and
1135
+ if the two coordinate lines containing `(0 : 0 : 1)` intersect `C` transversally in `d - r` distinct
1136
+ non-fundamental points, and if the other coordinate line intersects `C` transversally at `d` distinct,
1137
+ non-fundamental points.
1138
+
1139
+ INPUT:
1140
+
1141
+ - ``Q`` -- a point on this curve
1142
+
1143
+ OUTPUT:
1144
+
1145
+ A scheme morphism from this curve to a curve in excellent position that
1146
+ is a restriction of a change of coordinates map of the projective plane.
1147
+
1148
+ EXAMPLES::
1149
+
1150
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1151
+ sage: C = Curve([x*y - z^2], P)
1152
+ sage: Q = P([1,1,1])
1153
+ sage: C.excellent_position(Q)
1154
+ Scheme morphism:
1155
+ From: Projective Plane Curve over Rational Field defined by x*y - z^2
1156
+ To: Projective Plane Curve over Rational Field
1157
+ defined by -x^2 - 3*x*y - 4*y^2 - x*z - 3*y*z
1158
+ Defn: Defined on coordinates by sending (x : y : z) to
1159
+ (-x + 1/2*y + 1/2*z : -1/2*y + 1/2*z : x + 1/2*y - 1/2*z)
1160
+
1161
+ ::
1162
+
1163
+ sage: # needs sage.rings.number_field
1164
+ sage: R.<a> = QQ[]
1165
+ sage: K.<b> = NumberField(a^2 - 3)
1166
+ sage: P.<x,y,z> = ProjectiveSpace(K, 2)
1167
+ sage: C = P.curve([z^2*y^3*x^4 - y^6*x^3 - 4*z^2*y^4*x^3 - 4*z^4*y^2*x^3
1168
+ ....: + 3*y^7*x^2 + 10*z^2*y^5*x^2 + 9*z^4*y^3*x^2
1169
+ ....: + 5*z^6*y*x^2 - 3*y^8*x - 9*z^2*y^6*x - 11*z^4*y^4*x
1170
+ ....: - 7*z^6*y^2*x - 2*z^8*x + y^9 + 2*z^2*y^7 + 3*z^4*y^5
1171
+ ....: + 4*z^6*y^3 + 2*z^8*y])
1172
+ sage: Q = P([1,0,0])
1173
+ sage: C.excellent_position(Q)
1174
+ Scheme morphism:
1175
+ From: Projective Plane Curve over Number Field in b
1176
+ with defining polynomial a^2 - 3
1177
+ defined by -x^3*y^6 + 3*x^2*y^7 - 3*x*y^8 + y^9 + x^4*y^3*z^2
1178
+ - 4*x^3*y^4*z^2 + 10*x^2*y^5*z^2 - 9*x*y^6*z^2
1179
+ + 2*y^7*z^2 - 4*x^3*y^2*z^4 + 9*x^2*y^3*z^4
1180
+ - 11*x*y^4*z^4 + 3*y^5*z^4 + 5*x^2*y*z^6
1181
+ - 7*x*y^2*z^6 + 4*y^3*z^6 - 2*x*z^8 + 2*y*z^8
1182
+ To: Projective Plane Curve over Number Field in b
1183
+ with defining polynomial a^2 - 3
1184
+ defined by 900*x^9 - 7410*x^8*y + 29282*x^7*y^2 - 69710*x^6*y^3
1185
+ + 110818*x^5*y^4 - 123178*x^4*y^5 + 96550*x^3*y^6
1186
+ - 52570*x^2*y^7 + 18194*x*y^8 - 3388*y^9 - 1550*x^8*z
1187
+ + 9892*x^7*y*z - 30756*x^6*y^2*z + 58692*x^5*y^3*z
1188
+ - 75600*x^4*y^4*z + 67916*x^3*y^5*z - 42364*x^2*y^6*z
1189
+ + 16844*x*y^7*z - 3586*y^8*z + 786*x^7*z^2
1190
+ - 3958*x^6*y*z^2 + 9746*x^5*y^2*z^2 - 14694*x^4*y^3*z^2
1191
+ + 15174*x^3*y^4*z^2 - 10802*x^2*y^5*z^2
1192
+ + 5014*x*y^6*z^2 - 1266*y^7*z^2 - 144*x^6*z^3
1193
+ + 512*x^5*y*z^3 - 912*x^4*y^2*z^3 + 1024*x^3*y^3*z^3
1194
+ - 816*x^2*y^4*z^3 + 512*x*y^5*z^3 - 176*y^6*z^3
1195
+ + 8*x^5*z^4 - 8*x^4*y*z^4 - 16*x^3*y^2*z^4
1196
+ + 16*x^2*y^3*z^4 + 8*x*y^4*z^4 - 8*y^5*z^4
1197
+ Defn: Defined on coordinates by sending (x : y : z) to
1198
+ (1/4*y + 1/2*z : -1/4*y + 1/2*z : x + 1/4*y - 1/2*z)
1199
+
1200
+ ::
1201
+
1202
+ sage: # needs sage.rings.number_field sage.symbolic
1203
+ sage: set_verbose(-1)
1204
+ sage: a = QQbar(sqrt(2))
1205
+ sage: P.<x,y,z> = ProjectiveSpace(QQbar, 2)
1206
+ sage: C = Curve([(-1/4*a)*x^3 + (-3/4*a)*x^2*y
1207
+ ....: + (-3/4*a)*x*y^2 + (-1/4*a)*y^3 - 2*x*y*z], P)
1208
+ sage: Q = P([0,0,1])
1209
+ sage: C.excellent_position(Q)
1210
+ Scheme morphism:
1211
+ From: Projective Plane Curve over Algebraic Field defined
1212
+ by (-0.3535533905932738?)*x^3 + (-1.060660171779822?)*x^2*y
1213
+ + (-1.060660171779822?)*x*y^2 + (-0.3535533905932738?)*y^3
1214
+ + (-2)*x*y*z
1215
+ To: Projective Plane Curve over Algebraic Field defined
1216
+ by (-2.828427124746190?)*x^3 + (-2)*x^2*y + 2*y^3
1217
+ + (-2)*x^2*z + 2*y^2*z
1218
+ Defn: Defined on coordinates by sending (x : y : z) to
1219
+ (1/2*x + 1/2*y : (-1/2)*x + 1/2*y : 1/2*x + (-1/2)*y + z)
1220
+ """
1221
+ PP = self.ambient_space()
1222
+ # check that Q is on this curve
1223
+ try:
1224
+ Q = self(Q)
1225
+ except TypeError:
1226
+ raise TypeError("(=%s) must be a point on this curve" % Q)
1227
+ r = self.multiplicity(Q)
1228
+ d = self.degree()
1229
+ # first move Q to (0 : 0 : 1), (1 : 0 : 0), or (0 : 1 : 0)
1230
+ # this makes it easier to construct the main transformation
1231
+ i = 0
1232
+ while Q[i] == 0:
1233
+ i += 1
1234
+ coords = [PP.gens()[j] + Q[j]/Q[i]*PP.gens()[i] for j in range(3)]
1235
+ coords[i] = PP.gens()[i]
1236
+ accoords = [PP.gens()[j] - Q[j]/Q[i]*PP.gens()[i] for j in range(3)] # coords used in map construction
1237
+ accoords[i] = PP.gens()[i]
1238
+ baseC = PP.curve(self.defining_polynomial()(coords))
1239
+ P = [0]*3
1240
+ P[i] = 1
1241
+ P = PP(P)
1242
+ l = [0, 1, 2]
1243
+ l.pop(i)
1244
+ # choose points forming a triangle with one vertex at P to map to the coordinate triangle
1245
+ good = False
1246
+ a = 0
1247
+ while not good:
1248
+ a = a + 1
1249
+ # find points to map to (1 : 0 : 0) and (0 : 1 : 0), not on the curve
1250
+ Px = [0]*3
1251
+ Px[l[0]] = a
1252
+ Px[l[1]] = 1
1253
+ Py = [0]*3
1254
+ Py[l[0]] = -a
1255
+ Py[l[1]] = 1
1256
+ Py[i] = 1
1257
+ try:
1258
+ Px = baseC(Px)
1259
+ Py = baseC(Py)
1260
+ continue
1261
+ except TypeError:
1262
+ pass
1263
+ # by construction, P, Px, Py are linearly independent so the following matrix is invertible
1264
+ M = matrix([[Px[j], Py[j], P[j]] for j in range(3)])
1265
+ # M defines a change of coordinates sending (1 : 0 : 0) to Py, (0 : 1 : 0) to Px, (0 : 0 : 1) to P; the
1266
+ # inverse of the transformation we want, used to create the new defining polynomial
1267
+ coords = [sum([M.row(j)[k]*PP.gens()[k] for k in range(3)]) for j in range(3)]
1268
+ C = PP.curve(baseC.defining_polynomial()(coords))
1269
+ # check tangents at (0 : 0 : 1)
1270
+ T = C.tangents(PP([0, 0, 1]), factor=False)[0]
1271
+ if all(e[0] > 0 for e in T.exponents()) or all(e[1] > 0 for e in T.exponents()):
1272
+ continue
1273
+ # check that the other intersections of C with the exceptional lines are correct
1274
+ need_continue = False
1275
+ for j in range(3):
1276
+ poly = C.defining_polynomial().subs({PP.gens()[j]: 0})
1277
+ # this is a homogeneous polynomial in the other two variables
1278
+ # and so should factor completely into homogeneous linear factors
1279
+ # each corresponding to an intersection point where the jth coord is 0.
1280
+ # check if there are enough roots, up to multiplicity (that is, that PP.gens()[j]
1281
+ # doesn't divide the defining polynomial of C)
1282
+ if poly.degree() != d:
1283
+ need_continue = True
1284
+ break
1285
+ # if j != 2, then there should be d - r multiplicity 1 roots,
1286
+ # besides the root corresponding to (0 : 0 : 1)
1287
+ # if j == 2, then all roots should have multiplicity 1
1288
+ npoly = poly
1289
+ if j != 2:
1290
+ # since (0 : 0 : 1) has multiplicity r, divide out by the highest
1291
+ # shared power of the corresponding variable before doing the resultant computations
1292
+ if j == 0:
1293
+ div_pow = min(e[1] for e in npoly.exponents())
1294
+ npoly = PP.coordinate_ring()({(v0, v1 - div_pow, v2): g
1295
+ for (v0, v1, v2), g in npoly.monomial_coefficients().items()})
1296
+ else:
1297
+ div_pow = min(e[0] for e in npoly.exponents())
1298
+ npoly = PP.coordinate_ring()({(v0 - div_pow, v1, v2): g
1299
+ for (v0, v1, v2), g in npoly.monomial_coefficients().items()})
1300
+ # check the degree again
1301
+ if npoly.degree() != d - r:
1302
+ need_continue = True
1303
+ break
1304
+ # check that npoly isn't a constant now
1305
+ if npoly.degree() > 0:
1306
+ t = 0
1307
+ while npoly.degree(PP.gens()[t]) == 0:
1308
+ t = t + 1
1309
+ if npoly.resultant(npoly.derivative(PP.gens()[t]), PP.gens()[t]) == 0:
1310
+ need_continue = True
1311
+ break
1312
+ else:
1313
+ t = 0
1314
+ while npoly.degree(PP.gens()[t]) == 0:
1315
+ t = t + 1
1316
+ if poly.resultant(poly.derivative(PP.gens()[t]), PP.gens()[t]) == 0:
1317
+ need_continue = True
1318
+ break
1319
+ # check that intersections with the line PP.gens()[j] are transverse.
1320
+ # at a simple point P of the curve, the tangent at that point is
1321
+ # given by F_x(P)*x + F_y(P)*y + F_z(P)*z where F is the defining polynomial
1322
+ # of the curve
1323
+ tmp_l = [0, 1, 2]
1324
+ tmp_l.pop(j)
1325
+ poly1 = npoly.derivative(PP.gens()[tmp_l[0]])
1326
+ poly2 = npoly.derivative(PP.gens()[tmp_l[1]])
1327
+ if poly1.degree() > 0 or poly2.degree() > 0:
1328
+ t = 0
1329
+ while poly1.degree(PP.gens()[t]) == 0 and poly2.degree(PP.gens()[t]) == 0:
1330
+ t = t + 1
1331
+ # maybe a stricter check than necessary
1332
+ if poly1.resultant(poly2, PP.gens()[t]) == 0:
1333
+ need_continue = True
1334
+ break
1335
+ if need_continue:
1336
+ continue
1337
+ good = True
1338
+ # coords for map
1339
+ M = M.inverse()
1340
+ accoords2 = [sum([M.row(j)[k]*PP.gens()[k] for k in range(3)]) for j in range(3)]
1341
+ H = Hom(self, C)
1342
+ phi = H([f(accoords) for f in accoords2])
1343
+ return phi
1344
+
1345
+ def ordinary_model(self):
1346
+ r"""
1347
+ Return a birational map from this curve to a plane curve with only ordinary singularities.
1348
+
1349
+ Currently only implemented over number fields. If not all of the coordinates of the non-ordinary
1350
+ singularities of this curve are contained in its base field, then the domain and codomain of the
1351
+ map returned will be defined over an extension. This curve must be irreducible.
1352
+
1353
+ OUTPUT:
1354
+
1355
+ - a scheme morphism from this curve to a curve with only ordinary singularities that defines a
1356
+ birational map between the two curves.
1357
+
1358
+ EXAMPLES::
1359
+
1360
+ sage: # needs sage.rings.number_field
1361
+ sage: set_verbose(-1)
1362
+ sage: K = QuadraticField(3)
1363
+ sage: P.<x,y,z> = ProjectiveSpace(K, 2)
1364
+ sage: C = Curve([x^5 - K.0*y*z^4], P)
1365
+ sage: C.ordinary_model()
1366
+ Scheme morphism:
1367
+ From: Projective Plane Curve over Number Field in a
1368
+ with defining polynomial x^2 - 3 with a = 1.732050807568878?
1369
+ defined by x^5 + (-a)*y*z^4
1370
+ To: Projective Plane Curve over Number Field in a
1371
+ with defining polynomial x^2 - 3 with a = 1.732050807568878?
1372
+ defined by (-a)*x^5*y + (-4*a)*x^4*y^2 + (-6*a)*x^3*y^3
1373
+ + (-4*a)*x^2*y^4 + (-a)*x*y^5 + (-a - 1)*x^5*z
1374
+ + (-4*a + 5)*x^4*y*z + (-6*a - 10)*x^3*y^2*z
1375
+ + (-4*a + 10)*x^2*y^3*z + (-a - 5)*x*y^4*z + y^5*z
1376
+ Defn: Defined on coordinates by sending (x : y : z) to
1377
+ (-1/4*x^2 - 1/2*x*y + 1/2*x*z + 1/2*y*z - 1/4*z^2 :
1378
+ 1/4*x^2 + 1/2*x*y + 1/2*y*z - 1/4*z^2 :
1379
+ -1/4*x^2 + 1/4*z^2)
1380
+
1381
+ ::
1382
+
1383
+ sage: set_verbose(-1)
1384
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1385
+ sage: C = Curve([y^2*z^2 - x^4 - x^3*z], P)
1386
+ sage: D = C.ordinary_model(); D # long time (2 seconds)
1387
+ Scheme morphism:
1388
+ From: Projective Plane Curve over Rational Field defined
1389
+ by -x^4 - x^3*z + y^2*z^2
1390
+ To: Projective Plane Curve over Rational Field defined
1391
+ by 4*x^6*y^3 - 24*x^5*y^4 + 36*x^4*y^5 + 8*x^6*y^2*z
1392
+ - 40*x^5*y^3*z + 24*x^4*y^4*z + 72*x^3*y^5*z - 4*x^6*y*z^2
1393
+ + 8*x^5*y^2*z^2 - 56*x^4*y^3*z^2 + 104*x^3*y^4*z^2
1394
+ + 44*x^2*y^5*z^2 + 8*x^6*z^3 - 16*x^5*y*z^3
1395
+ - 24*x^4*y^2*z^3 + 40*x^3*y^3*z^3 + 48*x^2*y^4*z^3
1396
+ + 8*x*y^5*z^3 - 8*x^5*z^4 + 36*x^4*y*z^4 - 56*x^3*y^2*z^4
1397
+ + 20*x^2*y^3*z^4 + 40*x*y^4*z^4 - 16*y^5*z^4
1398
+ Defn: Defined on coordinates by sending (x : y : z) to
1399
+ (-3/64*x^4 + 9/64*x^2*y^2 - 3/32*x*y^3 - 1/16*x^3*z
1400
+ - 1/8*x^2*y*z + 1/4*x*y^2*z - 1/16*y^3*z - 1/8*x*y*z^2
1401
+ + 1/16*y^2*z^2 :
1402
+ -1/64*x^4 + 3/64*x^2*y^2 - 1/32*x*y^3 + 1/16*x*y^2*z
1403
+ - 1/16*y^3*z + 1/16*y^2*z^2 :
1404
+ 3/64*x^4 - 3/32*x^3*y + 3/64*x^2*y^2 + 1/16*x^3*z
1405
+ - 3/16*x^2*y*z + 1/8*x*y^2*z - 1/8*x*y*z^2 + 1/16*y^2*z^2)
1406
+ sage: all(D.codomain().is_ordinary_singularity(Q) # long time
1407
+ ....: for Q in D.codomain().singular_points())
1408
+ True
1409
+
1410
+ ::
1411
+
1412
+ sage: set_verbose(-1)
1413
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1414
+ sage: C = Curve([(x^2 + y^2 - y*z - 2*z^2)*(y*z - x^2 + 2*z^2)*z + y^5], P)
1415
+ sage: C.ordinary_model() # long time (5 seconds)
1416
+ Scheme morphism:
1417
+ From: Projective Plane Curve over Number Field in a
1418
+ with defining polynomial y^2 - 2 defined
1419
+ by y^5 - x^4*z - x^2*y^2*z + 2*x^2*y*z^2 + y^3*z^2
1420
+ + 4*x^2*z^3 + y^2*z^3 - 4*y*z^4 - 4*z^5
1421
+ To: Projective Plane Curve over Number Field in a
1422
+ with defining polynomial y^2 - 2 defined
1423
+ by (-29*a + 1)*x^8*y^6 + (10*a + 158)*x^7*y^7
1424
+ + (-109*a - 31)*x^6*y^8 + (-80*a - 198)*x^8*y^5*z
1425
+ + (531*a + 272)*x^7*y^6*z + (170*a - 718)*x^6*y^7*z
1426
+ + (19*a - 636)*x^5*y^8*z + (-200*a - 628)*x^8*y^4*z^2
1427
+ + (1557*a - 114)*x^7*y^5*z^2 + (2197*a - 2449)*x^6*y^6*z^2
1428
+ + (1223*a - 3800)*x^5*y^7*z^2 + (343*a - 1329)*x^4*y^8*z^2
1429
+ + (-323*a - 809)*x^8*y^3*z^3 + (1630*a - 631)*x^7*y^4*z^3
1430
+ + (4190*a - 3126)*x^6*y^5*z^3 + (3904*a - 7110)*x^5*y^6*z^3
1431
+ + (1789*a - 5161)*x^4*y^7*z^3 + (330*a - 1083)*x^3*y^8*z^3
1432
+ + (-259*a - 524)*x^8*y^2*z^4 + (720*a - 605)*x^7*y^3*z^4
1433
+ + (3082*a - 2011)*x^6*y^4*z^4 + (4548*a - 5462)*x^5*y^5*z^4
1434
+ + (2958*a - 6611)*x^4*y^6*z^4 + (994*a - 2931)*x^3*y^7*z^4
1435
+ + (117*a - 416)*x^2*y^8*z^4 + (-108*a - 184)*x^8*y*z^5
1436
+ + (169*a - 168)*x^7*y^2*z^5 + (831*a - 835)*x^6*y^3*z^5
1437
+ + (2225*a - 1725)*x^5*y^4*z^5 + (1970*a - 3316)*x^4*y^5*z^5
1438
+ + (952*a - 2442)*x^3*y^6*z^5 + (217*a - 725)*x^2*y^7*z^5
1439
+ + (16*a - 77)*x*y^8*z^5 + (-23*a - 35)*x^8*z^6
1440
+ + (43*a + 24)*x^7*y*z^6 + (21*a - 198)*x^6*y^2*z^6
1441
+ + (377*a - 179)*x^5*y^3*z^6 + (458*a - 537)*x^4*y^4*z^6
1442
+ + (288*a - 624)*x^3*y^5*z^6 + (100*a - 299)*x^2*y^6*z^6
1443
+ + (16*a - 67)*x*y^7*z^6 - 5*y^8*z^6
1444
+ Defn: Defined on coordinates by sending (x : y : z) to
1445
+ ((-5/128*a - 5/128)*x^4 + (-5/32*a + 5/32)*x^3*y
1446
+ + (-1/16*a + 3/32)*x^2*y^2 + (1/16*a - 1/16)*x*y^3
1447
+ + (1/32*a - 1/32)*y^4 - 1/32*x^3*z + (3/16*a - 5/8)*x^2*y*z
1448
+ + (1/8*a - 5/16)*x*y^2*z + (1/8*a + 5/32)*x^2*z^2
1449
+ + (-3/16*a + 5/16)*x*y*z^2 + (-3/16*a - 1/16)*y^2*z^2
1450
+ + 1/16*x*z^3 + (1/4*a + 1/4)*y*z^3 + (-3/32*a - 5/32)*z^4 :
1451
+ (-5/128*a - 5/128)*x^4 + (5/32*a)*x^3*y
1452
+ + (3/32*a + 3/32)*x^2*y^2 + (-1/16*a)*x*y^3
1453
+ + (-1/32*a - 1/32)*y^4 - 1/32*x^3*z + (-11/32*a)*x^2*y*z
1454
+ + (1/8*a + 5/16)*x*y^2*z + (3/16*a + 1/4)*y^3*z
1455
+ + (1/8*a + 5/32)*x^2*z^2 + (-1/16*a - 3/8)*x*y*z^2
1456
+ + (-3/8*a - 9/16)*y^2*z^2 + 1/16*x*z^3 + (5/16*a + 1/2)*y*z^3
1457
+ + (-3/32*a - 5/32)*z^4 :
1458
+ (1/64*a + 3/128)*x^4 + (-1/32*a - 1/32)*x^3*y
1459
+ + (3/32*a - 9/32)*x^2*y^2 + (1/16*a - 3/16)*x*y^3 - 1/32*y^4
1460
+ + (3/32*a + 1/8)*x^2*y*z + (-1/8*a + 1/8)*x*y^2*z
1461
+ + (-1/16*a)*y^3*z + (-1/16*a - 3/32)*x^2*z^2
1462
+ + (1/16*a + 1/16)*x*y*z^2 + (3/16*a + 3/16)*y^2*z^2
1463
+ + (-3/16*a - 1/4)*y*z^3 + (1/16*a + 3/32)*z^4)
1464
+ """
1465
+ from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics
1466
+
1467
+ def extension(self):
1468
+ # helper function for extending the base field
1469
+ F = self.base_ring()
1470
+ pts = self.change_ring(F.embeddings(QQbar)[0]).rational_points()
1471
+ L = [t for pt in pts for t in pt]
1472
+ K = number_field_elements_from_algebraics(L)[0]
1473
+ if isinstance(K, RationalField):
1474
+ return F.embeddings(F)[0]
1475
+ elif isinstance(F, RationalField):
1476
+ return F.embeddings(K)[0]
1477
+ else:
1478
+ # make sure the defining polynomial variable names are the same for K, N
1479
+ N = NumberField(K.defining_polynomial().parent()(F.defining_polynomial()), str(K.gen()))
1480
+ return N.composite_fields(K, both_maps=True)[0][1]*F.embeddings(N)[0]
1481
+ if self.base_ring() not in NumberFields():
1482
+ raise NotImplementedError("the base ring of this curve must be a number field")
1483
+ if not self.is_irreducible():
1484
+ raise TypeError("this curve must be irreducible")
1485
+ C_orig = self
1486
+ C = self
1487
+ PP = C.ambient_space()
1488
+ # extend the base field if necessary to find all singular points
1489
+ emb = extension(C.singular_subscheme())
1490
+ PP = PP.change_ring(emb)
1491
+ C = C.change_ring(emb)
1492
+ C_orig = C_orig.change_ring(emb)
1493
+ pts = C.singular_points()
1494
+ H = End(C)
1495
+ phi = H(list(C.ambient_space().gens()))
1496
+ while pts:
1497
+ for i in range(len(pts) - 1, -1, -1):
1498
+ try:
1499
+ if C.is_ordinary_singularity(pts[i]):
1500
+ pts.pop(i)
1501
+ except TypeError:
1502
+ pts.pop(i)
1503
+ if pts:
1504
+ temp_exc = C.excellent_position(pts[0])
1505
+ temp_qua = temp_exc.codomain().quadratic_transform()
1506
+ C = temp_qua.codomain()
1507
+ phi = temp_qua*temp_exc*phi
1508
+ # transform the old points
1509
+ for i in range(len(pts) - 1, -1, -1):
1510
+ # find image if it is a point the composition map is defined on
1511
+ try:
1512
+ temp_pt = (temp_qua*temp_exc)(temp_exc.domain()(pts[i]))
1513
+ pts.pop(i)
1514
+ if PP(list(temp_pt)) not in [PP(list(tpt)) for tpt in pts]:
1515
+ pts.append(temp_pt)
1516
+ except (TypeError, ValueError):
1517
+ pass
1518
+ # add points from the intersection of C and the line z
1519
+ PPline = ProjectiveSpace(PP.base_ring(), 1)
1520
+ # make sure the conversion happens in the right order
1521
+ ringH = Hom(PP.coordinate_ring(), PPline.coordinate_ring())
1522
+ psi = ringH(list(PPline.gens()) + [0])
1523
+ X = PPline.subscheme([psi(f) for f in C.singular_subscheme().defining_polynomials()])
1524
+ emb = extension(X)
1525
+ PP = PP.change_ring(emb)
1526
+ phi = phi.change_ring(emb)
1527
+ C = C.change_ring(emb)
1528
+ C_orig = C_orig.change_ring(emb)
1529
+ X = X.change_ring(emb)
1530
+ pts = [PP(pt.change_ring(emb)) for pt in pts]
1531
+ newpts = [PP(list(pt) + [0]) for pt in X.rational_points()]
1532
+ # avoid duplicates
1533
+ for pt in newpts:
1534
+ if PP(list(pt)) not in [PP(list(tpt)) for tpt in pts]:
1535
+ pts.append(pt)
1536
+ return phi
1537
+
1538
+ def is_transverse(self, C, P) -> bool:
1539
+ r"""
1540
+ Return whether the intersection of this curve with the curve ``C`` at the point ``P`` is transverse.
1541
+
1542
+ The intersection at ``P`` is transverse if ``P`` is a nonsingular point of both curves, and if the
1543
+ tangents of the curves at ``P`` are distinct.
1544
+
1545
+ INPUT:
1546
+
1547
+ - ``C`` -- a curve in the ambient space of this curve
1548
+
1549
+ - ``P`` -- a point in the intersection of both curves
1550
+
1551
+ OUTPUT: boolean
1552
+
1553
+ EXAMPLES::
1554
+
1555
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1556
+ sage: C = Curve([x^2 - y^2], P)
1557
+ sage: D = Curve([x - y], P)
1558
+ sage: Q = P([1,1,0])
1559
+ sage: C.is_transverse(D, Q)
1560
+ False
1561
+
1562
+ ::
1563
+
1564
+ sage: # needs sage.rings.number_field
1565
+ sage: K = QuadraticField(-1)
1566
+ sage: P.<x,y,z> = ProjectiveSpace(K, 2)
1567
+ sage: C = Curve([y^2*z - K.0*x^3], P)
1568
+ sage: D = Curve([z*x + y^2], P)
1569
+ sage: Q = P([0,0,1])
1570
+ sage: C.is_transverse(D, Q)
1571
+ False
1572
+
1573
+ ::
1574
+
1575
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1576
+ sage: C = Curve([x^2 - 2*y^2 - 2*z^2], P)
1577
+ sage: D = Curve([y - z], P)
1578
+ sage: Q = P([2,1,1])
1579
+ sage: C.is_transverse(D, Q)
1580
+ True
1581
+ """
1582
+ if not self.intersects_at(C, P):
1583
+ raise TypeError("(=%s) must be a point in the intersection of (=%s) and this curve" % (P, C))
1584
+ if self.is_singular(P) or C.is_singular(P):
1585
+ return False
1586
+
1587
+ # there is only one tangent at a nonsingular point of a plane curve
1588
+ return not self.tangents(P)[0] == C.tangents(P)[0]
1589
+
1590
+
1591
+ class ProjectiveCurve_field(ProjectiveCurve, AlgebraicScheme_subscheme_projective_field):
1592
+ """
1593
+ Projective curves over fields.
1594
+ """
1595
+ _point = ProjectiveCurvePoint_field
1596
+
1597
+ def __init__(self, A, X, category=None):
1598
+ """
1599
+ Initialize.
1600
+
1601
+ EXAMPLES::
1602
+
1603
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1604
+ sage: C = Curve(x*y^2*z^7 - x^10 - x^2*z^8)
1605
+ sage: loads(dumps(C)) == C
1606
+ True
1607
+
1608
+ TESTS::
1609
+
1610
+ sage: P.<x0,x1,x2,x3,x4> = ProjectiveSpace(QQ, 4)
1611
+ sage: C = Curve([x0^4 - x1^2*x4^2 - 19*x4^4, x2^4 - x3^2*x4^2 - 23*x4^4])
1612
+ Traceback (most recent call last):
1613
+ ...
1614
+ ValueError: defining equations (=[x0^4 - x1^2*x4^2 - 19*x4^4, x2^4 - x3^2*x4^2 - 23*x4^4])
1615
+ define a scheme of dimension 2 != 1
1616
+ """
1617
+ super().__init__(A, X, category=category)
1618
+
1619
+ if A.base_ring() not in Fields():
1620
+ raise TypeError("curve not defined over a field")
1621
+
1622
+ d = super(Curve_generic, self).dimension()
1623
+ if d != 1:
1624
+ raise ValueError(f"defining equations (={X}) define a scheme of dimension {d} != 1")
1625
+
1626
+ @lazy_attribute
1627
+ def _genus(self):
1628
+ """
1629
+ The geometric genus of this projective curve.
1630
+
1631
+ TESTS:
1632
+
1633
+ Geometric genus is not defined for geometrically reducible curves. You
1634
+ may get a nonsensical answer if the condition is not met::
1635
+
1636
+ sage: P2.<x,y,z> = ProjectiveSpace(QQ, 2)
1637
+ sage: C = Curve(x^2 + y^2)
1638
+ sage: C.genus() # indirect test
1639
+ -1
1640
+ """
1641
+ return self.defining_ideal().genus()
1642
+
1643
+ def arithmetic_genus(self):
1644
+ r"""
1645
+ Return the arithmetic genus of this projective curve.
1646
+
1647
+ This is the arithmetic genus `p_a(C)` as defined in [Har1977]_. If `P`
1648
+ is the Hilbert polynomial of the defining ideal of this curve, then the
1649
+ arithmetic genus of this curve is `1 - P(0)`.
1650
+
1651
+ EXAMPLES::
1652
+
1653
+ sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
1654
+ sage: C = P.curve([w*z - x^2, w^2 + y^2 + z^2])
1655
+ sage: C.arithmetic_genus()
1656
+ 1
1657
+
1658
+ ::
1659
+
1660
+ sage: P.<x,y,z,w,t> = ProjectiveSpace(GF(7), 4)
1661
+ sage: C = P.curve([t^3 - x*y*w, x^3 + y^3 + z^3, z - w])
1662
+ sage: C.arithmetic_genus()
1663
+ 10
1664
+ """
1665
+ return 1 - self.defining_ideal().hilbert_polynomial()(0)
1666
+
1667
+ def is_complete_intersection(self) -> bool:
1668
+ r"""
1669
+ Return whether this projective curve is a complete intersection.
1670
+
1671
+ EXAMPLES::
1672
+
1673
+ sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
1674
+ sage: C = Curve([x*y - z*w, x^2 - y*w, y^2*w - x*z*w], P)
1675
+ sage: C.is_complete_intersection()
1676
+ False
1677
+
1678
+ ::
1679
+
1680
+ sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
1681
+ sage: C = Curve([y*w - x^2, z*w^2 - x^3], P)
1682
+ sage: C.is_complete_intersection()
1683
+ True
1684
+
1685
+ sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
1686
+ sage: C = Curve([z^2 - y*w, y*z - x*w, y^2 - x*z], P)
1687
+ sage: C.is_complete_intersection()
1688
+ False
1689
+ """
1690
+ singular_lib("sing.lib")
1691
+ simplify = singular_function("simplify")
1692
+ is_ci = singular_function("is_ci")
1693
+
1694
+ # verbose unless printlevel is -1.
1695
+ saved_printlevel = get_printlevel()
1696
+ set_printlevel(-1)
1697
+ id = simplify(self.defining_ideal(), 10)
1698
+ L = is_ci(id)[-1]
1699
+ set_printlevel(saved_printlevel)
1700
+
1701
+ return len(self.ambient_space().gens()) - len(id) == L
1702
+
1703
+ def tangent_line(self, p):
1704
+ """
1705
+ Return the tangent line at the point ``p``.
1706
+
1707
+ INPUT:
1708
+
1709
+ - ``p`` -- a rational point of the curve
1710
+
1711
+ EXAMPLES::
1712
+
1713
+ sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
1714
+ sage: C = Curve([x*y - z*w, x^2 - y*w, y^2*w - x*z*w], P)
1715
+ sage: p = C(1,1,1,1)
1716
+ sage: C.tangent_line(p)
1717
+ Projective Curve over Rational Field
1718
+ defined by -2*x + y + w, -3*x + z + 2*w
1719
+ """
1720
+ for i in range(len(p)):
1721
+ if p[i]:
1722
+ C = self.affine_patch(i)
1723
+ q = p.dehomogenize(i)
1724
+ T = C.tangent_line(q)
1725
+ return T.projective_closure(i, self.ambient_space())
1726
+
1727
+ raise TypeError("{} does not define a point in the projective space".format(p))
1728
+
1729
+
1730
+ class ProjectivePlaneCurve_field(ProjectivePlaneCurve, ProjectiveCurve_field):
1731
+ """
1732
+ Projective plane curves over fields.
1733
+ """
1734
+ _point = ProjectivePlaneCurvePoint_field
1735
+
1736
+ def arithmetic_genus(self):
1737
+ r"""
1738
+ Return the arithmetic genus of this projective curve.
1739
+
1740
+ This is the arithmetic genus `p_a(C)` as defined in [Har1977]_.
1741
+
1742
+ For an irreducible projective plane curve of degree `d`, this is simply
1743
+ `(d - 1)(d - 2)/2`. It need *not* equal the geometric genus (the genus
1744
+ of the normalization of the curve).
1745
+
1746
+ EXAMPLES::
1747
+
1748
+ sage: x,y,z = PolynomialRing(GF(5), 3, 'xyz').gens()
1749
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8); C
1750
+ Projective Plane Curve over Finite Field of size 5
1751
+ defined by -x^9 + y^2*z^7 - x*z^8
1752
+ sage: C.arithmetic_genus()
1753
+ 28
1754
+ sage: C.genus() # geometric
1755
+ 4
1756
+
1757
+ ::
1758
+
1759
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1760
+ sage: C = Curve([y^3*x - x^2*y*z - 7*z^4])
1761
+ sage: C.arithmetic_genus()
1762
+ 3
1763
+ """
1764
+ if self.is_irreducible():
1765
+ # use genus-degree formula
1766
+ d = self.defining_polynomial().total_degree()
1767
+ return Integer(d - 1).binomial(2)
1768
+ return super().arithmetic_genus()
1769
+
1770
+ def fundamental_group(self):
1771
+ r"""
1772
+ Return a presentation of the fundamental group of the complement
1773
+ of ``self``.
1774
+
1775
+ .. NOTE::
1776
+
1777
+ The curve must be defined over the rationals or a number field
1778
+ with an embedding over `\QQbar`.
1779
+
1780
+ .. NOTE::
1781
+
1782
+ This functionality requires the ``sirocco`` package to be installed.
1783
+
1784
+ EXAMPLES::
1785
+
1786
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1787
+ sage: C = P.curve(x^2*z - y^3)
1788
+ sage: C.fundamental_group() # needs sirocco
1789
+ Finitely presented group < x0 | x0^3 >
1790
+ sage: g = P.curve(z*(x^2*z - y^3)).fundamental_group() # needs sirocco
1791
+ sage: g.sorted_presentation() # needs sirocco
1792
+ Finitely presented group < x0, x1 | x1^-1*x0^-1*x1^-1*x0*x1*x0 >
1793
+
1794
+ In the case of number fields, they need to have an embedding
1795
+ into the algebraic field::
1796
+
1797
+ sage: # needs sage.rings.number_field
1798
+ sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0]
1799
+ sage: a
1800
+ -2.236067977499790?*I
1801
+ sage: F = NumberField(a.minpoly(), 'a', embedding=a)
1802
+ sage: P.<x,y,z> = ProjectiveSpace(F, 2)
1803
+ sage: F.inject_variables()
1804
+ Defining a
1805
+ sage: C = P.curve(x^2 + a * y^2)
1806
+ sage: C.fundamental_group() # needs sirocco
1807
+ Finitely presented group < x0 | >
1808
+
1809
+ TESTS::
1810
+
1811
+ sage: # needs sage.combinat
1812
+ sage: F.<x0, x1> = FreeGroup()
1813
+ sage: G = F / [x1^-1*(x1^-1*x0^-1*x1*x0^-1)^2, (x1^-1*x0^-1)^2*x1^-1*(x0*x1)^2*x0]
1814
+ sage: G.order()
1815
+ 320
1816
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1817
+ sage: C = P.curve(z^2*y^3 - z*(33*x*z+2*x^2+8*z^2)*y^2
1818
+ ....: + (21*z^2+21*x*z-x^2)*(z^2+11*x*z-x^2)*y
1819
+ ....: + (x-18*z)*(z^2+11*x*z-x^2)^2)
1820
+ sage: G0 = C.fundamental_group() # needs sirocco, long time (:issue:`39569`)
1821
+ sage: G.is_isomorphic(G0) # needs sirocco, long time (:issue:`39569`)
1822
+ True
1823
+ sage: C = P.curve(z)
1824
+ sage: C.fundamental_group() # needs sirocco
1825
+ Finitely presented group < | >
1826
+ """
1827
+ from sage.schemes.curves.zariski_vankampen import fundamental_group
1828
+ F = self.base_ring()
1829
+ from sage.rings.qqbar import QQbar
1830
+ if QQbar.coerce_map_from(F) is None:
1831
+ raise NotImplementedError("the base field must have an embedding"
1832
+ " to the algebraic field")
1833
+ g = self.defining_polynomial()
1834
+ ring = self.ambient_space().affine_patch(2).coordinate_ring()
1835
+ if g.degree() == 1:
1836
+ return fundamental_group(ring.one())
1837
+ f = ring(self.affine_patch(2).defining_polynomial())
1838
+ if f.degree() == self.degree():
1839
+ return fundamental_group(f, projective=True)
1840
+ else: # in this case, the line at infinity is part of the curve, so the complement lies in the affine patch
1841
+ return fundamental_group(f, projective=False)
1842
+
1843
+ def rational_parameterization(self):
1844
+ r"""
1845
+ Return a rational parameterization of this curve.
1846
+
1847
+ This curve must have rational coefficients and be absolutely irreducible (i.e. irreducible
1848
+ over the algebraic closure of the rational field). The curve must also be rational (have
1849
+ geometric genus zero).
1850
+
1851
+ The rational parameterization may have coefficients in a quadratic extension of the rational
1852
+ field.
1853
+
1854
+ OUTPUT: a birational map between `\mathbb{P}^{1}` and this curve, given as a scheme morphism
1855
+
1856
+ EXAMPLES::
1857
+
1858
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1859
+ sage: C = Curve([y^2*z - x^3], P)
1860
+ sage: C.rational_parameterization()
1861
+ Scheme morphism:
1862
+ From: Projective Space of dimension 1 over Rational Field
1863
+ To: Projective Plane Curve over Rational Field
1864
+ defined by -x^3 + y^2*z
1865
+ Defn: Defined on coordinates by sending (s : t) to
1866
+ (s^2*t : s^3 : t^3)
1867
+
1868
+ ::
1869
+
1870
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1871
+ sage: C = Curve([x^3 - 4*y*z^2 + x*z^2 - x*y*z], P)
1872
+ sage: C.rational_parameterization()
1873
+ Scheme morphism:
1874
+ From: Projective Space of dimension 1 over Rational Field
1875
+ To: Projective Plane Curve over Rational Field
1876
+ defined by x^3 - x*y*z + x*z^2 - 4*y*z^2
1877
+ Defn: Defined on coordinates by sending (s : t) to
1878
+ (4*s^2*t + s*t^2 : s^2*t + t^3 : 4*s^3 + s^2*t)
1879
+
1880
+ ::
1881
+
1882
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
1883
+ sage: C = Curve([x^2 + y^2 + z^2], P)
1884
+ sage: C.rational_parameterization()
1885
+ Scheme morphism:
1886
+ From: Projective Space of dimension 1 over Number Field in a
1887
+ with defining polynomial a^2 + 1
1888
+ To: Projective Plane Curve over Number Field in a
1889
+ with defining polynomial a^2 + 1 defined by x^2 + y^2 + z^2
1890
+ Defn: Defined on coordinates by sending (s : t) to
1891
+ ((-a)*s^2 + (-a)*t^2 : s^2 - t^2 : 2*s*t)
1892
+ """
1893
+ if self.genus():
1894
+ raise TypeError("this curve must have geometric genus zero")
1895
+ if not isinstance(self.base_ring(), RationalField):
1896
+ raise TypeError("this curve must be defined over the rational field")
1897
+
1898
+ singular.lib("paraplanecurves.lib")
1899
+ R = singular.paraPlaneCurve(self.defining_polynomial()) # ring
1900
+ R.set_ring()
1901
+ param = singular('PARA').sage().gens() # ideal
1902
+ R = R.sage()
1903
+
1904
+ C = self.change_ring(R.base_ring())
1905
+ H = Hom(ProjectiveSpace(R.base_ring(), 1, R.gens()), C)
1906
+ return H(param)
1907
+
1908
+ def riemann_surface(self, **kwargs):
1909
+ r"""
1910
+ Return the complex Riemann surface determined by this curve.
1911
+
1912
+ OUTPUT: a :class:`~sage.schemes.riemann_surfaces.riemann_surface.RiemannSurface` object
1913
+
1914
+ EXAMPLES::
1915
+
1916
+ sage: R.<x,y,z> = QQ[]
1917
+ sage: C = Curve(x^3 + 3*y^3 + 5*z^3)
1918
+ sage: C.riemann_surface() # needs sage.graphs
1919
+ Riemann surface defined by polynomial f = x^3 + 3*y^3 + 5 = 0,
1920
+ with 53 bits of precision
1921
+ """
1922
+ return self.affine_patch(2).riemann_surface(**kwargs)
1923
+
1924
+
1925
+ class ProjectivePlaneCurve_finite_field(ProjectivePlaneCurve_field):
1926
+ """
1927
+ Projective plane curves over finite fields
1928
+ """
1929
+ _point = ProjectivePlaneCurvePoint_finite_field
1930
+
1931
+ def rational_points_iterator(self):
1932
+ r"""
1933
+ Return a generator object for the rational points on this curve.
1934
+
1935
+ INPUT:
1936
+
1937
+ - ``self`` -- a projective curve
1938
+
1939
+ OUTPUT: a generator of all the rational points on the curve defined over its base field
1940
+
1941
+ EXAMPLES::
1942
+
1943
+ sage: F = GF(37)
1944
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
1945
+ sage: C = Curve(X^7 + Y*X*Z^5*55 + Y^7*12)
1946
+ sage: len(list(C.rational_points_iterator()))
1947
+ 37
1948
+
1949
+ ::
1950
+
1951
+ sage: F = GF(2)
1952
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
1953
+ sage: C = Curve(X*Y*Z)
1954
+ sage: a = C.rational_points_iterator()
1955
+ sage: next(a)
1956
+ (1 : 0 : 0)
1957
+ sage: next(a)
1958
+ (0 : 1 : 0)
1959
+ sage: next(a)
1960
+ (1 : 1 : 0)
1961
+ sage: next(a)
1962
+ (0 : 0 : 1)
1963
+ sage: next(a)
1964
+ (1 : 0 : 1)
1965
+ sage: next(a)
1966
+ (0 : 1 : 1)
1967
+ sage: next(a)
1968
+ Traceback (most recent call last):
1969
+ ...
1970
+ StopIteration
1971
+
1972
+ ::
1973
+
1974
+ sage: # needs sage.rings.finite_rings
1975
+ sage: F = GF(3^2,'a')
1976
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
1977
+ sage: C = Curve(X^3 + 5*Y^2*Z - 33*X*Y*X)
1978
+ sage: b = C.rational_points_iterator()
1979
+ sage: next(b)
1980
+ (0 : 1 : 0)
1981
+ sage: next(b)
1982
+ (0 : 0 : 1)
1983
+ sage: next(b)
1984
+ (2*a + 2 : a : 1)
1985
+ sage: next(b)
1986
+ (2 : a + 1 : 1)
1987
+ sage: next(b)
1988
+ (a + 1 : 2*a + 1 : 1)
1989
+ sage: next(b)
1990
+ (1 : 2 : 1)
1991
+ sage: next(b)
1992
+ (2*a + 2 : 2*a : 1)
1993
+ sage: next(b)
1994
+ (2 : 2*a + 2 : 1)
1995
+ sage: next(b)
1996
+ (a + 1 : a + 2 : 1)
1997
+ sage: next(b)
1998
+ (1 : 1 : 1)
1999
+ sage: next(b)
2000
+ Traceback (most recent call last):
2001
+ ...
2002
+ StopIteration
2003
+ """
2004
+ g = self.defining_polynomial()
2005
+ K = g.parent().base_ring()
2006
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
2007
+ R = PolynomialRing(K, 'X')
2008
+ X = R.gen()
2009
+ one = K.one()
2010
+ zero = K.zero()
2011
+
2012
+ # the point with Z = 0 = Y
2013
+ try:
2014
+ t = self.point([one, zero, zero])
2015
+ yield t
2016
+ except TypeError:
2017
+ pass
2018
+
2019
+ # points with Z = 0, Y = 1
2020
+ g10 = R(g(X, one, zero))
2021
+ if g10.is_zero():
2022
+ for x in K:
2023
+ yield self.point([x, one, zero])
2024
+ else:
2025
+ for x in g10.roots(multiplicities=False):
2026
+ yield self.point([x, one, zero])
2027
+
2028
+ # points with Z = 1
2029
+ for y in K:
2030
+ gy1 = R(g(X, y, one))
2031
+ if gy1.is_zero():
2032
+ for x in K:
2033
+ yield self.point([x, y, one])
2034
+ else:
2035
+ for x in gy1.roots(multiplicities=False):
2036
+ yield self.point([x, y, one])
2037
+
2038
+ def _points_via_singular(self, sort=True):
2039
+ r"""
2040
+ Return all rational points on this curve, computed using Singular's
2041
+ Brill-Noether implementation.
2042
+
2043
+ INPUT:
2044
+
2045
+ - ``sort`` -- boolean (default: ``True``); if ``True`` return the
2046
+ point list sorted. If ``False``, returns the points in the order
2047
+ computed by Singular.
2048
+
2049
+ EXAMPLES::
2050
+
2051
+ sage: x, y, z = PolynomialRing(GF(5), 3, 'xyz').gens()
2052
+ sage: f = y^2*z^7 - x^9 - x*z^8
2053
+ sage: C = Curve(f); C
2054
+ Projective Plane Curve over Finite Field of size 5 defined by
2055
+ -x^9 + y^2*z^7 - x*z^8
2056
+ sage: C._points_via_singular()
2057
+ [(0 : 0 : 1), (0 : 1 : 0), (2 : 2 : 1), (2 : 3 : 1),
2058
+ (3 : 1 : 1), (3 : 4 : 1)]
2059
+ sage: C._points_via_singular(sort=False) # random
2060
+ [(0 : 1 : 0), (3 : 1 : 1), (3 : 4 : 1), (2 : 2 : 1),
2061
+ (0 : 0 : 1), (2 : 3 : 1)]
2062
+
2063
+
2064
+ .. NOTE::
2065
+
2066
+ The Brill-Noether package does not always work (i.e., the
2067
+ 'bn' algorithm. When it fails a :exc:`RuntimeError` exception is
2068
+ raised.
2069
+ """
2070
+ f = self.defining_polynomial()._singular_()
2071
+ singular = f.parent()
2072
+ singular.lib('brnoeth')
2073
+ try:
2074
+ X1 = f.Adj_div()
2075
+ except (TypeError, RuntimeError) as s:
2076
+ raise RuntimeError(str(s) + "\n\n ** Unable to use the\
2077
+ Brill-Noether Singular package to\
2078
+ compute all points (see above).")
2079
+
2080
+ X2 = singular.NSplaces(1, X1)
2081
+ R = X2[5][1][1]
2082
+ R.set_ring()
2083
+
2084
+ # We use sage_flattened_str_list since iterating through
2085
+ # the entire list through the sage/singular interface directly
2086
+ # would involve hundreds of calls to singular, and timing issues with
2087
+ # the expect interface could crop up. Also, this is vastly
2088
+ # faster (and more robust).
2089
+ v = singular('POINTS').sage_flattened_str_list()
2090
+ pnts = [self(int(v[3*i]), int(v[3*i+1]), int(v[3*i+2]))
2091
+ for i in range(len(v)//3)]
2092
+ # singular always dehomogenizes with respect to the last variable
2093
+ # so if this variable divides the curve equation, we need to add
2094
+ # points at infinity
2095
+ F = self.defining_polynomial()
2096
+ z = F.parent().gens()[-1]
2097
+ if z.divides(F):
2098
+ pnts += [self(1, a, 0) for a in self.base_ring()]
2099
+ pnts += [self(0, 1, 0)]
2100
+ # remove multiple points
2101
+ pnts = list(set(pnts))
2102
+ if sort:
2103
+ pnts.sort()
2104
+ return pnts
2105
+
2106
+ def riemann_roch_basis(self, D):
2107
+ r"""
2108
+ Return a basis for the Riemann-Roch space corresponding to `D`.
2109
+
2110
+ This uses Singular's Brill-Noether implementation.
2111
+
2112
+ INPUT:
2113
+
2114
+ - ``D`` -- a divisor
2115
+
2116
+ OUTPUT: list of function field elements that form a basis of the
2117
+ Riemann-Roch space
2118
+
2119
+ EXAMPLES::
2120
+
2121
+ sage: R.<x,y,z> = GF(2)[]
2122
+ sage: f = x^3*y + y^3*z + x*z^3
2123
+ sage: C = Curve(f); pts = C.rational_points()
2124
+ sage: D = C.divisor([ (4, pts[0]), (4, pts[2]) ])
2125
+ sage: C.riemann_roch_basis(D)
2126
+ [x/y, 1, z/y, z^2/y^2, z/x, z^2/(x*y)]
2127
+
2128
+ ::
2129
+
2130
+ sage: R.<x,y,z> = GF(5)[]
2131
+ sage: f = x^7 + y^7 + z^7
2132
+ sage: C = Curve(f); pts = C.rational_points()
2133
+ sage: D = C.divisor([ (3, pts[0]), (-1,pts[1]), (10, pts[5]) ])
2134
+ sage: C.riemann_roch_basis(D)
2135
+ [(-2*x + y)/(x + y), (-x + z)/(x + y)]
2136
+
2137
+ .. NOTE::
2138
+
2139
+ Currently this only works over prime field and divisors
2140
+ supported on rational points.
2141
+ """
2142
+ F = self.base_ring()
2143
+ if not F.is_prime_field():
2144
+ raise TypeError("only works for curves over prime finite fields")
2145
+
2146
+ f = self.defining_polynomial()._singular_()
2147
+ singular = f.parent()
2148
+ singular.lib('brnoeth')
2149
+ try:
2150
+ X1 = f.Adj_div()
2151
+ except (TypeError, RuntimeError) as s:
2152
+ raise RuntimeError(str(s) + "\n\n ** Unable to use the Brill-Noether Singular package to compute all points (see above).")
2153
+ X2 = singular.NSplaces(1, X1)
2154
+ # retrieve list of all computed closed points (possibly of degree >1)
2155
+ v = X2[3].sage_flattened_str_list()
2156
+ # We use sage_flattened_str_list since iterating through
2157
+ # the entire list through the sage/singular interface directly
2158
+ # would involve hundreds of calls to singular, and timing issues with
2159
+ # the expect interface could crop up. Also, this is vastly
2160
+ # faster (and more robust).
2161
+
2162
+ v = [v[i].partition(',') for i in range(len(v))]
2163
+ pnts = [(int(v[i][0]), int(v[i][2])-1) for i in range(len(v))]
2164
+ # retrieve coordinates of rational points
2165
+ R = X2[5][1][1]
2166
+ R.set_ring()
2167
+ v = singular('POINTS').sage_flattened_str_list()
2168
+ coords = [self(int(v[3*i]), int(v[3*i+1]), int(v[3*i+2])) for i in range(len(v)//3)]
2169
+ # build correct representation of D for singular
2170
+ Dcoeffs = []
2171
+ for x in pnts:
2172
+ if x[0] == 1:
2173
+ Dcoeffs.append(D.coefficient(coords[x[1]]))
2174
+ else:
2175
+ Dcoeffs.append(0)
2176
+ G = singular(','.join(str(x) for x in Dcoeffs), type='intvec')
2177
+ # call singular's brill noether routine and return
2178
+ T = X2[1][2]
2179
+ T.set_ring()
2180
+ LG = G.BrillNoether(X2)
2181
+ LG = [X.split(',\n') for X in LG.sage_structured_str_list()]
2182
+ x, y, z = self.ambient_space().coordinate_ring().gens()
2183
+ vars = {'x': x, 'y': y, 'z': z}
2184
+ V = [(sage_eval(a, vars)/sage_eval(b, vars)) for a, b in LG]
2185
+ return V
2186
+
2187
+ def rational_points(self, algorithm='enum', sort=True):
2188
+ r"""
2189
+ Return the rational points on this curve.
2190
+
2191
+ INPUT:
2192
+
2193
+ - ``algorithm`` -- one of
2194
+
2195
+ - ``'enum'`` -- straightforward enumeration
2196
+
2197
+ - ``'bn'`` -- via Singular's brnoeth package
2198
+
2199
+ - ``sort`` -- boolean (default: ``True``); whether the output
2200
+ points should be sorted. If ``False``, the order of the output
2201
+ is non-deterministic.
2202
+
2203
+ OUTPUT: list of all the rational points on the curve, possibly sorted
2204
+
2205
+ .. NOTE::
2206
+
2207
+ The Brill-Noether package does not always work (i.e., the 'bn'
2208
+ algorithm. When it fails a :exc:`RuntimeError` exception is raised.
2209
+
2210
+ EXAMPLES::
2211
+
2212
+ sage: x, y, z = PolynomialRing(GF(5), 3, 'xyz').gens()
2213
+ sage: f = y^2*z^7 - x^9 - x*z^8
2214
+ sage: C = Curve(f); C
2215
+ Projective Plane Curve over Finite Field of size 5
2216
+ defined by -x^9 + y^2*z^7 - x*z^8
2217
+ sage: C.rational_points()
2218
+ [(0 : 0 : 1), (0 : 1 : 0), (2 : 2 : 1), (2 : 3 : 1),
2219
+ (3 : 1 : 1), (3 : 4 : 1)]
2220
+ sage: C = Curve(x - y + z)
2221
+ sage: C.rational_points()
2222
+ [(0 : 1 : 1), (1 : 1 : 0), (1 : 2 : 1), (2 : 3 : 1),
2223
+ (3 : 4 : 1), (4 : 0 : 1)]
2224
+ sage: C = Curve(x*z + z^2)
2225
+ sage: C.rational_points('all')
2226
+ [(0 : 1 : 0), (1 : 0 : 0), (1 : 1 : 0), (2 : 1 : 0),
2227
+ (3 : 1 : 0), (4 : 0 : 1), (4 : 1 : 0), (4 : 1 : 1),
2228
+ (4 : 2 : 1), (4 : 3 : 1), (4 : 4 : 1)]
2229
+
2230
+ ::
2231
+
2232
+ sage: F = GF(7)
2233
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
2234
+ sage: C = Curve(X^3 + Y^3 - Z^3)
2235
+ sage: C.rational_points()
2236
+ [(0 : 1 : 1), (0 : 2 : 1), (0 : 4 : 1), (1 : 0 : 1), (2 : 0 : 1),
2237
+ (3 : 1 : 0), (4 : 0 : 1), (5 : 1 : 0), (6 : 1 : 0)]
2238
+
2239
+ ::
2240
+
2241
+ sage: F = GF(1237)
2242
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
2243
+ sage: C = Curve(X^7 + 7*Y^6*Z + Z^4*X^2*Y*89)
2244
+ sage: len(C.rational_points())
2245
+ 1237
2246
+
2247
+ ::
2248
+
2249
+ sage: # needs sage.rings.finite_rings
2250
+ sage: F = GF(2^6,'a')
2251
+ sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2)
2252
+ sage: C = Curve(X^5 + 11*X*Y*Z^3 + X^2*Y^3 - 13*Y^2*Z^3)
2253
+ sage: len(C.rational_points())
2254
+ 104
2255
+
2256
+ ::
2257
+
2258
+ sage: R.<x,y,z> = GF(2)[]
2259
+ sage: f = x^3*y + y^3*z + x*z^3
2260
+ sage: C = Curve(f); pts = C.rational_points()
2261
+ sage: pts
2262
+ [(0 : 0 : 1), (0 : 1 : 0), (1 : 0 : 0)]
2263
+ """
2264
+ if algorithm == "enum":
2265
+ points = list(self.rational_points_iterator())
2266
+ if sort:
2267
+ points.sort()
2268
+ return points
2269
+
2270
+ F = self.base_ring()
2271
+ if not F.is_prime_field():
2272
+ raise TypeError("other algorithms only works for curves over prime finite fields")
2273
+
2274
+ if algorithm == "bn":
2275
+ return self._points_via_singular(sort=sort)
2276
+
2277
+ if algorithm == "all":
2278
+ S_enum = self.rational_points(algorithm='enum')
2279
+ S_bn = self.rational_points(algorithm='bn')
2280
+ if S_enum != S_bn:
2281
+ raise RuntimeError("Bug in rational_points -- different\
2282
+ algorithms give different answers for\
2283
+ curve %s!" % self)
2284
+ return S_enum
2285
+
2286
+ raise ValueError(f"No algorithm '{algorithm}' known")
2287
+
2288
+ def random_element(self):
2289
+ """
2290
+ Return a random point on this elliptic/hyperelliptic curve, uniformly chosen
2291
+ among all rational points.
2292
+
2293
+ ALGORITHM:
2294
+
2295
+ Choose the point at infinity with probability `1/(2q + 1)`.
2296
+ Otherwise, take a random element from the field as x-coordinate
2297
+ and compute the possible y-coordinates. Return the i-th
2298
+ possible y-coordinate, where i is randomly chosen to be 0 or 1.
2299
+ If the i-th y-coordinate does not exist (either there is no
2300
+ point with the given x-coordinate or we hit a 2-torsion point
2301
+ with i == 1), try again.
2302
+
2303
+ This gives a uniform distribution because you can imagine
2304
+ `2q + 1` buckets, one for the point at infinity and 2 for each
2305
+ element of the field (representing the x-coordinates). This
2306
+ gives a 1-to-1 map of (hyper)elliptic curve points into buckets. At
2307
+ every iteration, we simply choose a random bucket until we find
2308
+ a bucket containing a point.
2309
+
2310
+ AUTHORS:
2311
+
2312
+ - Jeroen Demeyer (2014-09-09): choose points uniformly random,
2313
+ see :issue:`16951`.
2314
+
2315
+ EXAMPLES::
2316
+
2317
+ sage: k = GF(next_prime(7^5))
2318
+ sage: E = EllipticCurve(k,[2,4])
2319
+ sage: P = E.random_element(); P # random
2320
+ (16740 : 12486 : 1)
2321
+ sage: type(P)
2322
+ <class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
2323
+ sage: P in E
2324
+ True
2325
+
2326
+ ::
2327
+
2328
+ sage: # needs sage.rings.finite_rings
2329
+ sage: k.<a> = GF(7^5)
2330
+ sage: E = EllipticCurve(k,[2,4])
2331
+ sage: P = E.random_element(); P # random
2332
+ (5*a^4 + 3*a^3 + 2*a^2 + a + 4 : 2*a^4 + 3*a^3 + 4*a^2 + a + 5 : 1)
2333
+ sage: type(P)
2334
+ <class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
2335
+ sage: P in E
2336
+ True
2337
+
2338
+ ::
2339
+
2340
+ sage: # needs sage.rings.finite_rings
2341
+ sage: k.<a> = GF(2^5)
2342
+ sage: E = EllipticCurve(k,[a^2,a,1,a+1,1])
2343
+ sage: P = E.random_element(); P # random
2344
+ (a^4 + a : a^4 + a^3 + a^2 : 1)
2345
+ sage: type(P)
2346
+ <class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_finite_field'>
2347
+ sage: P in E
2348
+ True
2349
+
2350
+ Ensure that the entire point set is reachable::
2351
+
2352
+ sage: E = EllipticCurve(GF(11), [2,1])
2353
+ sage: S = set()
2354
+ sage: while len(S) < E.cardinality():
2355
+ ....: S.add(E.random_element())
2356
+
2357
+ TESTS:
2358
+
2359
+ See :issue:`8311`::
2360
+
2361
+ sage: E = EllipticCurve(GF(3), [0,0,0,2,2])
2362
+ sage: E.random_element()
2363
+ (0 : 1 : 0)
2364
+ sage: E.cardinality()
2365
+ 1
2366
+
2367
+ sage: E = EllipticCurve(GF(2), [0,0,1,1,1])
2368
+ sage: E.random_point()
2369
+ (0 : 1 : 0)
2370
+ sage: E.cardinality()
2371
+ 1
2372
+
2373
+ sage: # needs sage.rings.finite_rings
2374
+ sage: F.<a> = GF(4)
2375
+ sage: E = EllipticCurve(F, [0, 0, 1, 0, a])
2376
+ sage: E.random_point()
2377
+ (0 : 1 : 0)
2378
+ sage: E.cardinality()
2379
+ 1
2380
+
2381
+ Sampling from points on a hyperelliptic curve::
2382
+
2383
+ sage: R.<x,y> = GF(13)[]
2384
+ sage: C = HyperellipticCurve(y^2 + 3*x^2*y - (x^5 + x + 1))
2385
+ sage: P = C.random_element(); P # random
2386
+ (0 : 1 : 0)
2387
+ sage: P in C
2388
+ True
2389
+ """
2390
+ from sage.schemes.elliptic_curves.ell_finite_field import (
2391
+ EllipticCurve_finite_field,
2392
+ )
2393
+ from sage.schemes.hyperelliptic_curves.hyperelliptic_finite_field import (
2394
+ HyperellipticCurve_finite_field,
2395
+ )
2396
+ if not isinstance(self, (EllipticCurve_finite_field, HyperellipticCurve_finite_field)):
2397
+ raise NotImplementedError("only implemented for elliptic and hyperelliptic curves over finite fields")
2398
+
2399
+ k = self.base_ring()
2400
+ n = 2 * k.order() + 1
2401
+
2402
+ from sage.rings.integer_ring import ZZ
2403
+ while True:
2404
+ # Choose the point at infinity with probability 1/(2q + 1)
2405
+ i = ZZ.random_element(n)
2406
+ if not i:
2407
+ return self(0, 1, 0)
2408
+
2409
+ v = self.lift_x(k.random_element(), all=True)
2410
+ try:
2411
+ return v[i % 2]
2412
+ except IndexError:
2413
+ pass
2414
+
2415
+ random_point = random_element
2416
+
2417
+
2418
+ class IntegralProjectiveCurve(ProjectiveCurve_field):
2419
+ """
2420
+ Integral projective curve.
2421
+ """
2422
+ _point = IntegralProjectiveCurvePoint
2423
+ _closed_point = IntegralProjectiveCurveClosedPoint
2424
+
2425
+ def __init__(self, A, f):
2426
+ """
2427
+ Initialize.
2428
+
2429
+ TESTS::
2430
+
2431
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2432
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2433
+ sage: loads(dumps(C)) == C
2434
+ True
2435
+ """
2436
+ super().__init__(A, f)
2437
+ ideal = self.defining_ideal()
2438
+ gs = self.ambient_space().gens()
2439
+ for i in range(self.ngens()):
2440
+ if gs[i] not in ideal:
2441
+ self._open_affine_index = i
2442
+ break
2443
+ else:
2444
+ raise ValueError("no projective curve defined")
2445
+
2446
+ @lazy_attribute
2447
+ def _open_affine(self):
2448
+ r"""
2449
+ An affine patch of the curve.
2450
+
2451
+ TESTS::
2452
+
2453
+ sage: P2.<x,y,z> = ProjectiveSpace(GF(7), 2)
2454
+ sage: C = Curve(x^3 + 5*z^3 - y^2*z, P2)
2455
+ sage: C._open_affine
2456
+ Affine Plane Curve over Finite Field of size 7 defined by -y^2*z - 2*z^3 + 1
2457
+ """
2458
+ return self.affine_patch(self._open_affine_index)
2459
+
2460
+ def __getstate__(self):
2461
+ r"""
2462
+ Remove some attributes that cause issues before pickling.
2463
+ These are easily recomputed anyway.
2464
+
2465
+ TESTS:
2466
+
2467
+ Make sure that pickling and unpickling works after
2468
+ accessing attributes. See :issue:`41265`::
2469
+
2470
+ sage: P2.<x,y,z> = ProjectiveSpace(GF(7), 2)
2471
+ sage: C = Curve(x^3 + 5*z^3 - y^2*z, P2)
2472
+ sage: C._open_affine is not None
2473
+ True
2474
+ sage: C._map_from_function_field is not None
2475
+ True
2476
+ sage: loaded = loads(dumps(C))
2477
+ sage: loaded == C
2478
+ True
2479
+ sage: loaded._open_affine == C._open_affine
2480
+ True
2481
+ """
2482
+ state = super().__getstate__()
2483
+ # We don't use del in case these properties haven't been accessed and cached yet
2484
+ state.pop('_open_affine', None)
2485
+ state.pop('_map_from_function_field', None)
2486
+ return state
2487
+
2488
+ def function_field(self):
2489
+ """
2490
+ Return the function field of this curve.
2491
+
2492
+ EXAMPLES::
2493
+
2494
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
2495
+ sage: C = Curve(x^2 + y^2 + z^2, P)
2496
+ sage: C.function_field()
2497
+ Function field in z defined by z^2 + y^2 + 1
2498
+
2499
+ ::
2500
+
2501
+ sage: # needs sage.rings.finite_rings
2502
+ sage: P.<x,y,z> = ProjectiveSpace(GF(4), 2)
2503
+ sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5)
2504
+ sage: C.function_field()
2505
+ Function field in z defined by z^5 + y*z^3 + y^5 + 1
2506
+ """
2507
+ return self._function_field
2508
+
2509
+ @lazy_attribute
2510
+ def _genus(self):
2511
+ """
2512
+ The geometric genus of the curve.
2513
+
2514
+ EXAMPLES::
2515
+
2516
+ sage: P.<x,y,z> = ProjectiveSpace(GF(4), 2)
2517
+ sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5)
2518
+ sage: C.genus() # indirect doctest
2519
+ 1
2520
+ """
2521
+ return self._open_affine.genus()
2522
+
2523
+ def __call__(self, *args):
2524
+ """
2525
+ Return a rational point, a pointset or a function depending on ``args``.
2526
+
2527
+ EXAMPLES::
2528
+
2529
+ sage: # needs sage.rings.finite_rings
2530
+ sage: P.<x,y,z> = ProjectiveSpace(GF(4), 2)
2531
+ sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5)
2532
+ sage: C(1,1,1)
2533
+ (1 : 1 : 1)
2534
+ sage: C(y/z)
2535
+ (y/(y^5 + 1))*z^4 + (y^2/(y^5 + 1))*z^2
2536
+ sage: C(GF(4^2))
2537
+ Set of rational points of Closed subscheme of Projective Space of
2538
+ dimension 2 over Finite Field in z4 of size 2^4 defined by:
2539
+ x^5 + y^5 + x*y*z^3 + z^5
2540
+ """
2541
+ try:
2542
+ return super().__call__(*args)
2543
+ except TypeError as e:
2544
+ try:
2545
+ return self.function(*args)
2546
+ except AttributeError:
2547
+ raise e
2548
+
2549
+ def function(self, f):
2550
+ """
2551
+ Return the function field element corresponding to ``f``.
2552
+
2553
+ INPUT:
2554
+
2555
+ - ``f`` -- a fraction of homogeneous polynomials of the coordinate ring
2556
+ of the ambient space of the curve
2557
+
2558
+ OUTPUT: an element of the function field
2559
+
2560
+ EXAMPLES::
2561
+
2562
+ sage: # needs sage.rings.finite_rings
2563
+ sage: P.<x,y,z> = ProjectiveSpace(GF(4), 2)
2564
+ sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5)
2565
+ sage: f = C.function(x/y); f
2566
+ 1/y
2567
+ sage: f.divisor()
2568
+ Place (1/y, 1/y^2*z^2 + z2/y*z + 1)
2569
+ + Place (1/y, 1/y^2*z^2 + ((z2 + 1)/y)*z + 1)
2570
+ + Place (1/y, 1/y*z + 1)
2571
+ - Place (y, z^2 + z2*z + 1)
2572
+ - Place (y, z^2 + (z2 + 1)*z + 1)
2573
+ - Place (y, z + 1)
2574
+ """
2575
+ S = self.ambient_space().coordinate_ring()
2576
+ phi = self._map_to_function_field
2577
+ num = S(f.numerator())
2578
+ den = S(f.denominator())
2579
+ if num.degree() != den.degree():
2580
+ raise ValueError("not define a function on the curve")
2581
+
2582
+ return phi(num)/phi(den)
2583
+
2584
+ def coordinate_functions(self, i=None):
2585
+ """
2586
+ Return the coordinate functions for the ``i``-th affine patch.
2587
+
2588
+ If ``i`` is ``None``, return the homogeneous coordinate functions.
2589
+
2590
+ EXAMPLES::
2591
+
2592
+ sage: # needs sage.rings.finite_rings
2593
+ sage: P.<x,y,z> = ProjectiveSpace(GF(4), 2)
2594
+ sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5)
2595
+ sage: C.coordinate_functions(0)
2596
+ (y, z)
2597
+ sage: C.coordinate_functions(1)
2598
+ (1/y, 1/y*z)
2599
+ """
2600
+ coords = self._coordinate_functions
2601
+ if i is None:
2602
+ return coords
2603
+ inv = ~coords[i]
2604
+ return tuple([coords[j]*inv for j in range(len(coords)) if j != i])
2605
+
2606
+ def pull_from_function_field(self, f):
2607
+ """
2608
+ Return the fraction corresponding to ``f``.
2609
+
2610
+ INPUT:
2611
+
2612
+ - ``f`` -- an element of the function field
2613
+
2614
+ OUTPUT:
2615
+
2616
+ A fraction of homogeneous polynomials in the coordinate ring of the
2617
+ ambient space of the curve.
2618
+
2619
+ EXAMPLES::
2620
+
2621
+ sage: # needs sage.rings.finite_rings
2622
+ sage: P.<x,y,z> = ProjectiveSpace(GF(4), 2)
2623
+ sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5)
2624
+ sage: F = C.function_field()
2625
+ sage: C.pull_from_function_field(F.gen())
2626
+ z/x
2627
+ sage: C.pull_from_function_field(F.one())
2628
+ 1
2629
+ sage: C.pull_from_function_field(F.zero())
2630
+ 0
2631
+ sage: f1 = F.gen()
2632
+ sage: f2 = F.base_ring().gen()
2633
+ sage: C.function(C.pull_from_function_field(f1)) == f1
2634
+ True
2635
+ sage: C.function(C.pull_from_function_field(f2)) == f2
2636
+ True
2637
+ """
2638
+ return self._map_from_function_field(f)
2639
+
2640
+ @lazy_attribute
2641
+ def _function_field(self):
2642
+ """
2643
+ Return the abstract function field of the curve.
2644
+
2645
+ TESTS::
2646
+
2647
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2648
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2649
+ sage: C._function_field
2650
+ Function field in z defined by z^8 + 4*y^2*z^7 + 1
2651
+ """
2652
+ return self._open_affine._function_field
2653
+
2654
+ @lazy_attribute
2655
+ def _map_to_function_field(self):
2656
+ """
2657
+ Return the map to the function field of the curve.
2658
+
2659
+ TESTS::
2660
+
2661
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2662
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2663
+ sage: C._map_to_function_field
2664
+ Ring morphism:
2665
+ From: Multivariate Polynomial Ring in x, y, z over Finite Field of size 5
2666
+ To: Function field in z defined by z^8 + 4*y^2*z^7 + 1
2667
+ Defn: x |--> 1
2668
+ y |--> y
2669
+ z |--> z
2670
+ """
2671
+ F = self._function_field
2672
+ S = self.ambient_space().coordinate_ring()
2673
+ return hom(S, F, self._coordinate_functions)
2674
+
2675
+ @lazy_attribute
2676
+ def _coordinate_functions(self):
2677
+ """
2678
+ Return the homogeneous coordinate functions of the curve.
2679
+
2680
+ TESTS::
2681
+
2682
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2683
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2684
+ sage: C._coordinate_functions
2685
+ (1, y, z)
2686
+ """
2687
+ # homogeneous coordinate functions
2688
+ coords = list(self._open_affine._coordinate_functions)
2689
+ coords.insert(self._open_affine_index, self._function_field.one())
2690
+ return tuple(coords)
2691
+
2692
+ @lazy_attribute
2693
+ def _map_from_function_field(self):
2694
+ """
2695
+ Return the map from the function field of the curve.
2696
+
2697
+ TESTS::
2698
+
2699
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2700
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2701
+ sage: F = C.function_field()
2702
+ sage: f = F.random_element()
2703
+ sage: C.function(C._map_from_function_field(f)) == f
2704
+ True
2705
+ """
2706
+ S = self.ambient_space().coordinate_ring()
2707
+ phi = self._open_affine._nonsingular_model[2]
2708
+ i = self._open_affine_index
2709
+
2710
+ def m(f):
2711
+ pf = phi(f)
2712
+ num = S(pf.numerator()).homogenize(i)
2713
+ den = S(pf.denominator()).homogenize(i)
2714
+ return num / den * S.gen(i) ** (den.total_degree() - num.total_degree())
2715
+
2716
+ return m
2717
+
2718
+ @lazy_attribute
2719
+ def _singularities(self):
2720
+ """
2721
+ Return a list of the pairs of a singular closed point and the places above it.
2722
+
2723
+ TESTS::
2724
+
2725
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2726
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2727
+ sage: C._singularities
2728
+ [(Point (x, z), [Place (1/y, 1/y*z^5 + 4*y*z^4 + 1/y^2*z)])]
2729
+ sage: D = Curve(x)
2730
+ sage: D._singularities
2731
+ []
2732
+ """
2733
+ S = self.ambient_space().coordinate_ring()
2734
+ to_F = self._map_to_function_field
2735
+ sing = self.singular_subscheme() # singular locus
2736
+
2737
+ # for each affine patch, places on which the dehomogenized polynomials
2738
+ # defining the singular locus are collected.
2739
+ places = []
2740
+ for i in range(self.ngens()):
2741
+ denom = self._coordinate_functions[i]
2742
+ if denom:
2743
+ funcs = []
2744
+ for p in S._first_ngens(i) + sing.defining_polynomials():
2745
+ f = to_F(p)/denom**p.degree()
2746
+ if not f.is_zero():
2747
+ funcs.append(f)
2748
+
2749
+ if funcs:
2750
+ f = funcs.pop()
2751
+ pls = f.zeros()
2752
+ for f in funcs:
2753
+ pls = [p for p in pls if f.valuation(p) > 0]
2754
+
2755
+ places.extend(pls)
2756
+
2757
+ # compute closed points below the places lying on the singular locus,
2758
+ # and then collect places lying on each closed points
2759
+ points_and_places = []
2760
+ for place in places:
2761
+ p = self.place_to_closed_point(place)
2762
+ for q, places in points_and_places:
2763
+ if p == q:
2764
+ places.append(place)
2765
+ break
2766
+ else: # new singularity
2767
+ points_and_places.append((p, [place]))
2768
+
2769
+ return points_and_places
2770
+
2771
+ def singular_closed_points(self):
2772
+ """
2773
+ Return the singular closed points of the curve.
2774
+
2775
+ EXAMPLES::
2776
+
2777
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
2778
+ sage: C = Curve(y^2*z - x^3, P)
2779
+ sage: C.singular_closed_points()
2780
+ [Point (x, y)]
2781
+
2782
+ ::
2783
+
2784
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2785
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2786
+ sage: C.singular_closed_points()
2787
+ [Point (x, z)]
2788
+ """
2789
+ return [p[0] for p in self._singularities]
2790
+
2791
+ @cached_method
2792
+ def place_to_closed_point(self, place):
2793
+ """
2794
+ Return the closed point at the place.
2795
+
2796
+ INPUT:
2797
+
2798
+ - ``place`` -- a place of the function field of the curve
2799
+
2800
+ EXAMPLES::
2801
+
2802
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2803
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2804
+ sage: pls = C.places()
2805
+ sage: C.place_to_closed_point(pls[-1])
2806
+ Point (x - 2*z, y - 2*z)
2807
+ sage: pls2 = C.places(2)
2808
+ sage: C.place_to_closed_point(pls2[0])
2809
+ Point (y^2 + y*z + z^2, x + y)
2810
+ """
2811
+ F = self.function_field()
2812
+
2813
+ A = self.ambient_space()
2814
+ S = A.coordinate_ring().change_ring(order='degrevlex') # homogeneous coordinate ring
2815
+
2816
+ # prepare coordinates for the affine patch containing the place
2817
+ vals = [f.valuation(place) for f in self._coordinate_functions]
2818
+ imin = vals.index(min(vals))
2819
+ R = S.remove_var(S.gen(imin))
2820
+ hcoords = self._coordinate_functions
2821
+ coords = [hcoords[i]/hcoords[imin] for i in range(S.ngens()) if i != imin]
2822
+
2823
+ k, from_k, to_k = place.residue_field()
2824
+ V, from_V, to_V = k.vector_space(F.constant_base_field(), map=True)
2825
+
2826
+ # implement an FGLM-like algorithm
2827
+ e = [0 for i in range(R.ngens())]
2828
+ basis = [R.one()]
2829
+ basis_vecs = [to_V(k.one())] # represent as a vector
2830
+
2831
+ gens = []
2832
+ gens_lts = []
2833
+ terminate = False
2834
+ while True: # check FGLM termination condition
2835
+ # compute next exponent in degree reverse lexicographical order
2836
+ j = R.ngens() - 1
2837
+ while j > 0 and not e[j]:
2838
+ j -= 1
2839
+
2840
+ if not j: # j is zero
2841
+ if terminate:
2842
+ break
2843
+ terminate = True
2844
+ d = e[0]
2845
+ e[0] = 0
2846
+ e[-1] = d + 1
2847
+ else:
2848
+ e[j] -= 1
2849
+ e[j-1] += 1
2850
+
2851
+ m = R.monomial(*e)
2852
+ if any(g.divides(m) for g in gens_lts):
2853
+ continue
2854
+
2855
+ prod = 1
2856
+ for i in range(R.ngens()):
2857
+ prod *= coords[i]**e[i]
2858
+ vec = to_V(to_k(prod)) # represent as a vector
2859
+ mat = matrix(basis_vecs)
2860
+ try:
2861
+ s = mat.solve_left(vec)
2862
+ except ValueError: # no solution
2863
+ basis.append(m)
2864
+ basis_vecs.append(vec)
2865
+ terminate = False
2866
+ continue
2867
+
2868
+ gens.append(m - sum([s[i] * basis[i] for i in range(len(basis))]))
2869
+ gens_lts.append(m)
2870
+
2871
+ gens_homo = [S(g).homogenize(imin) for g in gens]
2872
+ prime = S.ideal(gens_homo).groebner_basis().ideal()
2873
+
2874
+ return self._closed_point(self, prime, len(basis))
2875
+
2876
+ def places_on(self, point):
2877
+ """
2878
+ Return the places on the closed point.
2879
+
2880
+ INPUT:
2881
+
2882
+ - ``point`` -- a closed point of the curve
2883
+
2884
+ EXAMPLES::
2885
+
2886
+ sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
2887
+ sage: C = Curve(x*y*z^4 - x^6 - y^6)
2888
+ sage: C.singular_closed_points()
2889
+ [Point (x, y)]
2890
+ sage: p, = _
2891
+ sage: C.places_on(p)
2892
+ [Place (1/y, 1/y^2*z, 1/y^3*z^2, 1/y^4*z^3),
2893
+ Place (y, y*z, y*z^2, y*z^3)]
2894
+ sage: pl1, pl2 =_
2895
+ sage: C.place_to_closed_point(pl1)
2896
+ Point (x, y)
2897
+ sage: C.place_to_closed_point(pl2)
2898
+ Point (x, y)
2899
+
2900
+ ::
2901
+
2902
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2903
+ sage: C = Curve(x^2*z - y^3)
2904
+ sage: [C.places_on(p) for p in C.closed_points()]
2905
+ [[Place (1/y)],
2906
+ [Place (y)],
2907
+ [Place (y + 1)],
2908
+ [Place (y + 2)],
2909
+ [Place (y + 3)],
2910
+ [Place (y + 4)]]
2911
+ """
2912
+ prime = point.prime_ideal()
2913
+
2914
+ # determine the affine patch where the point lies
2915
+ S = prime.ring()
2916
+ for i in range(S.ngens()):
2917
+ if S.gen(i) not in prime:
2918
+ break
2919
+
2920
+ phi = self._map_to_function_field
2921
+ denom = self._coordinate_functions[i]
2922
+ gs = [phi(f) / denom**f.degree() for f in prime.gens()]
2923
+ fs = [g for g in gs if not g.is_zero()]
2924
+ f = fs.pop()
2925
+ return [p for p in f.zeros()
2926
+ if all(f.valuation(p) > 0 for f in fs)]
2927
+
2928
+ def jacobian(self, model, base_div=None):
2929
+ """
2930
+ Return the Jacobian of this curve.
2931
+
2932
+ INPUT:
2933
+
2934
+ - ``model`` -- model to use for arithmetic
2935
+
2936
+ - ``base_div`` -- an effective divisor for the model
2937
+
2938
+ The degree of the base divisor should satisfy certain degree condition
2939
+ corresponding to the model used. The following table lists these
2940
+ conditions. Let `g` be the geometric genus of the curve.
2941
+
2942
+ - ``hess``: ideal-based arithmetic; requires base divisor of degree `g`
2943
+
2944
+ - ``km_large``: Khuri-Makdisi's large model; requires base divisor of
2945
+ degree at least `2g + 1`
2946
+
2947
+ - ``km_medium``: Khuri-Makdisi's medium model; requires base divisor of
2948
+ degree at least `2g + 1`
2949
+
2950
+ - ``km_small``: Khuri-Makdisi's small model requires base divisor of
2951
+ degree at least `g + 1`
2952
+
2953
+ We assume the curve (or its function field) has a rational place. If a
2954
+ base divisor is not given, one is chosen using a rational place.
2955
+
2956
+ EXAMPLES::
2957
+
2958
+ sage: A.<x,y> = AffineSpace(GF(5), 2)
2959
+ sage: C = Curve(y^2*(x^3 - 1) - (x^3 - 2)).projective_closure()
2960
+ sage: J = C.jacobian(model='hess'); J
2961
+ Jacobian of Projective Plane Curve over Finite Field of size 5
2962
+ defined by 2*x0^5 - x0^2*x1^3 - x0^3*x2^2 + x1^3*x2^2 (Hess model)
2963
+ sage: J.base_divisor().degree() == C.genus()
2964
+ True
2965
+ """
2966
+ return self.function_field().jacobian(model, base_div, curve=self)
2967
+
2968
+
2969
+ class IntegralProjectiveCurve_finite_field(IntegralProjectiveCurve):
2970
+ """
2971
+ Integral projective curve over a finite field.
2972
+
2973
+ INPUT:
2974
+
2975
+ - ``A`` -- an ambient projective space
2976
+
2977
+ - ``f`` -- homogeneous polynomials defining the curve
2978
+
2979
+ EXAMPLES::
2980
+
2981
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
2982
+ sage: C = Curve(y^2*z^7 - x^9 - x*z^8)
2983
+ sage: C.function_field()
2984
+ Function field in z defined by z^8 + 4*y^2*z^7 + 1
2985
+ sage: C.closed_points()
2986
+ [Point (x, z),
2987
+ Point (x, y),
2988
+ Point (x - 2*z, y + 2*z),
2989
+ Point (x + 2*z, y + z),
2990
+ Point (x + 2*z, y - z),
2991
+ Point (x - 2*z, y - 2*z)]
2992
+ """
2993
+ _point = IntegralProjectiveCurvePoint_finite_field
2994
+
2995
+ def places(self, degree=1):
2996
+ """
2997
+ Return all places on the curve of the ``degree``.
2998
+
2999
+ INPUT:
3000
+
3001
+ - ``degree`` -- positive integer
3002
+
3003
+ EXAMPLES::
3004
+
3005
+ sage: P.<x,y,z> = ProjectiveSpace(GF(5), 2)
3006
+ sage: C = Curve(x^2*z - y^3)
3007
+ sage: C.places()
3008
+ [Place (1/y),
3009
+ Place (y),
3010
+ Place (y + 1),
3011
+ Place (y + 2),
3012
+ Place (y + 3),
3013
+ Place (y + 4)]
3014
+ sage: C.places(2)
3015
+ [Place (y^2 + 2),
3016
+ Place (y^2 + 3),
3017
+ Place (y^2 + y + 1),
3018
+ Place (y^2 + y + 2),
3019
+ Place (y^2 + 2*y + 3),
3020
+ Place (y^2 + 2*y + 4),
3021
+ Place (y^2 + 3*y + 3),
3022
+ Place (y^2 + 3*y + 4),
3023
+ Place (y^2 + 4*y + 1),
3024
+ Place (y^2 + 4*y + 2)]
3025
+ """
3026
+ F = self.function_field()
3027
+ return F.places(degree)
3028
+
3029
+ def closed_points(self, degree=1):
3030
+ """
3031
+ Return a list of closed points of ``degree`` of the curve.
3032
+
3033
+ INPUT:
3034
+
3035
+ - ``degree`` -- positive integer
3036
+
3037
+ EXAMPLES::
3038
+
3039
+ sage: # needs sage.rings.finite_rings
3040
+ sage: A.<x,y> = AffineSpace(GF(9),2)
3041
+ sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x-2)
3042
+ sage: Cp = C.projective_closure()
3043
+ sage: Cp.closed_points()
3044
+ [Point (x0, x1),
3045
+ Point (x0 + (-z2 - 1)*x2, x1),
3046
+ Point (x0 + (z2 + 1)*x2, x1),
3047
+ Point (x0 + z2*x2, x1 + (z2 - 1)*x2),
3048
+ Point (x0 + (-z2)*x2, x1 + (-z2 + 1)*x2),
3049
+ Point (x0 + (-z2 - 1)*x2, x1 + (-z2 - 1)*x2),
3050
+ Point (x0 + (z2 + 1)*x2, x1 + (z2 + 1)*x2),
3051
+ Point (x0 + (z2 - 1)*x2, x1 + z2*x2),
3052
+ Point (x0 + (-z2 + 1)*x2, x1 + (-z2)*x2),
3053
+ Point (x0 + x2, x1 - x2),
3054
+ Point (x0 - x2, x1 + x2)]
3055
+ """
3056
+ F = self.function_field()
3057
+ places_above = F.places(degree)
3058
+
3059
+ points = []
3060
+
3061
+ # consider singular points
3062
+ for p in self.singular_closed_points():
3063
+ if p.degree() == degree:
3064
+ points.append(p)
3065
+ for place in p.places():
3066
+ if place.degree() == degree:
3067
+ places_above.remove(place)
3068
+
3069
+ for place in places_above:
3070
+ p = self.place_to_closed_point(place)
3071
+ assert p.degree() == degree # sanity check
3072
+ points.append(p)
3073
+
3074
+ return points
3075
+
3076
+ @cached_method
3077
+ def L_polynomial(self, name='t'):
3078
+ """
3079
+ Return the L-polynomial of this possibly singular curve.
3080
+
3081
+ INPUT:
3082
+
3083
+ - ``name`` -- (default: ``t``) name of the variable of the polynomial
3084
+
3085
+ EXAMPLES::
3086
+
3087
+ sage: A.<x,y> = AffineSpace(GF(3), 2)
3088
+ sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2)
3089
+ sage: Cbar = C.projective_closure()
3090
+ sage: Cbar.L_polynomial()
3091
+ 9*t^4 - 3*t^3 + t^2 - t + 1
3092
+ """
3093
+ F = self.function_field()
3094
+ L = F.L_polynomial()
3095
+
3096
+ R = L.parent()
3097
+ T = R.gen()
3098
+
3099
+ f = R.one()
3100
+ for p, places in self._singularities:
3101
+ for place in places:
3102
+ f = f * (1 - T**place.degree())
3103
+ f = f // (1 - T**p.degree())
3104
+
3105
+ return L * f
3106
+
3107
+ def number_of_rational_points(self, r=1):
3108
+ """
3109
+ Return the number of rational points of the curve with
3110
+ constant field extended by degree ``r``.
3111
+
3112
+ INPUT:
3113
+
3114
+ - ``r`` -- positive integer (default: `1`)
3115
+
3116
+ EXAMPLES::
3117
+
3118
+ sage: # needs sage.rings.finite_rings
3119
+ sage: A.<x,y> = AffineSpace(GF(3), 2)
3120
+ sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2)
3121
+ sage: Cbar = C.projective_closure()
3122
+ sage: Cbar.number_of_rational_points(3)
3123
+ 21
3124
+ sage: D = Cbar.change_ring(Cbar.base_ring().extension(3))
3125
+ sage: D.base_ring()
3126
+ Finite Field in z3 of size 3^3
3127
+ sage: len(D.closed_points())
3128
+ 21
3129
+ """
3130
+ q = self.base_ring().order()
3131
+ L = self.L_polynomial()
3132
+ Lp = L.derivative()
3133
+
3134
+ R = IntegerRing()[[L.parent().gen()]] # power series ring
3135
+ L = R(L)
3136
+ Lp = R(Lp)
3137
+
3138
+ f = R(Lp / L, prec=r)
3139
+ n = f[r-1] + q**r + 1
3140
+
3141
+ return n
3142
+
3143
+
3144
+ class IntegralProjectivePlaneCurve(IntegralProjectiveCurve, ProjectivePlaneCurve_field):
3145
+ _point = IntegralProjectivePlaneCurvePoint
3146
+
3147
+
3148
+ class IntegralProjectivePlaneCurve_finite_field(IntegralProjectiveCurve_finite_field,
3149
+ ProjectivePlaneCurve_finite_field):
3150
+ """
3151
+ Integral projective plane curve over a finite field.
3152
+
3153
+ INPUT:
3154
+
3155
+ - ``A`` -- ambient projective plane
3156
+
3157
+ - ``f`` -- a homogeneous equation that defines the curve
3158
+
3159
+ EXAMPLES::
3160
+
3161
+ sage: # needs sage.rings.finite_rings
3162
+ sage: A.<x,y> = AffineSpace(GF(9), 2)
3163
+ sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2)
3164
+ sage: Cb = C.projective_closure()
3165
+ sage: Cb.singular_closed_points()
3166
+ [Point (x0, x1)]
3167
+ sage: Cb.function_field()
3168
+ Function field in y defined by y^2 + 2*x^5 + 2*x^4 + x^3 + x + 1
3169
+ """
3170
+ _point = IntegralProjectivePlaneCurvePoint_finite_field
3171
+
3172
+
3173
+ def Hasse_bounds(q, genus=1):
3174
+ r"""
3175
+ Return the Hasse-Weil bounds for the cardinality of a nonsingular
3176
+ curve defined over `\GF{q}` of given ``genus``.
3177
+
3178
+ INPUT:
3179
+
3180
+ - ``q`` -- integer; a prime power
3181
+
3182
+ - ``genus`` -- nonnegative integer (default: 1)
3183
+
3184
+ OUTPUT: tuple; the Hasse bounds (lb,ub) for the cardinality of a curve of
3185
+ genus ``genus`` defined over `\GF{q}`
3186
+
3187
+ EXAMPLES::
3188
+
3189
+ sage: Hasse_bounds(2)
3190
+ (1, 5)
3191
+ sage: Hasse_bounds(next_prime(10^30)) # needs sage.libs.pari
3192
+ (999999999999998000000000000058, 1000000000000002000000000000058)
3193
+ """
3194
+ if genus == 1:
3195
+ rq = (4*q).isqrt()
3196
+ else:
3197
+ rq = (4*(genus**2)*q).isqrt()
3198
+ return (q+1-rq, q+1+rq)
3199
+
3200
+
3201
+ # Fix pickles from changing class names and plane_curves folder name
3202
+ register_unpickle_override('sage.schemes.plane_curves.projective_curve',
3203
+ 'ProjectiveCurve_generic', ProjectivePlaneCurve)