passagemath-pari 10.6.32__cp314-cp314-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-pari might be problematic. Click here for more details.
- PARIKernel/__init__.py +2 -0
- PARIKernel/__main__.py +5 -0
- PARIKernel/io.cpython-314-x86_64-linux-musl.so +0 -0
- PARIKernel/io.pxd +7 -0
- PARIKernel/io.pyx +84 -0
- PARIKernel/kernel.cpython-314-x86_64-linux-musl.so +0 -0
- PARIKernel/kernel.pyx +260 -0
- PARIKernel/paridecl.pxd +95 -0
- PARIKernel/svg.cpython-314-x86_64-linux-musl.so +0 -0
- PARIKernel/svg.pyx +52 -0
- cypari2/__init__.py +8 -0
- cypari2/auto_paridecl.pxd +1070 -0
- cypari2/closure.cpython-314-x86_64-linux-musl.so +0 -0
- cypari2/closure.pxd +5 -0
- cypari2/closure.pyx +246 -0
- cypari2/convert.cpython-314-x86_64-linux-musl.so +0 -0
- cypari2/convert.pxd +80 -0
- cypari2/convert.pyx +613 -0
- cypari2/custom_block.cpython-314-x86_64-linux-musl.so +0 -0
- cypari2/custom_block.pyx +30 -0
- cypari2/cypari.h +13 -0
- cypari2/gen.cpython-314-x86_64-linux-musl.so +0 -0
- cypari2/gen.pxd +69 -0
- cypari2/gen.pyx +4819 -0
- cypari2/handle_error.cpython-314-x86_64-linux-musl.so +0 -0
- cypari2/handle_error.pxd +7 -0
- cypari2/handle_error.pyx +232 -0
- cypari2/pari_instance.cpython-314-x86_64-linux-musl.so +0 -0
- cypari2/pari_instance.pxd +27 -0
- cypari2/pari_instance.pyx +1438 -0
- cypari2/paridecl.pxd +5353 -0
- cypari2/paripriv.pxd +34 -0
- cypari2/pycore_long.h +98 -0
- cypari2/pycore_long.pxd +9 -0
- cypari2/stack.cpython-314-x86_64-linux-musl.so +0 -0
- cypari2/stack.pxd +27 -0
- cypari2/stack.pyx +278 -0
- cypari2/string_utils.cpython-314-x86_64-linux-musl.so +0 -0
- cypari2/string_utils.pxd +29 -0
- cypari2/string_utils.pyx +65 -0
- cypari2/types.pxd +147 -0
- passagemath_pari-10.6.32.data/data/etc/jupyter/nbconfig/notebook.d/gp-mode.json +5 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/kernels/pari_jupyter/kernel.js +28 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/kernels/pari_jupyter/kernel.json +6 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/kernels/pari_jupyter/logo-64x64.png +0 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/kernels/xeus-gp/kernel.json +13 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/kernels/xeus-gp/logo-32x32.png +0 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/kernels/xeus-gp/logo-64x64.png +0 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/kernels/xeus-gp/logo-svg.svg +75 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/nbextensions/gp-mode/gp.js +284 -0
- passagemath_pari-10.6.32.data/data/share/jupyter/nbextensions/gp-mode/main.js +15 -0
- passagemath_pari-10.6.32.dist-info/METADATA +209 -0
- passagemath_pari-10.6.32.dist-info/RECORD +331 -0
- passagemath_pari-10.6.32.dist-info/WHEEL +5 -0
- passagemath_pari-10.6.32.dist-info/top_level.txt +4 -0
- passagemath_pari.libs/libcrypto-f04afe95.so.3 +0 -0
- passagemath_pari.libs/libflint-fd6f12fc.so.21.0.0 +0 -0
- passagemath_pari.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_pari.libs/libgf2x-9e30c3e3.so.3.0.0 +0 -0
- passagemath_pari.libs/libgfortran-2c33b284.so.5.0.0 +0 -0
- passagemath_pari.libs/libgivaro-9a94c711.so.9.2.1 +0 -0
- passagemath_pari.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_pari.libs/libgmpxx-9e08595c.so.4.7.0 +0 -0
- passagemath_pari.libs/libgsl-42cda06f.so.28.0.0 +0 -0
- passagemath_pari.libs/libmpfr-aaecbfc0.so.6.2.1 +0 -0
- passagemath_pari.libs/libncursesw-9c9e32c3.so.6.5 +0 -0
- passagemath_pari.libs/libntl-26885ca2.so.44.0.1 +0 -0
- passagemath_pari.libs/libopenblasp-r0-905cb27d.3.29.so +0 -0
- passagemath_pari.libs/libpari-gmp-tls-f31f908f.so.2.17.2 +0 -0
- passagemath_pari.libs/libquadmath-bb76a5fc.so.0.0.0 +0 -0
- passagemath_pari.libs/libreadline-06542304.so.8.2 +0 -0
- passagemath_pari.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
- passagemath_pari.libs/libuuid-f3770415.so.1.3.0 +0 -0
- passagemath_pari.libs/libxeus-735780ff.so.13.1.0 +0 -0
- passagemath_pari.libs/libxeus-zmq-c68577b4.so.6.0.1 +0 -0
- passagemath_pari.libs/libzmq-1ba9a3da.so.5.2.5 +0 -0
- sage/all__sagemath_pari.py +26 -0
- sage/databases/all__sagemath_pari.py +7 -0
- sage/databases/conway.py +274 -0
- sage/ext/all__sagemath_pari.py +1 -0
- sage/ext/memory.cpython-314-x86_64-linux-musl.so +0 -0
- sage/ext/memory.pyx +98 -0
- sage/ext_data/pari/buzzard/DimensionSk.g +286 -0
- sage/ext_data/pari/buzzard/Tpprog.g +179 -0
- sage/ext_data/pari/buzzard/genusn.g +129 -0
- sage/ext_data/pari/dokchitser/computel.gp +740 -0
- sage/ext_data/pari/dokchitser/computel.gp.template +740 -0
- sage/ext_data/pari/dokchitser/ex-bsw +43 -0
- sage/ext_data/pari/dokchitser/ex-chgen +48 -0
- sage/ext_data/pari/dokchitser/ex-chqua +37 -0
- sage/ext_data/pari/dokchitser/ex-delta +35 -0
- sage/ext_data/pari/dokchitser/ex-eisen +30 -0
- sage/ext_data/pari/dokchitser/ex-gen2 +38 -0
- sage/ext_data/pari/dokchitser/ex-gen3 +49 -0
- sage/ext_data/pari/dokchitser/ex-gen4 +54 -0
- sage/ext_data/pari/dokchitser/ex-nf +48 -0
- sage/ext_data/pari/dokchitser/ex-shin +50 -0
- sage/ext_data/pari/dokchitser/ex-tau2 +30 -0
- sage/ext_data/pari/dokchitser/ex-zeta +27 -0
- sage/ext_data/pari/dokchitser/ex-zeta2 +47 -0
- sage/ext_data/pari/dokchitser/testall +13 -0
- sage/ext_data/pari/simon/ell.gp +2129 -0
- sage/ext_data/pari/simon/ellQ.gp +2151 -0
- sage/ext_data/pari/simon/ellcommon.gp +126 -0
- sage/ext_data/pari/simon/qfsolve.gp +722 -0
- sage/ext_data/pari/simon/resultant3.gp +306 -0
- sage/groups/all__sagemath_pari.py +3 -0
- sage/groups/pari_group.py +175 -0
- sage/interfaces/all__sagemath_pari.py +1 -0
- sage/interfaces/genus2reduction.py +464 -0
- sage/interfaces/gp.py +1114 -0
- sage/libs/all__sagemath_pari.py +2 -0
- sage/libs/linkages/__init__.py +1 -0
- sage/libs/linkages/padics/API.pxi +617 -0
- sage/libs/linkages/padics/Polynomial_ram.pxi +388 -0
- sage/libs/linkages/padics/Polynomial_shared.pxi +554 -0
- sage/libs/linkages/padics/__init__.py +1 -0
- sage/libs/linkages/padics/fmpz_poly_unram.pxi +869 -0
- sage/libs/linkages/padics/mpz.pxi +691 -0
- sage/libs/linkages/padics/relaxed/API.pxi +518 -0
- sage/libs/linkages/padics/relaxed/__init__.py +1 -0
- sage/libs/linkages/padics/relaxed/flint.pxi +543 -0
- sage/libs/linkages/padics/unram_shared.pxi +247 -0
- sage/libs/pari/__init__.py +210 -0
- sage/libs/pari/all.py +5 -0
- sage/libs/pari/convert_flint.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/pari/convert_flint.pxd +14 -0
- sage/libs/pari/convert_flint.pyx +159 -0
- sage/libs/pari/convert_gmp.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/pari/convert_gmp.pxd +14 -0
- sage/libs/pari/convert_gmp.pyx +210 -0
- sage/libs/pari/convert_sage.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/pari/convert_sage.pxd +16 -0
- sage/libs/pari/convert_sage.pyx +588 -0
- sage/libs/pari/convert_sage_complex_double.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/pari/convert_sage_complex_double.pxd +14 -0
- sage/libs/pari/convert_sage_complex_double.pyx +132 -0
- sage/libs/pari/convert_sage_matrix.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/pari/convert_sage_matrix.pyx +106 -0
- sage/libs/pari/convert_sage_real_double.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/pari/convert_sage_real_double.pxd +5 -0
- sage/libs/pari/convert_sage_real_double.pyx +14 -0
- sage/libs/pari/convert_sage_real_mpfr.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/pari/convert_sage_real_mpfr.pxd +7 -0
- sage/libs/pari/convert_sage_real_mpfr.pyx +108 -0
- sage/libs/pari/misc.cpython-314-x86_64-linux-musl.so +0 -0
- sage/libs/pari/misc.pxd +4 -0
- sage/libs/pari/misc.pyx +26 -0
- sage/libs/pari/tests.py +1848 -0
- sage/matrix/all__sagemath_pari.py +1 -0
- sage/matrix/matrix_integer_pari.cpython-314-x86_64-linux-musl.so +0 -0
- sage/matrix/matrix_integer_pari.pyx +187 -0
- sage/matrix/matrix_rational_pari.cpython-314-x86_64-linux-musl.so +0 -0
- sage/matrix/matrix_rational_pari.pyx +160 -0
- sage/quadratic_forms/all__sagemath_pari.py +10 -0
- sage/quadratic_forms/genera/all.py +9 -0
- sage/quadratic_forms/genera/genus.py +3506 -0
- sage/quadratic_forms/genera/normal_form.py +1519 -0
- sage/quadratic_forms/genera/spinor_genus.py +243 -0
- sage/quadratic_forms/qfsolve.py +255 -0
- sage/quadratic_forms/quadratic_form__automorphisms.py +427 -0
- sage/quadratic_forms/quadratic_form__genus.py +141 -0
- sage/quadratic_forms/quadratic_form__local_density_interfaces.py +140 -0
- sage/quadratic_forms/quadratic_form__local_normal_form.py +421 -0
- sage/quadratic_forms/quadratic_form__local_representation_conditions.py +889 -0
- sage/quadratic_forms/quadratic_form__mass.py +69 -0
- sage/quadratic_forms/quadratic_form__mass__Conway_Sloane_masses.py +663 -0
- sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py +373 -0
- sage/quadratic_forms/quadratic_form__siegel_product.py +198 -0
- sage/quadratic_forms/special_values.py +323 -0
- sage/rings/all__sagemath_pari.py +15 -0
- sage/rings/factorint_pari.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/factorint_pari.pyx +80 -0
- sage/rings/finite_rings/all__sagemath_pari.py +1 -0
- sage/rings/finite_rings/element_givaro.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/finite_rings/element_givaro.pxd +91 -0
- sage/rings/finite_rings/element_givaro.pyx +1769 -0
- sage/rings/finite_rings/element_ntl_gf2e.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/finite_rings/element_ntl_gf2e.pxd +22 -0
- sage/rings/finite_rings/element_ntl_gf2e.pyx +1333 -0
- sage/rings/finite_rings/element_pari_ffelt.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/finite_rings/element_pari_ffelt.pxd +13 -0
- sage/rings/finite_rings/element_pari_ffelt.pyx +1441 -0
- sage/rings/finite_rings/finite_field_givaro.py +612 -0
- sage/rings/finite_rings/finite_field_pari_ffelt.py +238 -0
- sage/rings/finite_rings/hom_finite_field_givaro.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/finite_rings/hom_finite_field_givaro.pxd +28 -0
- sage/rings/finite_rings/hom_finite_field_givaro.pyx +280 -0
- sage/rings/finite_rings/residue_field_givaro.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/finite_rings/residue_field_givaro.pyx +133 -0
- sage/rings/finite_rings/residue_field_pari_ffelt.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/finite_rings/residue_field_pari_ffelt.pyx +128 -0
- sage/rings/function_field/all__sagemath_pari.py +1 -0
- sage/rings/function_field/valuation.py +1450 -0
- sage/rings/function_field/valuation_ring.py +212 -0
- sage/rings/number_field/all__sagemath_pari.py +14 -0
- sage/rings/number_field/totallyreal.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/number_field/totallyreal.pyx +509 -0
- sage/rings/number_field/totallyreal_data.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/number_field/totallyreal_data.pxd +26 -0
- sage/rings/number_field/totallyreal_data.pyx +928 -0
- sage/rings/number_field/totallyreal_phc.py +144 -0
- sage/rings/number_field/totallyreal_rel.py +1018 -0
- sage/rings/padics/CA_template.pxi +1847 -0
- sage/rings/padics/CA_template_header.pxi +50 -0
- sage/rings/padics/CR_template.pxi +2563 -0
- sage/rings/padics/CR_template_header.pxi +57 -0
- sage/rings/padics/FM_template.pxi +1575 -0
- sage/rings/padics/FM_template_header.pxi +50 -0
- sage/rings/padics/FP_template.pxi +2176 -0
- sage/rings/padics/FP_template_header.pxi +57 -0
- sage/rings/padics/all.py +3 -0
- sage/rings/padics/all__sagemath_pari.py +11 -0
- sage/rings/padics/common_conversion.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/common_conversion.pxd +15 -0
- sage/rings/padics/common_conversion.pyx +508 -0
- sage/rings/padics/eisenstein_extension_generic.py +232 -0
- sage/rings/padics/factory.py +3623 -0
- sage/rings/padics/generic_nodes.py +1615 -0
- sage/rings/padics/lattice_precision.py +2889 -0
- sage/rings/padics/morphism.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/morphism.pxd +11 -0
- sage/rings/padics/morphism.pyx +366 -0
- sage/rings/padics/padic_base_generic.py +467 -0
- sage/rings/padics/padic_base_leaves.py +1235 -0
- sage/rings/padics/padic_capped_absolute_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/padic_capped_absolute_element.pxd +15 -0
- sage/rings/padics/padic_capped_absolute_element.pyx +520 -0
- sage/rings/padics/padic_capped_relative_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/padic_capped_relative_element.pxd +14 -0
- sage/rings/padics/padic_capped_relative_element.pyx +614 -0
- sage/rings/padics/padic_extension_generic.py +990 -0
- sage/rings/padics/padic_extension_leaves.py +738 -0
- sage/rings/padics/padic_fixed_mod_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/padic_fixed_mod_element.pxd +15 -0
- sage/rings/padics/padic_fixed_mod_element.pyx +584 -0
- sage/rings/padics/padic_floating_point_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/padic_floating_point_element.pxd +14 -0
- sage/rings/padics/padic_floating_point_element.pyx +447 -0
- sage/rings/padics/padic_generic_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/padic_generic_element.pxd +48 -0
- sage/rings/padics/padic_generic_element.pyx +4642 -0
- sage/rings/padics/padic_lattice_element.py +1342 -0
- sage/rings/padics/padic_printing.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/padic_printing.pxd +38 -0
- sage/rings/padics/padic_printing.pyx +1505 -0
- sage/rings/padics/padic_relaxed_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/padic_relaxed_element.pxd +56 -0
- sage/rings/padics/padic_relaxed_element.pyx +18 -0
- sage/rings/padics/padic_relaxed_errors.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/padic_relaxed_errors.pxd +11 -0
- sage/rings/padics/padic_relaxed_errors.pyx +71 -0
- sage/rings/padics/padic_template_element.pxi +1212 -0
- sage/rings/padics/padic_template_element_header.pxi +50 -0
- sage/rings/padics/padic_valuation.py +1423 -0
- sage/rings/padics/pow_computer_flint.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/pow_computer_flint.pxd +38 -0
- sage/rings/padics/pow_computer_flint.pyx +641 -0
- sage/rings/padics/pow_computer_relative.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/pow_computer_relative.pxd +29 -0
- sage/rings/padics/pow_computer_relative.pyx +415 -0
- sage/rings/padics/qadic_flint_CA.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/qadic_flint_CA.pxd +21 -0
- sage/rings/padics/qadic_flint_CA.pyx +130 -0
- sage/rings/padics/qadic_flint_CR.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/qadic_flint_CR.pxd +13 -0
- sage/rings/padics/qadic_flint_CR.pyx +172 -0
- sage/rings/padics/qadic_flint_FM.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/qadic_flint_FM.pxd +14 -0
- sage/rings/padics/qadic_flint_FM.pyx +111 -0
- sage/rings/padics/qadic_flint_FP.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/qadic_flint_FP.pxd +12 -0
- sage/rings/padics/qadic_flint_FP.pyx +165 -0
- sage/rings/padics/relative_extension_leaves.py +429 -0
- sage/rings/padics/relative_ramified_CA.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/relative_ramified_CA.pxd +9 -0
- sage/rings/padics/relative_ramified_CA.pyx +33 -0
- sage/rings/padics/relative_ramified_CR.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/relative_ramified_CR.pxd +8 -0
- sage/rings/padics/relative_ramified_CR.pyx +33 -0
- sage/rings/padics/relative_ramified_FM.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/relative_ramified_FM.pxd +9 -0
- sage/rings/padics/relative_ramified_FM.pyx +33 -0
- sage/rings/padics/relative_ramified_FP.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/padics/relative_ramified_FP.pxd +8 -0
- sage/rings/padics/relative_ramified_FP.pyx +33 -0
- sage/rings/padics/relaxed_template.pxi +4229 -0
- sage/rings/padics/relaxed_template_header.pxi +160 -0
- sage/rings/padics/tests.py +35 -0
- sage/rings/padics/tutorial.py +341 -0
- sage/rings/padics/unramified_extension_generic.py +335 -0
- sage/rings/padics/witt_vector.py +917 -0
- sage/rings/padics/witt_vector_ring.py +934 -0
- sage/rings/pari_ring.py +235 -0
- sage/rings/polynomial/all__sagemath_pari.py +1 -0
- sage/rings/polynomial/padics/all.py +1 -0
- sage/rings/polynomial/padics/polynomial_padic.py +360 -0
- sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +1324 -0
- sage/rings/polynomial/padics/polynomial_padic_flat.py +72 -0
- sage/rings/power_series_pari.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/power_series_pari.pxd +6 -0
- sage/rings/power_series_pari.pyx +934 -0
- sage/rings/tate_algebra.py +1282 -0
- sage/rings/tate_algebra_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/tate_algebra_element.pxd +49 -0
- sage/rings/tate_algebra_element.pyx +3464 -0
- sage/rings/tate_algebra_ideal.cpython-314-x86_64-linux-musl.so +0 -0
- sage/rings/tate_algebra_ideal.pxd +7 -0
- sage/rings/tate_algebra_ideal.pyx +1307 -0
- sage/rings/valuation/all.py +7 -0
- sage/rings/valuation/augmented_valuation.py +2118 -0
- sage/rings/valuation/developing_valuation.py +362 -0
- sage/rings/valuation/gauss_valuation.py +812 -0
- sage/rings/valuation/inductive_valuation.py +1686 -0
- sage/rings/valuation/limit_valuation.py +946 -0
- sage/rings/valuation/mapped_valuation.py +656 -0
- sage/rings/valuation/scaled_valuation.py +322 -0
- sage/rings/valuation/trivial_valuation.py +382 -0
- sage/rings/valuation/valuation.py +1119 -0
- sage/rings/valuation/valuation_space.py +1615 -0
- sage/rings/valuation/valuations_catalog.py +10 -0
- sage/rings/valuation/value_group.py +697 -0
- sage/schemes/all__sagemath_pari.py +1 -0
- sage/schemes/elliptic_curves/all__sagemath_pari.py +1 -0
- sage/schemes/elliptic_curves/descent_two_isogeny_pari.cpython-314-x86_64-linux-musl.so +0 -0
- sage/schemes/elliptic_curves/descent_two_isogeny_pari.pyx +46 -0
- sage_wheels/bin/gp +0 -0
- sage_wheels/bin/gp2c +0 -0
- sage_wheels/bin/gp2c-run +57 -0
- sage_wheels/bin/xeus-gp +0 -0
- sage_wheels/share/gp2c/func.dsc +18414 -0
|
@@ -0,0 +1,1519 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-pari
|
|
2
|
+
# sage.doctest: needs sage.libs.pari sage.rings.padics
|
|
3
|
+
r"""
|
|
4
|
+
Normal forms for `p`-adic quadratic and bilinear forms
|
|
5
|
+
|
|
6
|
+
We represent a quadratic or bilinear form by its `n \times n` Gram matrix `G`.
|
|
7
|
+
Then two `p`-adic forms `G` and `G'` are integrally equivalent if and only if
|
|
8
|
+
there is a matrix `B` in `GL(n,\ZZ_p)` such that `G' = B G B^T`.
|
|
9
|
+
|
|
10
|
+
This module allows the computation of a normal form. This means that two
|
|
11
|
+
`p`-adic forms are integrally equivalent if and only if they have the same
|
|
12
|
+
normal form. Further, we can compute a transformation into normal form
|
|
13
|
+
(up to finite precision).
|
|
14
|
+
|
|
15
|
+
EXAMPLES::
|
|
16
|
+
|
|
17
|
+
sage: from sage.quadratic_forms.genera.normal_form import p_adic_normal_form
|
|
18
|
+
sage: G1 = Matrix(ZZ,4, [2, 0, 0, 1, 0, 2, 0, 1, 0, 0, 4, 2, 1, 1, 2, 6])
|
|
19
|
+
sage: G1
|
|
20
|
+
[2 0 0 1]
|
|
21
|
+
[0 2 0 1]
|
|
22
|
+
[0 0 4 2]
|
|
23
|
+
[1 1 2 6]
|
|
24
|
+
sage: G2 = Matrix(ZZ,4, [2, 1, 1, 0, 1, 2, 0, 0, 1, 0, 2, 0, 0, 0, 0, 16])
|
|
25
|
+
sage: G2
|
|
26
|
+
[ 2 1 1 0]
|
|
27
|
+
[ 1 2 0 0]
|
|
28
|
+
[ 1 0 2 0]
|
|
29
|
+
[ 0 0 0 16]
|
|
30
|
+
|
|
31
|
+
A computation reveals that both forms are equivalent over `\ZZ_2`::
|
|
32
|
+
|
|
33
|
+
sage: D1, U1 = p_adic_normal_form(G1,2, precision=30)
|
|
34
|
+
sage: D2, U2 = p_adic_normal_form(G1,2, precision=30)
|
|
35
|
+
sage: D1
|
|
36
|
+
[ 2 1 0 0]
|
|
37
|
+
[ 1 2 0 0]
|
|
38
|
+
[ 0 0 2^2 + 2^3 0]
|
|
39
|
+
[ 0 0 0 2^4]
|
|
40
|
+
sage: D2
|
|
41
|
+
[ 2 1 0 0]
|
|
42
|
+
[ 1 2 0 0]
|
|
43
|
+
[ 0 0 2^2 + 2^3 0]
|
|
44
|
+
[ 0 0 0 2^4]
|
|
45
|
+
|
|
46
|
+
Moreover, we have computed the `2`-adic isomorphism::
|
|
47
|
+
|
|
48
|
+
sage: U = U2.inverse()*U1
|
|
49
|
+
sage: U*G1*U.T
|
|
50
|
+
[ 2 2^31 + 2^32 2^32 + 2^33 1]
|
|
51
|
+
[2^31 + 2^32 2 2^32 1]
|
|
52
|
+
[2^32 + 2^33 2^32 2^2 2]
|
|
53
|
+
[ 1 1 2 2 + 2^2]
|
|
54
|
+
|
|
55
|
+
As you can see this isomorphism is only up to the precision we set before::
|
|
56
|
+
|
|
57
|
+
sage: (U*G1*U.T).change_ring(IntegerModRing(2^30))
|
|
58
|
+
[2 0 0 1]
|
|
59
|
+
[0 2 0 1]
|
|
60
|
+
[0 0 4 2]
|
|
61
|
+
[1 1 2 6]
|
|
62
|
+
|
|
63
|
+
If you are only interested if the forms are isomorphic,
|
|
64
|
+
there are much faster ways::
|
|
65
|
+
|
|
66
|
+
sage: q1 = QuadraticForm(G1)
|
|
67
|
+
sage: q2 = QuadraticForm(G2)
|
|
68
|
+
sage: q1.is_locally_equivalent_to(q2,2)
|
|
69
|
+
True
|
|
70
|
+
|
|
71
|
+
SEEALSO::
|
|
72
|
+
|
|
73
|
+
:mod:`~sage.quadratic_forms.genera.genus`
|
|
74
|
+
:meth:`~sage.quadratic_forms.quadratic_form.QuadraticForm.is_locally_equivalent_to`
|
|
75
|
+
:meth:`~sage.modules.torsion_quadratic_module.TorsionQuadraticModule.normal_form`
|
|
76
|
+
|
|
77
|
+
AUTHORS:
|
|
78
|
+
|
|
79
|
+
- Simon Brandhorst (2018-01): initial version
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# ****************************************************************************
|
|
83
|
+
# Copyright (C) 2018 Simon Branhdorst <sbrandhorst@web.de>
|
|
84
|
+
#
|
|
85
|
+
# This program is free software: you can redistribute it and/or modify
|
|
86
|
+
# it under the terms of the GNU General Public License as published by
|
|
87
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
88
|
+
# (at your option) any later version.
|
|
89
|
+
# https://www.gnu.org/licenses/
|
|
90
|
+
# ****************************************************************************
|
|
91
|
+
|
|
92
|
+
from sage.rings.integer_ring import ZZ
|
|
93
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
|
|
94
|
+
from sage.matrix.constructor import Matrix
|
|
95
|
+
from copy import copy
|
|
96
|
+
from sage.rings.finite_rings.integer_mod import mod
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def collect_small_blocks(G):
|
|
100
|
+
r"""
|
|
101
|
+
Return the blocks as list.
|
|
102
|
+
|
|
103
|
+
INPUT:
|
|
104
|
+
|
|
105
|
+
- ``G`` -- a ``block_diagonal`` matrix consisting of
|
|
106
|
+
`1` by `1` and `2` by `2` blocks
|
|
107
|
+
|
|
108
|
+
OUTPUT: list of `1` by `1` and `2` by `2` matrices; the blocks
|
|
109
|
+
|
|
110
|
+
EXAMPLES::
|
|
111
|
+
|
|
112
|
+
sage: from sage.quadratic_forms.genera.normal_form import collect_small_blocks
|
|
113
|
+
sage: W1 = Matrix([1])
|
|
114
|
+
sage: V = Matrix(ZZ, 2, [2, 1, 1, 2])
|
|
115
|
+
sage: L = [W1, V, V, W1, W1, V, W1, V]
|
|
116
|
+
sage: G = Matrix.block_diagonal(L)
|
|
117
|
+
sage: L == collect_small_blocks(G)
|
|
118
|
+
True
|
|
119
|
+
"""
|
|
120
|
+
D = copy(G)
|
|
121
|
+
L = _get_small_block_indices(D)[1:]
|
|
122
|
+
D.subdivide(L, L)
|
|
123
|
+
blocks = []
|
|
124
|
+
for i in range(len(L) + 1):
|
|
125
|
+
block = copy(D.subdivision(i, i))
|
|
126
|
+
blocks.append(block)
|
|
127
|
+
return blocks
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def p_adic_normal_form(G, p, precision=None, partial=False, debug=False):
|
|
131
|
+
r"""
|
|
132
|
+
Return the transformation to the `p`-adic normal form of a symmetric matrix.
|
|
133
|
+
|
|
134
|
+
Two ```p`-adic`` quadratic forms are integrally equivalent if and only if
|
|
135
|
+
their Gram matrices have the same normal form.
|
|
136
|
+
|
|
137
|
+
Let `p` be odd and `u` be the smallest non-square modulo `p`.
|
|
138
|
+
The normal form is a block diagonal matrix
|
|
139
|
+
with blocks `p^k G_k` such that `G_k` is either the identity matrix or
|
|
140
|
+
the identity matrix with the last diagonal entry replaced by `u`.
|
|
141
|
+
|
|
142
|
+
If `p=2` is even, define the `1` by `1` matrices::
|
|
143
|
+
|
|
144
|
+
sage: W1 = Matrix([1]); W1
|
|
145
|
+
[1]
|
|
146
|
+
sage: W3 = Matrix([3]); W3
|
|
147
|
+
[3]
|
|
148
|
+
sage: W5 = Matrix([5]); W5
|
|
149
|
+
[5]
|
|
150
|
+
sage: W7 = Matrix([7]); W7
|
|
151
|
+
[7]
|
|
152
|
+
|
|
153
|
+
and the `2` by `2` matrices::
|
|
154
|
+
|
|
155
|
+
sage: U = Matrix(2,[0,1,1,0]); U
|
|
156
|
+
[0 1]
|
|
157
|
+
[1 0]
|
|
158
|
+
sage: V = Matrix(2,[2,1,1,2]); V
|
|
159
|
+
[2 1]
|
|
160
|
+
[1 2]
|
|
161
|
+
|
|
162
|
+
For `p=2` the partial normal form is a block diagonal matrix with blocks
|
|
163
|
+
`2^k G_k` such that `G_k` is a block diagonal matrix of the form
|
|
164
|
+
`[U`, ... , `U`, `V`, `Wa`, `Wb]`
|
|
165
|
+
where we allow `V`, `Wa`, `Wb` to be `0 \times 0` matrices.
|
|
166
|
+
|
|
167
|
+
Further restrictions to the full normal form apply.
|
|
168
|
+
We refer to [MirMor2009]_ IV Definition 4.6. for the details.
|
|
169
|
+
|
|
170
|
+
INPUT:
|
|
171
|
+
|
|
172
|
+
- ``G`` -- a symmetric `n` by `n` matrix in `\QQ`
|
|
173
|
+
- ``p`` -- a prime number -- it is not checked whether it is prime
|
|
174
|
+
- ``precision`` -- if not set, the minimal possible is taken
|
|
175
|
+
- ``partial`` -- boolean (default: ``False``); if set, only the
|
|
176
|
+
partial normal form is returned
|
|
177
|
+
|
|
178
|
+
OUTPUT:
|
|
179
|
+
|
|
180
|
+
- ``D`` -- the jordan matrix over `\QQ_p`
|
|
181
|
+
- ``B`` -- invertible transformation matrix over `\ZZ_p`,
|
|
182
|
+
i.e., `D = B * G * B^T`
|
|
183
|
+
|
|
184
|
+
EXAMPLES::
|
|
185
|
+
|
|
186
|
+
sage: from sage.quadratic_forms.genera.normal_form import p_adic_normal_form
|
|
187
|
+
sage: D4 = Matrix(ZZ, 4, [2,-1,-1,-1,-1,2,0,0,-1,0,2,0,-1,0,0,2])
|
|
188
|
+
sage: D4
|
|
189
|
+
[ 2 -1 -1 -1]
|
|
190
|
+
[-1 2 0 0]
|
|
191
|
+
[-1 0 2 0]
|
|
192
|
+
[-1 0 0 2]
|
|
193
|
+
sage: D, B = p_adic_normal_form(D4, 2)
|
|
194
|
+
sage: D
|
|
195
|
+
[ 2 1 0 0]
|
|
196
|
+
[ 1 2 0 0]
|
|
197
|
+
[ 0 0 2^2 2]
|
|
198
|
+
[ 0 0 2 2^2]
|
|
199
|
+
sage: D == B * D4 * B.T
|
|
200
|
+
True
|
|
201
|
+
sage: A4 = Matrix(ZZ, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
|
|
202
|
+
sage: A4
|
|
203
|
+
[ 2 -1 0 0]
|
|
204
|
+
[-1 2 -1 0]
|
|
205
|
+
[ 0 -1 2 -1]
|
|
206
|
+
[ 0 0 -1 2]
|
|
207
|
+
sage: D, B = p_adic_normal_form(A4, 2)
|
|
208
|
+
sage: D
|
|
209
|
+
[0 1 0 0]
|
|
210
|
+
[1 0 0 0]
|
|
211
|
+
[0 0 2 1]
|
|
212
|
+
[0 0 1 2]
|
|
213
|
+
|
|
214
|
+
We can handle degenerate forms::
|
|
215
|
+
|
|
216
|
+
sage: A4_extended = Matrix(ZZ, 5, [2, -1, 0, 0, -1, -1, 2, -1, 0, 0, 0, -1, 2, -1, 0, 0, 0, -1, 2, -1, -1, 0, 0, -1, 2])
|
|
217
|
+
sage: D, B = p_adic_normal_form(A4_extended, 5)
|
|
218
|
+
sage: D
|
|
219
|
+
[1 0 0 0 0]
|
|
220
|
+
[0 1 0 0 0]
|
|
221
|
+
[0 0 1 0 0]
|
|
222
|
+
[0 0 0 5 0]
|
|
223
|
+
[0 0 0 0 0]
|
|
224
|
+
|
|
225
|
+
and denominators::
|
|
226
|
+
|
|
227
|
+
sage: A4dual = A4.inverse()
|
|
228
|
+
sage: D, B = p_adic_normal_form(A4dual, 5)
|
|
229
|
+
sage: D
|
|
230
|
+
[5^-1 0 0 0]
|
|
231
|
+
[ 0 1 0 0]
|
|
232
|
+
[ 0 0 1 0]
|
|
233
|
+
[ 0 0 0 1]
|
|
234
|
+
|
|
235
|
+
TESTS::
|
|
236
|
+
|
|
237
|
+
sage: Z = Matrix(ZZ, 0, [])
|
|
238
|
+
sage: p_adic_normal_form(Z, 3)
|
|
239
|
+
([], [])
|
|
240
|
+
sage: Z = matrix.zero(10)
|
|
241
|
+
sage: p_adic_normal_form(Z, 3)[0] == 0
|
|
242
|
+
True
|
|
243
|
+
"""
|
|
244
|
+
from sage.rings.padics.factory import Zp
|
|
245
|
+
|
|
246
|
+
p = ZZ(p)
|
|
247
|
+
# input checks!!
|
|
248
|
+
G0, denom = G._clear_denom()
|
|
249
|
+
d = denom.valuation(p)
|
|
250
|
+
r = G0.rank()
|
|
251
|
+
if r != G0.ncols():
|
|
252
|
+
U = G0.hermite_form(transformation=True)[1]
|
|
253
|
+
else:
|
|
254
|
+
U = G0.parent().identity_matrix()
|
|
255
|
+
kernel = U[r:, :]
|
|
256
|
+
nondeg = U[:r, :]
|
|
257
|
+
|
|
258
|
+
# continue with the non-degenerate part
|
|
259
|
+
G = nondeg * G * nondeg.T * p**d
|
|
260
|
+
if precision is None:
|
|
261
|
+
# in Zp(2) we have to calculate at least mod 8 for things to make sense.
|
|
262
|
+
precision = G.det().valuation(p) + 4
|
|
263
|
+
R = Zp(p, prec=precision, type='fixed-mod')
|
|
264
|
+
G = G.change_ring(R)
|
|
265
|
+
G.set_immutable() # is not changed during computation
|
|
266
|
+
n = G.ncols()
|
|
267
|
+
# The trivial case
|
|
268
|
+
if n == 0:
|
|
269
|
+
return G.parent().zero(), G.parent().zero()
|
|
270
|
+
# the transformation matrix is called B
|
|
271
|
+
if p == 2:
|
|
272
|
+
D, B = _jordan_2_adic(G)
|
|
273
|
+
else:
|
|
274
|
+
D, B = _jordan_odd_adic(G)
|
|
275
|
+
D, B1 = _normalize(D)
|
|
276
|
+
B = B1 * B
|
|
277
|
+
# we have reached a normal form for p != 2
|
|
278
|
+
# for p == 2 extra work is necessary
|
|
279
|
+
if p == 2:
|
|
280
|
+
D, B1 = _two_adic_normal_forms(D, partial=partial)
|
|
281
|
+
B = B1 * B
|
|
282
|
+
nondeg = B * nondeg
|
|
283
|
+
B = nondeg.stack(kernel)
|
|
284
|
+
D = Matrix.block_diagonal([D, Matrix.zero(kernel.nrows())])
|
|
285
|
+
if debug:
|
|
286
|
+
assert B.determinant().valuation() == 0 # B is invertible!
|
|
287
|
+
if p == 2:
|
|
288
|
+
assert B * G * B.T == Matrix.block_diagonal(collect_small_blocks(D))
|
|
289
|
+
else:
|
|
290
|
+
assert B * G * B.T == Matrix.diagonal(D.diagonal())
|
|
291
|
+
return D / p**d, B
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _find_min_p(G, cnt, lower_bound=0):
|
|
295
|
+
r"""
|
|
296
|
+
Find smallest valuation below and right from ``cnt`` preferring the diagonal.
|
|
297
|
+
|
|
298
|
+
INPUT:
|
|
299
|
+
|
|
300
|
+
- ``G`` -- a symmetric `n` by `n` matrix in `\QQ_p`
|
|
301
|
+
- ``cnt`` -- start search from this index
|
|
302
|
+
- ``lower_bound`` -- integer (default: 0)
|
|
303
|
+
a lower bound for the valuations used for optimization
|
|
304
|
+
|
|
305
|
+
OUTPUT:
|
|
306
|
+
|
|
307
|
+
- ``min`` -- minimal valuation
|
|
308
|
+
- ``min_i`` -- row index of the minimal valuation
|
|
309
|
+
- ``min_j`` -- column index of the minimal valuation
|
|
310
|
+
|
|
311
|
+
EXAMPLES::
|
|
312
|
+
|
|
313
|
+
sage: from sage.quadratic_forms.genera.normal_form import _find_min_p
|
|
314
|
+
sage: G = matrix(Qp(2, show_prec=False), 3, 3, [4,0,1,0,4,2,1,2,1])
|
|
315
|
+
sage: G
|
|
316
|
+
[2^2 0 1]
|
|
317
|
+
[ 0 2^2 2]
|
|
318
|
+
[ 1 2 1]
|
|
319
|
+
sage: _find_min_p(G, 0)
|
|
320
|
+
(0, 2, 2)
|
|
321
|
+
sage: G = matrix(Qp(3, show_prec=False), 3, 3, [4,0,1,0,4,2,1,2,1])
|
|
322
|
+
sage: G
|
|
323
|
+
[1 + 3 0 1]
|
|
324
|
+
[ 0 1 + 3 2]
|
|
325
|
+
[ 1 2 1]
|
|
326
|
+
sage: _find_min_p(G, 0)
|
|
327
|
+
(0, 0, 0)
|
|
328
|
+
"""
|
|
329
|
+
n = G.ncols()
|
|
330
|
+
minval = G[cnt, cnt].valuation()
|
|
331
|
+
min_i = cnt
|
|
332
|
+
min_j = cnt
|
|
333
|
+
# diagonal has precedence
|
|
334
|
+
for i in range(cnt, n):
|
|
335
|
+
v = G[i, i].valuation()
|
|
336
|
+
if v == lower_bound:
|
|
337
|
+
return lower_bound, i, i
|
|
338
|
+
if v < minval:
|
|
339
|
+
min_i = i
|
|
340
|
+
min_j = i
|
|
341
|
+
minval = v
|
|
342
|
+
# off diagonal
|
|
343
|
+
for i in range(cnt, n):
|
|
344
|
+
for j in range(i + 1, n):
|
|
345
|
+
v = G[i, j].valuation()
|
|
346
|
+
if v == lower_bound:
|
|
347
|
+
return lower_bound, i, j
|
|
348
|
+
if v < minval:
|
|
349
|
+
min_i = i
|
|
350
|
+
min_j = j
|
|
351
|
+
minval = v
|
|
352
|
+
return minval, min_i, min_j
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _get_small_block_indices(G):
|
|
356
|
+
r"""
|
|
357
|
+
Return the indices of the blocks.
|
|
358
|
+
|
|
359
|
+
For internal use in :meth:`collect_small_blocks`.
|
|
360
|
+
|
|
361
|
+
INPUT:
|
|
362
|
+
|
|
363
|
+
- ``G`` -- a block_diagonal matrix consisting of `1` by `1` and `2` by `2` blocks
|
|
364
|
+
|
|
365
|
+
OUTPUT: list of integers
|
|
366
|
+
|
|
367
|
+
EXAMPLES::
|
|
368
|
+
|
|
369
|
+
sage: from sage.quadratic_forms.genera.normal_form import _get_small_block_indices
|
|
370
|
+
sage: W1 = Matrix([1])
|
|
371
|
+
sage: U = Matrix(ZZ, 2, [0, 1, 1, 0])
|
|
372
|
+
sage: G = Matrix.block_diagonal([W1, U, U, W1, W1, U, W1, U])
|
|
373
|
+
sage: _get_small_block_indices(G)
|
|
374
|
+
[0, 1, 3, 5, 6, 7, 9, 10]
|
|
375
|
+
"""
|
|
376
|
+
L = []
|
|
377
|
+
n = G.ncols()
|
|
378
|
+
i = 0
|
|
379
|
+
while i < n - 1:
|
|
380
|
+
L.append(i)
|
|
381
|
+
if G[i, i + 1] != 0:
|
|
382
|
+
i += 2
|
|
383
|
+
else:
|
|
384
|
+
i += 1
|
|
385
|
+
if i == n - 1:
|
|
386
|
+
L.append(i)
|
|
387
|
+
return L[:]
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _get_homogeneous_block_indices(G):
|
|
391
|
+
r"""
|
|
392
|
+
Return the indices of the homogeneous blocks.
|
|
393
|
+
|
|
394
|
+
We call a matrix homogeneous if it is a multiple of an invertible matrix.
|
|
395
|
+
Sometimes they are also called modular.
|
|
396
|
+
|
|
397
|
+
INPUT:
|
|
398
|
+
|
|
399
|
+
- ``G`` -- a block diagonal matrix over the `p`-adics
|
|
400
|
+
with blocks of size at most `2`
|
|
401
|
+
|
|
402
|
+
OUTPUT: list of integers
|
|
403
|
+
|
|
404
|
+
EXAMPLES::
|
|
405
|
+
|
|
406
|
+
sage: from sage.quadratic_forms.genera.normal_form import _get_homogeneous_block_indices
|
|
407
|
+
sage: W1 = Matrix(Zp(2), [1])
|
|
408
|
+
sage: V = Matrix(Zp(2), 2, [2, 1, 1, 2])
|
|
409
|
+
sage: G = Matrix.block_diagonal([W1, V, V, 2*W1, 2*W1, 8*V, 8*W1, 16*V])
|
|
410
|
+
sage: _get_homogeneous_block_indices(G)
|
|
411
|
+
([0, 5, 7, 10], [0, 1, 3, 4])
|
|
412
|
+
sage: G = Matrix.block_diagonal([W1, V, V, 2*W1, 2*W1, 8*V, 8*W1, 16*W1])
|
|
413
|
+
sage: _get_homogeneous_block_indices(G)
|
|
414
|
+
([0, 5, 7, 10], [0, 1, 3, 4])
|
|
415
|
+
"""
|
|
416
|
+
L = []
|
|
417
|
+
vals = []
|
|
418
|
+
n = G.ncols()
|
|
419
|
+
i = 0
|
|
420
|
+
val = -5
|
|
421
|
+
while i < n - 1:
|
|
422
|
+
if G[i, i + 1] != 0:
|
|
423
|
+
m = G[i, i + 1].valuation()
|
|
424
|
+
else:
|
|
425
|
+
m = G[i, i].valuation()
|
|
426
|
+
if m > val:
|
|
427
|
+
L.append(i)
|
|
428
|
+
val = m
|
|
429
|
+
vals.append(val)
|
|
430
|
+
if G[i, i + 1] != 0:
|
|
431
|
+
i += 1
|
|
432
|
+
i += 1
|
|
433
|
+
if i == n - 1:
|
|
434
|
+
m = G[i, i].valuation()
|
|
435
|
+
if m > val:
|
|
436
|
+
L.append(i)
|
|
437
|
+
val = m
|
|
438
|
+
vals.append(val)
|
|
439
|
+
return L, vals
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _homogeneous_normal_form(G, w):
|
|
443
|
+
r"""
|
|
444
|
+
Return the homogeneous normal form of the homogeneous ``G``.
|
|
445
|
+
|
|
446
|
+
INPUT:
|
|
447
|
+
|
|
448
|
+
- ``G`` -- a modular symmetric matrix over the `2`-adic integers
|
|
449
|
+
in partial normal form
|
|
450
|
+
|
|
451
|
+
OUTPUT:
|
|
452
|
+
|
|
453
|
+
- ``B`` -- an invertible matrix over the basering of ``G``
|
|
454
|
+
such that ``B*G*B.T`` is in homogeneous normal form
|
|
455
|
+
|
|
456
|
+
EXAMPLES::
|
|
457
|
+
|
|
458
|
+
sage: from sage.quadratic_forms.genera.normal_form import _homogeneous_normal_form
|
|
459
|
+
sage: R = Zp(2, type='fixed-mod', print_mode='terse', show_prec=False)
|
|
460
|
+
sage: U = Matrix(R, 2, [0,1,1,0])
|
|
461
|
+
sage: V = Matrix(R, 2, [2,1,1,2])
|
|
462
|
+
sage: W1 = Matrix(R, 1, [1])
|
|
463
|
+
sage: W3 = Matrix(R, 1, [3])
|
|
464
|
+
sage: W5 = Matrix(R, 1, [5])
|
|
465
|
+
sage: W7 = Matrix(R, 1, [7])
|
|
466
|
+
sage: G = Matrix.block_diagonal([V, W1])
|
|
467
|
+
sage: B = _homogeneous_normal_form(G, 1)[1]
|
|
468
|
+
sage: B * G * B.T
|
|
469
|
+
[2 1 0]
|
|
470
|
+
[1 2 0]
|
|
471
|
+
[0 0 1]
|
|
472
|
+
sage: G = Matrix.block_diagonal([V, W1, W3])
|
|
473
|
+
sage: B = _homogeneous_normal_form(G, 2)[1]
|
|
474
|
+
sage: B * G * B.T
|
|
475
|
+
[2 1 0 0]
|
|
476
|
+
[1 2 0 0]
|
|
477
|
+
[0 0 1 0]
|
|
478
|
+
[0 0 0 3]
|
|
479
|
+
sage: G = Matrix.block_diagonal([U, V, W1, W5])
|
|
480
|
+
sage: B = _homogeneous_normal_form(G, 2)[1]
|
|
481
|
+
sage: B * G * B.T
|
|
482
|
+
[0 1 0 0 0 0]
|
|
483
|
+
[1 0 0 0 0 0]
|
|
484
|
+
[0 0 0 1 0 0]
|
|
485
|
+
[0 0 1 0 0 0]
|
|
486
|
+
[0 0 0 0 7 0]
|
|
487
|
+
[0 0 0 0 0 7]
|
|
488
|
+
sage: G = Matrix.block_diagonal([U, W7, W3])
|
|
489
|
+
sage: B = _homogeneous_normal_form(G, 2)[1]
|
|
490
|
+
sage: B * G * B.T
|
|
491
|
+
[0 1 0 0]
|
|
492
|
+
[1 0 0 0]
|
|
493
|
+
[0 0 3 0]
|
|
494
|
+
[0 0 0 7]
|
|
495
|
+
sage: G = Matrix.block_diagonal([V, W5, W5])
|
|
496
|
+
sage: B = _homogeneous_normal_form(G, 2)[1]
|
|
497
|
+
sage: B * G * B.T
|
|
498
|
+
[0 1 0 0]
|
|
499
|
+
[1 0 0 0]
|
|
500
|
+
[0 0 3 0]
|
|
501
|
+
[0 0 0 7]
|
|
502
|
+
sage: G = Matrix.block_diagonal([V, W3, W3])
|
|
503
|
+
sage: B = _homogeneous_normal_form(G, 2)[1]
|
|
504
|
+
sage: B * G * B.T
|
|
505
|
+
[0 1 0 0]
|
|
506
|
+
[1 0 0 0]
|
|
507
|
+
[0 0 1 0]
|
|
508
|
+
[0 0 0 5]
|
|
509
|
+
sage: G = Matrix.block_diagonal([V, W1, W3])
|
|
510
|
+
sage: B = _homogeneous_normal_form(G, 2)[1]
|
|
511
|
+
sage: B * G * B.T
|
|
512
|
+
[2 1 0 0]
|
|
513
|
+
[1 2 0 0]
|
|
514
|
+
[0 0 1 0]
|
|
515
|
+
[0 0 0 3]
|
|
516
|
+
sage: G = Matrix.block_diagonal([W3, W3])
|
|
517
|
+
sage: B = _homogeneous_normal_form(G, 2)[1]
|
|
518
|
+
sage: B * G * B.T
|
|
519
|
+
[7 0]
|
|
520
|
+
[0 7]
|
|
521
|
+
"""
|
|
522
|
+
B = copy(G.parent().identity_matrix())
|
|
523
|
+
D = copy(G)
|
|
524
|
+
n = B.ncols()
|
|
525
|
+
if w == 2:
|
|
526
|
+
if n > 2 and D[-3, -3] != 0:
|
|
527
|
+
v = 2
|
|
528
|
+
else:
|
|
529
|
+
v = 0
|
|
530
|
+
if v == 2:
|
|
531
|
+
e1 = D[-2, -2].unit_part()
|
|
532
|
+
e2 = D[-1, -1].unit_part()
|
|
533
|
+
e = {e1, e2}
|
|
534
|
+
E = [{1, 3}, {1, 7}, {5, 7}, {3, 5}]
|
|
535
|
+
if e not in E:
|
|
536
|
+
B[-4:, :] = _relations(D[-4:, -4:], 5) * B[-4:, :]
|
|
537
|
+
D = B * G * B.T
|
|
538
|
+
e1 = D[-2, -2].unit_part()
|
|
539
|
+
e2 = D[-1, -1].unit_part()
|
|
540
|
+
e = {e1, e2}
|
|
541
|
+
E = [{3}, {3, 5}, {5}, {5, 7}]
|
|
542
|
+
if e in E:
|
|
543
|
+
B[-2:, :] = _relations(D[-2:, -2:], 1) * B[-2:, :]
|
|
544
|
+
D = B * G * B.T
|
|
545
|
+
# assert that e1 < e2
|
|
546
|
+
e1 = D[-2, -2].unit_part()
|
|
547
|
+
e2 = D[-1, -1].unit_part()
|
|
548
|
+
if ZZ(e1) > ZZ(e2):
|
|
549
|
+
B.swap_rows(n - 1, n - 2)
|
|
550
|
+
D.swap_rows(n - 1, n - 2)
|
|
551
|
+
D.swap_columns(n - 1, n - 2)
|
|
552
|
+
return D, B
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def _jordan_odd_adic(G):
|
|
556
|
+
r"""
|
|
557
|
+
Return the Jordan decomposition of a symmetric matrix over an odd prime.
|
|
558
|
+
|
|
559
|
+
INPUT:
|
|
560
|
+
|
|
561
|
+
- ``G`` -- a symmetric matrix over `\ZZ_p` of type ``'fixed-mod'``
|
|
562
|
+
|
|
563
|
+
OUTPUT:
|
|
564
|
+
|
|
565
|
+
- ``D`` -- a diagonal matrix
|
|
566
|
+
- ``B`` -- a unimodular matrix such that ``D = B * G * B.T``
|
|
567
|
+
|
|
568
|
+
EXAMPLES::
|
|
569
|
+
|
|
570
|
+
sage: from sage.quadratic_forms.genera.normal_form import _jordan_odd_adic
|
|
571
|
+
sage: R = Zp(3, prec=2, print_mode='terse', show_prec=False)
|
|
572
|
+
sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
|
|
573
|
+
sage: A4
|
|
574
|
+
[2 8 0 0]
|
|
575
|
+
[8 2 8 0]
|
|
576
|
+
[0 8 2 8]
|
|
577
|
+
[0 0 8 2]
|
|
578
|
+
sage: D, B = _jordan_odd_adic(A4)
|
|
579
|
+
sage: D
|
|
580
|
+
[2 0 0 0]
|
|
581
|
+
[0 2 0 0]
|
|
582
|
+
[0 0 1 0]
|
|
583
|
+
[0 0 0 8]
|
|
584
|
+
sage: D == B*A4*B.T
|
|
585
|
+
True
|
|
586
|
+
sage: B.determinant().valuation() == 0
|
|
587
|
+
True
|
|
588
|
+
"""
|
|
589
|
+
R = G.base_ring()
|
|
590
|
+
D = copy(G)
|
|
591
|
+
n = G.ncols()
|
|
592
|
+
|
|
593
|
+
# transformation matrix
|
|
594
|
+
B = Matrix.identity(R, n)
|
|
595
|
+
|
|
596
|
+
# indices of the diagonal entries which are already used
|
|
597
|
+
cnt = 0
|
|
598
|
+
minval = 0
|
|
599
|
+
while cnt < n:
|
|
600
|
+
pivot = _find_min_p(D, cnt, minval)
|
|
601
|
+
piv1 = pivot[1]
|
|
602
|
+
piv2 = pivot[2]
|
|
603
|
+
minval = pivot[0]
|
|
604
|
+
# the smallest valuation is on the diagonal
|
|
605
|
+
if piv1 == piv2:
|
|
606
|
+
# move pivot to position [cnt,cnt]
|
|
607
|
+
if piv1 != cnt:
|
|
608
|
+
B.swap_rows(cnt, piv1)
|
|
609
|
+
D.swap_rows(cnt, piv1)
|
|
610
|
+
D.swap_columns(cnt, piv1)
|
|
611
|
+
# we are already orthogonal to the part with i < cnt
|
|
612
|
+
# now make the rest orthogonal too
|
|
613
|
+
for i in range(cnt + 1, n):
|
|
614
|
+
if D[i, cnt] != 0:
|
|
615
|
+
c = D[i, cnt] // D[cnt, cnt]
|
|
616
|
+
B[i, :] += - c * B[cnt, :]
|
|
617
|
+
D[i, :] += - c * D[cnt, :]
|
|
618
|
+
D[:, i] += - c * D[:, cnt]
|
|
619
|
+
cnt = cnt + 1
|
|
620
|
+
else:
|
|
621
|
+
# the smallest valuation is off the diagonal
|
|
622
|
+
row = pivot[1]
|
|
623
|
+
col = pivot[2]
|
|
624
|
+
B[row, :] += B[col, :]
|
|
625
|
+
D[row, :] += D[col, :]
|
|
626
|
+
D[:, row] += D[:, col]
|
|
627
|
+
# the smallest valuation is now on the diagonal
|
|
628
|
+
return D, B
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def _jordan_2_adic(G):
|
|
632
|
+
r"""
|
|
633
|
+
Transform a symmetric matrix over the `2`-adic integers into Jordan form.
|
|
634
|
+
|
|
635
|
+
Note that if the precision is too low, this method fails.
|
|
636
|
+
The method is only tested for input over `\ZZ_2` of ``'type=fixed-mod'``.
|
|
637
|
+
|
|
638
|
+
INPUT:
|
|
639
|
+
|
|
640
|
+
- ``G`` -- symmetric `n` by `n` matrix in `\ZZ_p`
|
|
641
|
+
|
|
642
|
+
OUTPUT:
|
|
643
|
+
|
|
644
|
+
- ``D`` -- the jordan matrix
|
|
645
|
+
- ``B`` -- transformation matrix, i.e, ``D = B * G * B^T``
|
|
646
|
+
|
|
647
|
+
The matrix ``D`` is a block diagonal matrix consisting
|
|
648
|
+
of `1` by `1` and `2` by `2` blocks.
|
|
649
|
+
The `2` by `2` blocks are matrices of the form
|
|
650
|
+
`[[2a, b], [b, 2c]] * 2^k`
|
|
651
|
+
with `b` of valuation `0`.
|
|
652
|
+
|
|
653
|
+
EXAMPLES::
|
|
654
|
+
|
|
655
|
+
sage: from sage.quadratic_forms.genera.normal_form import _jordan_2_adic
|
|
656
|
+
sage: R = Zp(2, prec=3, print_mode='terse', show_prec=False)
|
|
657
|
+
sage: A4 = Matrix(R, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
|
|
658
|
+
sage: A4
|
|
659
|
+
[2 7 0 0]
|
|
660
|
+
[7 2 7 0]
|
|
661
|
+
[0 7 2 7]
|
|
662
|
+
[0 0 7 2]
|
|
663
|
+
sage: D, B = _jordan_2_adic(A4)
|
|
664
|
+
sage: D
|
|
665
|
+
[ 2 7 0 0]
|
|
666
|
+
[ 7 2 0 0]
|
|
667
|
+
[ 0 0 12 7]
|
|
668
|
+
[ 0 0 7 2]
|
|
669
|
+
sage: D == B*A4*B.T
|
|
670
|
+
True
|
|
671
|
+
sage: B.determinant().valuation() == 0
|
|
672
|
+
True
|
|
673
|
+
"""
|
|
674
|
+
R = G.base_ring()
|
|
675
|
+
D = copy(G)
|
|
676
|
+
n = G.ncols()
|
|
677
|
+
|
|
678
|
+
# transformation matrix
|
|
679
|
+
B = Matrix.identity(R, n)
|
|
680
|
+
|
|
681
|
+
# indices of the diagonal entries which are already used
|
|
682
|
+
cnt = 0
|
|
683
|
+
minval = None
|
|
684
|
+
while cnt < n:
|
|
685
|
+
pivot = _find_min_p(D, cnt)
|
|
686
|
+
piv1 = pivot[1]
|
|
687
|
+
piv2 = pivot[2]
|
|
688
|
+
minval = pivot[0]
|
|
689
|
+
# the smallest valuation is on the diagonal
|
|
690
|
+
if piv1 == piv2:
|
|
691
|
+
# move pivot to position [cnt,cnt]
|
|
692
|
+
if piv1 != cnt:
|
|
693
|
+
B.swap_rows(cnt, piv1)
|
|
694
|
+
D.swap_rows(cnt, piv1)
|
|
695
|
+
D.swap_columns(cnt, piv1)
|
|
696
|
+
# we are already orthogonal to the part with i < cnt
|
|
697
|
+
# now make the rest orthogonal too
|
|
698
|
+
for i in range(cnt + 1, n):
|
|
699
|
+
if D[i, cnt] != 0:
|
|
700
|
+
c = D[i, cnt] // D[cnt, cnt]
|
|
701
|
+
B[i, :] += -c * B[cnt, :]
|
|
702
|
+
D[i, :] += -c * D[cnt, :]
|
|
703
|
+
D[:, i] += -c * D[:, cnt]
|
|
704
|
+
cnt = cnt + 1
|
|
705
|
+
# the smallest valuation is off the diagonal
|
|
706
|
+
else:
|
|
707
|
+
# move this 2 x 2 block to the top left (starting from cnt)
|
|
708
|
+
if piv1 != cnt:
|
|
709
|
+
B.swap_rows(cnt, piv1)
|
|
710
|
+
D.swap_rows(cnt, piv1)
|
|
711
|
+
D.swap_columns(cnt, piv1)
|
|
712
|
+
if piv2 != cnt + 1:
|
|
713
|
+
B.swap_rows(cnt + 1, piv2)
|
|
714
|
+
D.swap_rows(cnt + 1, piv2)
|
|
715
|
+
D.swap_columns(cnt + 1, piv2)
|
|
716
|
+
# we split off a 2 x 2 block
|
|
717
|
+
# if it is the last 2 x 2 block, there is nothing to do.
|
|
718
|
+
if cnt != n - 2:
|
|
719
|
+
content = R(2 ** minval)
|
|
720
|
+
eqn_mat = D[cnt:cnt+2, cnt:cnt+2].list()
|
|
721
|
+
eqn_mat = Matrix(R, 2, 2, [e // content for e in eqn_mat])
|
|
722
|
+
# calculate the inverse without using division
|
|
723
|
+
inv = eqn_mat.adjugate() * eqn_mat.det().inverse_of_unit()
|
|
724
|
+
B1 = B[cnt:cnt+2, :]
|
|
725
|
+
B2 = D[cnt+2:, cnt:cnt+2] * inv
|
|
726
|
+
for i in range(B2.nrows()):
|
|
727
|
+
for j in range(B2.ncols()):
|
|
728
|
+
B2[i, j] = B2[i, j] // content
|
|
729
|
+
B[cnt + 2:, :] -= B2 * B1
|
|
730
|
+
D[cnt:, cnt:] = B[cnt:, :] * G * B[cnt:, :].transpose()
|
|
731
|
+
cnt += 2
|
|
732
|
+
return D, B
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def _min_nonsquare(p):
|
|
736
|
+
r"""
|
|
737
|
+
Return the minimal nonsquare modulo the prime `p`.
|
|
738
|
+
|
|
739
|
+
INPUT:
|
|
740
|
+
|
|
741
|
+
- ``p`` -- a prime number
|
|
742
|
+
|
|
743
|
+
EXAMPLES::
|
|
744
|
+
|
|
745
|
+
sage: from sage.quadratic_forms.genera.normal_form import _min_nonsquare
|
|
746
|
+
sage: _min_nonsquare(2)
|
|
747
|
+
sage: _min_nonsquare(3)
|
|
748
|
+
2
|
|
749
|
+
sage: _min_nonsquare(5)
|
|
750
|
+
2
|
|
751
|
+
sage: _min_nonsquare(7)
|
|
752
|
+
3
|
|
753
|
+
"""
|
|
754
|
+
for i in GF(p):
|
|
755
|
+
if not i.is_square():
|
|
756
|
+
return i
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
def _normalize(G, normal_odd=True):
|
|
760
|
+
r"""
|
|
761
|
+
Return the transformation to sums of forms of types `U`, `V` and `W`.
|
|
762
|
+
|
|
763
|
+
Part of the algorithm :meth:`p_adic_normal_form`.
|
|
764
|
+
|
|
765
|
+
INPUT:
|
|
766
|
+
|
|
767
|
+
- ``G`` -- a symmetric matrix over `\ZZ_p` in jordan form --
|
|
768
|
+
the output of :meth:`p_adic_normal_form` or :meth:`_jordan_2_adic`
|
|
769
|
+
- ``normal_odd`` -- boolean (default: ``True``); if ``True`` and `p` is odd,
|
|
770
|
+
compute a normal form
|
|
771
|
+
|
|
772
|
+
OUTPUT:
|
|
773
|
+
|
|
774
|
+
- ``(D, B)`` -- a pair of matrices such that ``D=B*G*B.T``
|
|
775
|
+
is a sum of forms of types `U`, `V` and `W` for `p=2` or
|
|
776
|
+
diagonal with diagonal entries equal `1` or `u`
|
|
777
|
+
where `u` is the smallest non-square modulo the odd prime `p`.
|
|
778
|
+
|
|
779
|
+
EXAMPLES::
|
|
780
|
+
|
|
781
|
+
sage: from sage.quadratic_forms.genera.normal_form import _normalize
|
|
782
|
+
sage: R = Zp(3, prec=5, type='fixed-mod', print_mode='series', show_prec=False)
|
|
783
|
+
sage: G = matrix.diagonal(R, [1,7,3,3*5,3,9,-9,27*13])
|
|
784
|
+
sage: D, B =_normalize(G)
|
|
785
|
+
sage: D
|
|
786
|
+
[ 1 0 0 0 0 0 0 0]
|
|
787
|
+
[ 0 1 0 0 0 0 0 0]
|
|
788
|
+
[ 0 0 3 0 0 0 0 0]
|
|
789
|
+
[ 0 0 0 3 0 0 0 0]
|
|
790
|
+
[ 0 0 0 0 2*3 0 0 0]
|
|
791
|
+
[ 0 0 0 0 0 3^2 0 0]
|
|
792
|
+
[ 0 0 0 0 0 0 2*3^2 0]
|
|
793
|
+
[ 0 0 0 0 0 0 0 3^3]
|
|
794
|
+
"""
|
|
795
|
+
R = G.base_ring()
|
|
796
|
+
D = copy(G)
|
|
797
|
+
p = R.prime()
|
|
798
|
+
n = G.ncols()
|
|
799
|
+
B = copy(G.parent().identity_matrix())
|
|
800
|
+
if p != 2:
|
|
801
|
+
# squareclasses 1, v
|
|
802
|
+
v = _min_nonsquare(p)
|
|
803
|
+
v = R(v)
|
|
804
|
+
non_squares = []
|
|
805
|
+
val = 0
|
|
806
|
+
for i in range(n):
|
|
807
|
+
if D[i, i].valuation() > val:
|
|
808
|
+
# a new block starts
|
|
809
|
+
val = D[i, i].valuation()
|
|
810
|
+
if normal_odd and len(non_squares) != 0:
|
|
811
|
+
# move the non-square to
|
|
812
|
+
# the last entry of the previous block
|
|
813
|
+
j = non_squares.pop()
|
|
814
|
+
B.swap_rows(j, i - 1)
|
|
815
|
+
d = D[i, i].unit_part()
|
|
816
|
+
if d.is_square():
|
|
817
|
+
D[i, i] = 1
|
|
818
|
+
B[i, :] *= d.inverse_of_unit().sqrt()
|
|
819
|
+
else:
|
|
820
|
+
D[i, i] = v
|
|
821
|
+
B[i, :] *= (v * d.inverse_of_unit()).sqrt()
|
|
822
|
+
if normal_odd and len(non_squares) != 0:
|
|
823
|
+
# we combine two non-squares to get
|
|
824
|
+
# the 2 x 2 identity matrix
|
|
825
|
+
j = non_squares.pop()
|
|
826
|
+
trafo = _normalize_odd_2x2(D[[i, j], [i, j]])
|
|
827
|
+
B[[i, j], :] = trafo * B[[i, j], :]
|
|
828
|
+
D[i, i] = 1
|
|
829
|
+
D[j, j] = 1
|
|
830
|
+
else:
|
|
831
|
+
non_squares.append(i)
|
|
832
|
+
if normal_odd and non_squares:
|
|
833
|
+
j = non_squares.pop()
|
|
834
|
+
B.swap_rows(j, n - 1)
|
|
835
|
+
else:
|
|
836
|
+
# squareclasses 1,3,5,7 modulo 8
|
|
837
|
+
for i in range(n):
|
|
838
|
+
d = D[i, i].unit_part()
|
|
839
|
+
if d != 0:
|
|
840
|
+
v = R(mod(d, 8))
|
|
841
|
+
B[i, :] *= (v * d.inverse_of_unit()).sqrt()
|
|
842
|
+
D = B * G * B.T
|
|
843
|
+
for i in range(n - 1):
|
|
844
|
+
if D[i, i + 1] != 0: # there is a 2 x 2 block here
|
|
845
|
+
block = D[i:i+2, i:i+2]
|
|
846
|
+
trafo = _normalize_2x2(block)
|
|
847
|
+
B[i:i+2, :] = trafo * B[i:i+2, :]
|
|
848
|
+
D = B * G * B.T
|
|
849
|
+
return D, B
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
def _normalize_2x2(G):
|
|
853
|
+
r"""
|
|
854
|
+
Normalize this indecomposable `2` by `2` block.
|
|
855
|
+
|
|
856
|
+
INPUT:
|
|
857
|
+
|
|
858
|
+
- ``G`` -- a `2` by `2` matrix over `\ZZ_p`
|
|
859
|
+
with ``type='fixed-mod'`` of the form::
|
|
860
|
+
|
|
861
|
+
[2a b]
|
|
862
|
+
[ b 2c] * 2^n
|
|
863
|
+
|
|
864
|
+
with `b` of valuation 1.
|
|
865
|
+
|
|
866
|
+
OUTPUT:
|
|
867
|
+
|
|
868
|
+
A unimodular `2` by `2` matrix ``B`` over `\ZZ_p` with
|
|
869
|
+
``B * G * B.transpose()``
|
|
870
|
+
either::
|
|
871
|
+
|
|
872
|
+
[0 1] [2 1]
|
|
873
|
+
[1 0] * 2^n or [1 2] * 2^n
|
|
874
|
+
|
|
875
|
+
EXAMPLES::
|
|
876
|
+
|
|
877
|
+
sage: from sage.quadratic_forms.genera.normal_form import _normalize_2x2
|
|
878
|
+
sage: R = Zp(2, prec=15, type='fixed-mod', print_mode='series', show_prec=False)
|
|
879
|
+
sage: G = Matrix(R, 2, [-17*2,3,3,23*2])
|
|
880
|
+
sage: B =_normalize_2x2(G)
|
|
881
|
+
sage: B * G * B.T
|
|
882
|
+
[2 1]
|
|
883
|
+
[1 2]
|
|
884
|
+
|
|
885
|
+
sage: G = Matrix(R, 2, [-17*4,3,3,23*2])
|
|
886
|
+
sage: B = _normalize_2x2(G)
|
|
887
|
+
sage: B*G*B.T
|
|
888
|
+
[0 1]
|
|
889
|
+
[1 0]
|
|
890
|
+
|
|
891
|
+
sage: G = 2^3 * Matrix(R, 2, [-17*2,3,3,23*2])
|
|
892
|
+
sage: B = _normalize_2x2(G)
|
|
893
|
+
sage: B * G * B.T
|
|
894
|
+
[2^4 2^3]
|
|
895
|
+
[2^3 2^4]
|
|
896
|
+
"""
|
|
897
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
898
|
+
from sage.modules.free_module_element import vector
|
|
899
|
+
B = copy(G.parent().identity_matrix())
|
|
900
|
+
R = G.base_ring()
|
|
901
|
+
P = PolynomialRing(R, 'x')
|
|
902
|
+
x = P.gen()
|
|
903
|
+
|
|
904
|
+
# The input must be an even block
|
|
905
|
+
odd1 = (G[0, 0].valuation() < G[1, 0].valuation())
|
|
906
|
+
odd2 = (G[1, 1].valuation() < G[1, 0].valuation())
|
|
907
|
+
if odd1 or odd2:
|
|
908
|
+
raise ValueError("not a valid 2 x 2 block")
|
|
909
|
+
scale = 2 ** G[0, 1].valuation()
|
|
910
|
+
D = Matrix(R, 2, 2, [d // scale for d in G.list()])
|
|
911
|
+
# now D is of the form
|
|
912
|
+
# [2a b ]
|
|
913
|
+
# [b 2c]
|
|
914
|
+
# where b has valuation 1.
|
|
915
|
+
G = copy(D)
|
|
916
|
+
|
|
917
|
+
# Make sure G[1, 1] has valuation 1.
|
|
918
|
+
if D[1, 1].valuation() > D[0, 0].valuation():
|
|
919
|
+
B.swap_columns(0, 1)
|
|
920
|
+
D.swap_columns(0, 1)
|
|
921
|
+
D.swap_rows(0, 1)
|
|
922
|
+
if D[1, 1].valuation() != 1:
|
|
923
|
+
# this works because
|
|
924
|
+
# D[0, 0] has valuation at least 2
|
|
925
|
+
B[1, :] += B[0, :]
|
|
926
|
+
D = B * G * B.transpose()
|
|
927
|
+
assert D[1, 1].valuation() == 1
|
|
928
|
+
|
|
929
|
+
if mod(D.det(), 8) == 3:
|
|
930
|
+
# in this case we can transform D to
|
|
931
|
+
# 2 1
|
|
932
|
+
# 1 2
|
|
933
|
+
# Find a point of norm 2
|
|
934
|
+
# solve: 2 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0]
|
|
935
|
+
pol = (D[1, 1] * x**2 + 2 * D[1, 0] * x + D[0, 0] - 2) // 2
|
|
936
|
+
# somehow else pari can get a hickup see trac #24065
|
|
937
|
+
pol = pol // pol.leading_coefficient()
|
|
938
|
+
sol = pol.roots()[0][0]
|
|
939
|
+
B[0, 1] = sol
|
|
940
|
+
D = B * G * B.transpose()
|
|
941
|
+
# make D[0, 1] = 1
|
|
942
|
+
B[1, :] *= D[1, 0].inverse_of_unit()
|
|
943
|
+
D = B * G * B.transpose()
|
|
944
|
+
|
|
945
|
+
# solve: v*D*v == 2 with v = (x, -2*x+1)
|
|
946
|
+
if D[1, 1] != 2:
|
|
947
|
+
v = vector([x, -2 * x + 1])
|
|
948
|
+
pol = (v * D * v - 2) // 2
|
|
949
|
+
# somehow else pari can get a hickup see trac #24065
|
|
950
|
+
pol = pol // pol.leading_coefficient()
|
|
951
|
+
sol = pol.roots()[0][0]
|
|
952
|
+
B[1, :] = sol * B[0, :] + (-2 * sol + 1) * B[1, :]
|
|
953
|
+
D = B * G * B.transpose()
|
|
954
|
+
# check the result
|
|
955
|
+
assert D == Matrix(G.parent(), 2, 2, [2, 1, 1, 2]), "D1 \n %r" % D
|
|
956
|
+
elif mod(D.det(), 8) == 7:
|
|
957
|
+
# in this case we can transform D to
|
|
958
|
+
# 0 1
|
|
959
|
+
# 1 0
|
|
960
|
+
# Find a point representing 0
|
|
961
|
+
# solve: 0 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0]
|
|
962
|
+
pol = (D[1, 1] * x**2 + 2 * D[1, 0] * x + D[0, 0]) // 2
|
|
963
|
+
# somehow else pari can get a hickup, see trac #24065
|
|
964
|
+
pol = pol // pol.leading_coefficient()
|
|
965
|
+
sol = pol.roots()[0][0]
|
|
966
|
+
B[0, :] += sol * B[1, :]
|
|
967
|
+
D = B * G * B.transpose()
|
|
968
|
+
# make the second basis vector have 0 square as well.
|
|
969
|
+
B[1, :] = B[1, :] - D[1, 1] // (2 * D[0, 1]) * B[0, :]
|
|
970
|
+
D = B * G * B.transpose()
|
|
971
|
+
# rescale to get D[0,1] = 1
|
|
972
|
+
B[0, :] *= D[1, 0].inverse_of_unit()
|
|
973
|
+
D = B * G * B.transpose()
|
|
974
|
+
# check the result
|
|
975
|
+
assert D == Matrix(G.parent(), 2, 2, [0, 1, 1, 0]), "D2 \n %r" % D
|
|
976
|
+
return B
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
def _normalize_odd_2x2(G):
|
|
980
|
+
r"""
|
|
981
|
+
Normalize this `2` by `2` block.
|
|
982
|
+
|
|
983
|
+
INPUT:
|
|
984
|
+
|
|
985
|
+
- ``G`` -- a multiple of the `2` by `2` identity_matrix
|
|
986
|
+
over the `p`-adics for `p` odd
|
|
987
|
+
|
|
988
|
+
OUTPUT:
|
|
989
|
+
|
|
990
|
+
A transformation matrix ``B`` such that ``B * G * B.T`` is the identity
|
|
991
|
+
matrix.
|
|
992
|
+
|
|
993
|
+
EXAMPLES::
|
|
994
|
+
|
|
995
|
+
sage: from sage.quadratic_forms.genera.normal_form import _normalize_odd_2x2
|
|
996
|
+
sage: R = Zp(5, type='fixed-mod', print_mode='terse', show_prec=False)
|
|
997
|
+
sage: G = 2 * Matrix.identity(R, 2)
|
|
998
|
+
sage: B = _normalize_odd_2x2(G)
|
|
999
|
+
sage: B*G*B.T
|
|
1000
|
+
[1 0]
|
|
1001
|
+
[0 1]
|
|
1002
|
+
"""
|
|
1003
|
+
assert G[0, 0] == G[1, 1]
|
|
1004
|
+
u = G[0, 0]
|
|
1005
|
+
y = G.base_ring().zero()
|
|
1006
|
+
while not (1 / u - y**2).is_square():
|
|
1007
|
+
y += 1
|
|
1008
|
+
x = (1 / u - y**2).sqrt()
|
|
1009
|
+
B = copy(G.parent().identity_matrix())
|
|
1010
|
+
B[0, 0] = x
|
|
1011
|
+
B[0, 1] = y
|
|
1012
|
+
B[1, 0] = y
|
|
1013
|
+
B[1, 1] = -x
|
|
1014
|
+
return B
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
def _partial_normal_form_of_block(G):
|
|
1018
|
+
r"""
|
|
1019
|
+
Return the partial normal form of the homogeneous block ``G``.
|
|
1020
|
+
|
|
1021
|
+
For internal use in :meth:`_two_adic_normal_forms`.
|
|
1022
|
+
|
|
1023
|
+
INPUT:
|
|
1024
|
+
|
|
1025
|
+
- ``G`` -- a modular symmetric matrix over the `2`-adic integers
|
|
1026
|
+
|
|
1027
|
+
OUTPUT:
|
|
1028
|
+
|
|
1029
|
+
- ``D``, ``B``, ``w`` -- with ``B`` a transformation matrix such that
|
|
1030
|
+
``B * G * B.T`` is in partial normal form
|
|
1031
|
+
and `w = 0, 1, 2` is the size of the part consisting of forms of type W
|
|
1032
|
+
|
|
1033
|
+
EXAMPLES::
|
|
1034
|
+
|
|
1035
|
+
sage: from sage.quadratic_forms.genera.normal_form import _partial_normal_form_of_block
|
|
1036
|
+
sage: R = Zp(2, prec=4, type='fixed-mod', print_mode='terse', show_prec=False)
|
|
1037
|
+
sage: U = Matrix(R, 2, [0,1,1,0])
|
|
1038
|
+
sage: V = Matrix(R, 2, [2,1,1,2])
|
|
1039
|
+
sage: W1 = Matrix(R, 1, [1])
|
|
1040
|
+
sage: W3 = Matrix(R, 1, [3])
|
|
1041
|
+
sage: W5 = Matrix(R, 1, [5])
|
|
1042
|
+
sage: W7 = Matrix(R, 1, [7])
|
|
1043
|
+
sage: G = Matrix.block_diagonal([W1, U, V, W5, V, W3, V, W7])
|
|
1044
|
+
sage: B = _partial_normal_form_of_block(G)[1]
|
|
1045
|
+
sage: B * G * B.T
|
|
1046
|
+
[0 1 0 0 0 0 0 0 0 0 0 0]
|
|
1047
|
+
[1 0 0 0 0 0 0 0 0 0 0 0]
|
|
1048
|
+
[0 0 0 1 0 0 0 0 0 0 0 0]
|
|
1049
|
+
[0 0 1 0 0 0 0 0 0 0 0 0]
|
|
1050
|
+
[0 0 0 0 0 1 0 0 0 0 0 0]
|
|
1051
|
+
[0 0 0 0 1 0 0 0 0 0 0 0]
|
|
1052
|
+
[0 0 0 0 0 0 0 1 0 0 0 0]
|
|
1053
|
+
[0 0 0 0 0 0 1 0 0 0 0 0]
|
|
1054
|
+
[0 0 0 0 0 0 0 0 2 1 0 0]
|
|
1055
|
+
[0 0 0 0 0 0 0 0 1 2 0 0]
|
|
1056
|
+
[0 0 0 0 0 0 0 0 0 0 1 0]
|
|
1057
|
+
[0 0 0 0 0 0 0 0 0 0 0 7]
|
|
1058
|
+
sage: G = Matrix.block_diagonal([W1, U, V, W1, V, W1, V, W7])
|
|
1059
|
+
sage: B = _partial_normal_form_of_block(G)[1]
|
|
1060
|
+
sage: B * G * B.T
|
|
1061
|
+
[0 1 0 0 0 0 0 0 0 0 0 0]
|
|
1062
|
+
[1 0 0 0 0 0 0 0 0 0 0 0]
|
|
1063
|
+
[0 0 0 1 0 0 0 0 0 0 0 0]
|
|
1064
|
+
[0 0 1 0 0 0 0 0 0 0 0 0]
|
|
1065
|
+
[0 0 0 0 0 1 0 0 0 0 0 0]
|
|
1066
|
+
[0 0 0 0 1 0 0 0 0 0 0 0]
|
|
1067
|
+
[0 0 0 0 0 0 0 1 0 0 0 0]
|
|
1068
|
+
[0 0 0 0 0 0 1 0 0 0 0 0]
|
|
1069
|
+
[0 0 0 0 0 0 0 0 0 1 0 0]
|
|
1070
|
+
[0 0 0 0 0 0 0 0 1 0 0 0]
|
|
1071
|
+
[0 0 0 0 0 0 0 0 0 0 3 0]
|
|
1072
|
+
[0 0 0 0 0 0 0 0 0 0 0 7]
|
|
1073
|
+
"""
|
|
1074
|
+
D = copy(G)
|
|
1075
|
+
n = D.ncols()
|
|
1076
|
+
B = copy(G.parent().identity_matrix()) # the transformation matrix
|
|
1077
|
+
blocks = _get_small_block_indices(D)
|
|
1078
|
+
# collect the indices of forms of types U, V and W
|
|
1079
|
+
U = []
|
|
1080
|
+
V = []
|
|
1081
|
+
W = []
|
|
1082
|
+
for i in blocks:
|
|
1083
|
+
if i + 1 in blocks or i == n - 1:
|
|
1084
|
+
W.append(i)
|
|
1085
|
+
else:
|
|
1086
|
+
if D[i, i] != 0:
|
|
1087
|
+
V += [i, i + 1]
|
|
1088
|
+
else:
|
|
1089
|
+
U += [i, i + 1]
|
|
1090
|
+
if len(W) == 3:
|
|
1091
|
+
# W W W transforms to W U or W V
|
|
1092
|
+
B[W, :] = _relations(D[W, W], 2) * B[W, :]
|
|
1093
|
+
D = B * G * B.T
|
|
1094
|
+
if mod(D[W[1:], W[1:]].det().unit_part(), 8) == 3:
|
|
1095
|
+
V += W[1:]
|
|
1096
|
+
else:
|
|
1097
|
+
U += W[1:]
|
|
1098
|
+
W = W[:1]
|
|
1099
|
+
if len(V) == 4:
|
|
1100
|
+
B[V, :] = _relations(D[V, V], 3) * B[V, :]
|
|
1101
|
+
U += V
|
|
1102
|
+
V = []
|
|
1103
|
+
D = B * G * B.T
|
|
1104
|
+
# put everything into the right order
|
|
1105
|
+
UVW = U + V + W
|
|
1106
|
+
B = B[UVW, :]
|
|
1107
|
+
D = B * G * B.T
|
|
1108
|
+
return D, B, len(W)
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
def _relations(G, n):
|
|
1112
|
+
r"""
|
|
1113
|
+
Return relations of `2`-adic quadratic forms.
|
|
1114
|
+
|
|
1115
|
+
See [MirMor2009]_ IV Prop. 3.2. This function is for internal use only.
|
|
1116
|
+
|
|
1117
|
+
INPUT:
|
|
1118
|
+
|
|
1119
|
+
- ``n`` -- integer between 1 and 10 -- the number of the relation
|
|
1120
|
+
- ``G`` -- a block diagonal matrix consisting of blocks of types `U, V, W`
|
|
1121
|
+
the left side of the relation. If ``G`` does not match `n` then the
|
|
1122
|
+
results are unpredictable.
|
|
1123
|
+
|
|
1124
|
+
OUTPUT:
|
|
1125
|
+
|
|
1126
|
+
Square matrix ``B`` such that ``B * G * B.T`` is the right side of the
|
|
1127
|
+
relation which consists of blocks of types `U`, `V`, `W` again.
|
|
1128
|
+
|
|
1129
|
+
EXAMPLES::
|
|
1130
|
+
|
|
1131
|
+
sage: from sage.quadratic_forms.genera.normal_form import _relations
|
|
1132
|
+
sage: R = Zp(2, type='fixed-mod', print_mode='terse', show_prec=False)
|
|
1133
|
+
sage: U = Matrix(R, 2, [0,1,1,0])
|
|
1134
|
+
sage: V = Matrix(R, 2, [2,1,1,2])
|
|
1135
|
+
sage: W1 = Matrix(R, 1, [1])
|
|
1136
|
+
sage: W3 = Matrix(R, 1, [3])
|
|
1137
|
+
sage: W5 = Matrix(R, 1, [5])
|
|
1138
|
+
sage: W7 = Matrix(R, 1, [7])
|
|
1139
|
+
sage: G = Matrix.block_diagonal(W1,W1)
|
|
1140
|
+
sage: b = _relations(G,1)
|
|
1141
|
+
sage: b * G * b.T
|
|
1142
|
+
[5 0]
|
|
1143
|
+
[0 5]
|
|
1144
|
+
sage: G = Matrix.block_diagonal(W1,W3)
|
|
1145
|
+
sage: b = _relations(G,1)
|
|
1146
|
+
sage: b * G * b.T
|
|
1147
|
+
[5 0]
|
|
1148
|
+
[0 7]
|
|
1149
|
+
sage: G = Matrix.block_diagonal(W1,W5)
|
|
1150
|
+
sage: b = _relations(G,1)
|
|
1151
|
+
sage: b * G * b.T
|
|
1152
|
+
[5 0]
|
|
1153
|
+
[0 1]
|
|
1154
|
+
sage: G = Matrix.block_diagonal(W1,W7)
|
|
1155
|
+
sage: b = _relations(G,1)
|
|
1156
|
+
sage: b * G * b.T
|
|
1157
|
+
[5 0]
|
|
1158
|
+
[0 3]
|
|
1159
|
+
sage: G = Matrix.block_diagonal(W3,W3)
|
|
1160
|
+
sage: b = _relations(G,1)
|
|
1161
|
+
sage: b * G * b.T
|
|
1162
|
+
[7 0]
|
|
1163
|
+
[0 7]
|
|
1164
|
+
sage: G = Matrix.block_diagonal(W3,W5)
|
|
1165
|
+
sage: b = _relations(G,1)
|
|
1166
|
+
sage: b * G * b.T
|
|
1167
|
+
[7 0]
|
|
1168
|
+
[0 1]
|
|
1169
|
+
sage: G = Matrix.block_diagonal(W3,W7)
|
|
1170
|
+
sage: b = _relations(G,1)
|
|
1171
|
+
sage: b * G * b.T
|
|
1172
|
+
[7 0]
|
|
1173
|
+
[0 3]
|
|
1174
|
+
sage: G = Matrix.block_diagonal(W5,W5)
|
|
1175
|
+
sage: b = _relations(G,1)
|
|
1176
|
+
sage: b * G * b.T
|
|
1177
|
+
[1 0]
|
|
1178
|
+
[0 1]
|
|
1179
|
+
sage: G = Matrix.block_diagonal(W5,W7)
|
|
1180
|
+
sage: b = _relations(G,1)
|
|
1181
|
+
sage: b * G * b.T
|
|
1182
|
+
[1 0]
|
|
1183
|
+
[0 3]
|
|
1184
|
+
sage: G = Matrix.block_diagonal(W7,W7)
|
|
1185
|
+
sage: b = _relations(G,1)
|
|
1186
|
+
sage: b * G * b.T
|
|
1187
|
+
[3 0]
|
|
1188
|
+
[0 3]
|
|
1189
|
+
sage: G = Matrix.block_diagonal([V,V])
|
|
1190
|
+
sage: b = _relations(G,3)
|
|
1191
|
+
sage: b * G * b.T
|
|
1192
|
+
[0 1 0 0]
|
|
1193
|
+
[1 0 0 0]
|
|
1194
|
+
[0 0 0 1]
|
|
1195
|
+
[0 0 1 0]
|
|
1196
|
+
sage: G = Matrix.block_diagonal([V,W1,W1])
|
|
1197
|
+
sage: b = _relations(G,5)
|
|
1198
|
+
sage: b * G * b.T
|
|
1199
|
+
[0 1 0 0]
|
|
1200
|
+
[1 0 0 0]
|
|
1201
|
+
[0 0 7 0]
|
|
1202
|
+
[0 0 0 3]
|
|
1203
|
+
sage: G = Matrix.block_diagonal([V,W1,W5])
|
|
1204
|
+
sage: b = _relations(G,5)
|
|
1205
|
+
sage: b * G * b.T
|
|
1206
|
+
[0 1 0 0]
|
|
1207
|
+
[1 0 0 0]
|
|
1208
|
+
[0 0 3 0]
|
|
1209
|
+
[0 0 0 3]
|
|
1210
|
+
sage: G = Matrix.block_diagonal([V,W3,W7])
|
|
1211
|
+
sage: b = _relations(G,5)
|
|
1212
|
+
sage: b * G * b.T
|
|
1213
|
+
[0 1 0 0]
|
|
1214
|
+
[1 0 0 0]
|
|
1215
|
+
[0 0 5 0]
|
|
1216
|
+
[0 0 0 5]
|
|
1217
|
+
sage: G = Matrix.block_diagonal([W1,2*W1])
|
|
1218
|
+
sage: b = _relations(G,6)
|
|
1219
|
+
sage: b * G * b.T
|
|
1220
|
+
[3 0]
|
|
1221
|
+
[0 6]
|
|
1222
|
+
sage: G = Matrix.block_diagonal([W1,2*W3])
|
|
1223
|
+
sage: b = _relations(G,6)
|
|
1224
|
+
sage: b * G * b.T
|
|
1225
|
+
[ 7 0]
|
|
1226
|
+
[ 0 10]
|
|
1227
|
+
sage: G = Matrix.block_diagonal([W1,2*W5])
|
|
1228
|
+
sage: b = _relations(G,6)
|
|
1229
|
+
sage: b * G * b.T
|
|
1230
|
+
[ 3 0]
|
|
1231
|
+
[ 0 14]
|
|
1232
|
+
sage: G = Matrix.block_diagonal([W1,2*W7])
|
|
1233
|
+
sage: b = _relations(G,6)
|
|
1234
|
+
sage: b * G * b.T
|
|
1235
|
+
[7 0]
|
|
1236
|
+
[0 2]
|
|
1237
|
+
sage: G = Matrix.block_diagonal([W3,2*W5])
|
|
1238
|
+
sage: b = _relations(G,6)
|
|
1239
|
+
sage: b * G * b.T
|
|
1240
|
+
[5 0]
|
|
1241
|
+
[0 6]
|
|
1242
|
+
sage: G = Matrix.block_diagonal([W3,2*W3])
|
|
1243
|
+
sage: b = _relations(G,6)
|
|
1244
|
+
sage: b * G * b.T
|
|
1245
|
+
[1 0]
|
|
1246
|
+
[0 2]
|
|
1247
|
+
sage: G = Matrix.block_diagonal([2*W5,4*W7])
|
|
1248
|
+
sage: b = _relations(G,6)
|
|
1249
|
+
sage: b * G * b.T
|
|
1250
|
+
[6 0]
|
|
1251
|
+
[0 4]
|
|
1252
|
+
sage: G = Matrix.block_diagonal([W3,2*V])
|
|
1253
|
+
sage: b = _relations(G,7)
|
|
1254
|
+
sage: b * G * b.T
|
|
1255
|
+
[7 0 0]
|
|
1256
|
+
[0 0 2]
|
|
1257
|
+
[0 2 0]
|
|
1258
|
+
sage: G = Matrix.block_diagonal([W7,2*V])
|
|
1259
|
+
sage: b = _relations(G,7)
|
|
1260
|
+
sage: b * G * b.T
|
|
1261
|
+
[3 0 0]
|
|
1262
|
+
[0 0 2]
|
|
1263
|
+
[0 2 0]
|
|
1264
|
+
sage: G = Matrix.block_diagonal([U,2*W1])
|
|
1265
|
+
sage: b = _relations(G,8)
|
|
1266
|
+
sage: b * G * b.T
|
|
1267
|
+
[ 2 1 0]
|
|
1268
|
+
[ 1 2 0]
|
|
1269
|
+
[ 0 0 10]
|
|
1270
|
+
sage: G = Matrix.block_diagonal([U,2*W5])
|
|
1271
|
+
sage: b = _relations(G,8)
|
|
1272
|
+
sage: b * G * b.T
|
|
1273
|
+
[2 1 0]
|
|
1274
|
+
[1 2 0]
|
|
1275
|
+
[0 0 2]
|
|
1276
|
+
sage: G = Matrix.block_diagonal([V,2*W1])
|
|
1277
|
+
sage: b = _relations(G,8)
|
|
1278
|
+
sage: b * G * b.T
|
|
1279
|
+
[ 0 1 0]
|
|
1280
|
+
[ 1 0 0]
|
|
1281
|
+
[ 0 0 10]
|
|
1282
|
+
sage: G = Matrix.block_diagonal([V,2*W7])
|
|
1283
|
+
sage: b = _relations(G,8)
|
|
1284
|
+
sage: b * G * b.T
|
|
1285
|
+
[0 1 0]
|
|
1286
|
+
[1 0 0]
|
|
1287
|
+
[0 0 6]
|
|
1288
|
+
sage: G = Matrix.block_diagonal([W1,W5,2*W5])
|
|
1289
|
+
sage: b = _relations(G,9)
|
|
1290
|
+
sage: b * G * b.T
|
|
1291
|
+
[3 0 0]
|
|
1292
|
+
[0 3 0]
|
|
1293
|
+
[0 0 2]
|
|
1294
|
+
sage: G = Matrix.block_diagonal([W3,W3,2*W5])
|
|
1295
|
+
sage: b = _relations(G,9)
|
|
1296
|
+
sage: b * G * b.T
|
|
1297
|
+
[5 0 0]
|
|
1298
|
+
[0 1 0]
|
|
1299
|
+
[0 0 2]
|
|
1300
|
+
sage: G = Matrix.block_diagonal([W3,W3,2*W1])
|
|
1301
|
+
sage: b = _relations(G,9)
|
|
1302
|
+
sage: b * G * b.T
|
|
1303
|
+
[ 5 0 0]
|
|
1304
|
+
[ 0 1 0]
|
|
1305
|
+
[ 0 0 10]
|
|
1306
|
+
sage: G = Matrix.block_diagonal([W3,4*W1])
|
|
1307
|
+
sage: b = _relations(G,10)
|
|
1308
|
+
sage: b * G * b.T
|
|
1309
|
+
[ 7 0]
|
|
1310
|
+
[ 0 20]
|
|
1311
|
+
sage: G = Matrix.block_diagonal([W5,4*W5])
|
|
1312
|
+
sage: b = _relations(G,10)
|
|
1313
|
+
sage: b * G * b.T
|
|
1314
|
+
[1 0]
|
|
1315
|
+
[0 4]
|
|
1316
|
+
"""
|
|
1317
|
+
R = G.base_ring()
|
|
1318
|
+
if n == 1:
|
|
1319
|
+
e1 = G[0, 0].unit_part()
|
|
1320
|
+
e2 = G[1, 1].unit_part()
|
|
1321
|
+
B = Matrix(R, 2, 2, [1, 2, 2 * e2, -e1])
|
|
1322
|
+
elif n == 2:
|
|
1323
|
+
e1 = G[0, 0].unit_part()
|
|
1324
|
+
e2 = G[1, 1].unit_part()
|
|
1325
|
+
e3 = G[2, 2].unit_part()
|
|
1326
|
+
B = Matrix(R, 3, 3, [1, 1, 1, e2, -e1, 0, e3, 0, -e1])
|
|
1327
|
+
elif n == 3:
|
|
1328
|
+
B = Matrix(R, 4, 4,
|
|
1329
|
+
[1, 1, 1, 0, 1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, -1])
|
|
1330
|
+
elif n == 4:
|
|
1331
|
+
raise NotImplementedError("relation 4 is not needed")
|
|
1332
|
+
elif n == 5:
|
|
1333
|
+
e1 = G[2, 2].unit_part()
|
|
1334
|
+
e2 = G[3, 3].unit_part()
|
|
1335
|
+
if mod(e1, 4) != mod(e2, 4):
|
|
1336
|
+
raise ValueError("W is of the wrong type for relation 5")
|
|
1337
|
+
B = Matrix(R, 4, [1, 0, 1, 1,
|
|
1338
|
+
0, 1, 1, 1,
|
|
1339
|
+
-e2, -e2, 0, 3,
|
|
1340
|
+
-e1, -e1, 2 * e2 + 3, -2 * e1])
|
|
1341
|
+
elif n == 6:
|
|
1342
|
+
if G[0, 0].valuation() + 1 != G[1, 1].valuation():
|
|
1343
|
+
raise ValueError("wrong scales for relation 6")
|
|
1344
|
+
e1 = G[0, 0].unit_part()
|
|
1345
|
+
e2 = G[1, 1].unit_part()
|
|
1346
|
+
B = Matrix(R, 2, 2, [1, 1, -2 * e2, e1])
|
|
1347
|
+
elif n == 7:
|
|
1348
|
+
e = G[0, 0].unit_part()
|
|
1349
|
+
B = Matrix(R, 3, 3, [-3, e**2, e**2, 2 * e, 1, 0, 2 * e, 0, 1])
|
|
1350
|
+
elif n == 8:
|
|
1351
|
+
e = G[2, 2].unit_part()
|
|
1352
|
+
if G[0, 0] == 0:
|
|
1353
|
+
B = Matrix(R, 3, 3, [e, 0, -1,
|
|
1354
|
+
0, e, -1,
|
|
1355
|
+
2, 2, 1])
|
|
1356
|
+
else:
|
|
1357
|
+
B = Matrix(R, 3, 3, [1, 0, 1,
|
|
1358
|
+
0, 1, 1,
|
|
1359
|
+
2 * e, 2 * e, - 3])
|
|
1360
|
+
elif n == 9:
|
|
1361
|
+
e1 = G[0, 0].unit_part()
|
|
1362
|
+
e2 = G[1, 1].unit_part()
|
|
1363
|
+
e3 = G[2, 2].unit_part()
|
|
1364
|
+
B = Matrix(R, 3, 3, [1, 0, 1,
|
|
1365
|
+
2 * e3, 1, -e1,
|
|
1366
|
+
-2 * e2 * e3, 2 * e1**2 * e3 + 4 * e1 * e3**2,
|
|
1367
|
+
e1 * e2])
|
|
1368
|
+
elif n == 10:
|
|
1369
|
+
e1 = G[0, 0].unit_part()
|
|
1370
|
+
e2 = G[1, 1].unit_part()
|
|
1371
|
+
B = Matrix(R, 2, 2, [1, 1, -4 * e2, e1])
|
|
1372
|
+
_, B1 = _normalize(B * G * B.T)
|
|
1373
|
+
return B1 * B
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
def _two_adic_normal_forms(G, partial=False):
|
|
1377
|
+
r"""
|
|
1378
|
+
Return the 2-adic normal form of a symmetric matrix.
|
|
1379
|
+
|
|
1380
|
+
INPUT:
|
|
1381
|
+
|
|
1382
|
+
- ``G`` -- block diagonal matrix with blocks of type `U`, `V`, `W`
|
|
1383
|
+
- ``partial`` -- boolean (default: ``False``)
|
|
1384
|
+
|
|
1385
|
+
OUTPUT: ``D``, ``B``; such that ``D = B * G * B.T``
|
|
1386
|
+
|
|
1387
|
+
EXAMPLES::
|
|
1388
|
+
|
|
1389
|
+
sage: from sage.quadratic_forms.genera.normal_form import _two_adic_normal_forms
|
|
1390
|
+
sage: R = Zp(2, type='fixed-mod', print_mode='terse', show_prec=False)
|
|
1391
|
+
sage: U = Matrix(R, 2, [0,1,1,0])
|
|
1392
|
+
sage: V = Matrix(R, 2, [2,1,1,2])
|
|
1393
|
+
sage: W1 = Matrix(R, 1, [1])
|
|
1394
|
+
sage: W3 = Matrix(R, 1, [3])
|
|
1395
|
+
sage: W5 = Matrix(R, 1, [5])
|
|
1396
|
+
sage: W7 = Matrix(R, 1, [7])
|
|
1397
|
+
sage: G = Matrix.block_diagonal([2*W1,2*W1,4*V])
|
|
1398
|
+
sage: B = _two_adic_normal_forms(G)[1]
|
|
1399
|
+
sage: B * G * B.T
|
|
1400
|
+
[ 2 0 0 0]
|
|
1401
|
+
[ 0 10 0 0]
|
|
1402
|
+
[ 0 0 0 4]
|
|
1403
|
+
[ 0 0 4 0]
|
|
1404
|
+
sage: G = Matrix.block_diagonal([W1,2*V,2*W3,2*W5])
|
|
1405
|
+
sage: B = _two_adic_normal_forms(G)[1]
|
|
1406
|
+
sage: B * G * B.T
|
|
1407
|
+
[3 0 0 0 0]
|
|
1408
|
+
[0 0 2 0 0]
|
|
1409
|
+
[0 2 0 0 0]
|
|
1410
|
+
[0 0 0 2 0]
|
|
1411
|
+
[0 0 0 0 2]
|
|
1412
|
+
sage: G = Matrix.block_diagonal([U,2*V,2*W3,2*W5])
|
|
1413
|
+
sage: B = _two_adic_normal_forms(G)[1]
|
|
1414
|
+
sage: B * G * B.T
|
|
1415
|
+
[2 1 0 0 0 0]
|
|
1416
|
+
[1 2 0 0 0 0]
|
|
1417
|
+
[0 0 4 2 0 0]
|
|
1418
|
+
[0 0 2 4 0 0]
|
|
1419
|
+
[0 0 0 0 2 0]
|
|
1420
|
+
[0 0 0 0 0 6]
|
|
1421
|
+
"""
|
|
1422
|
+
B = copy(G.parent().identity_matrix())
|
|
1423
|
+
h, scales = _get_homogeneous_block_indices(G)
|
|
1424
|
+
h.append(B.ncols())
|
|
1425
|
+
# UVlist[k] is a list of indices of the block of scale p^k.
|
|
1426
|
+
# It contains the indices of the part of types U or V.
|
|
1427
|
+
# So it may be empty.
|
|
1428
|
+
UVlist = [[], []] # empty lists are appended to avoid special cases.
|
|
1429
|
+
# same as UVlist but contains the indices of the part of type W
|
|
1430
|
+
Wlist = [[], []]
|
|
1431
|
+
# homogeneous normal form for each part
|
|
1432
|
+
for k in range(scales[-1] - scales[0] + 1):
|
|
1433
|
+
if k + scales[0] in scales:
|
|
1434
|
+
i = scales.index(k + scales[0])
|
|
1435
|
+
Gk = G[h[i]:h[i + 1], h[i]:h[i + 1]]
|
|
1436
|
+
Dk, Bk, wk = _partial_normal_form_of_block(Gk)
|
|
1437
|
+
B[h[i]:h[i + 1], :] = Bk * B[h[i]:h[i + 1], :]
|
|
1438
|
+
if not partial:
|
|
1439
|
+
Dk, B1k = _homogeneous_normal_form(Dk, wk)
|
|
1440
|
+
B[h[i]:h[i + 1], :] = B1k * B[h[i]:h[i + 1], :]
|
|
1441
|
+
UVlist.append(list(range(h[i], h[i + 1] - wk)))
|
|
1442
|
+
Wlist.append(list(range(h[i + 1] - wk, h[i + 1])))
|
|
1443
|
+
else:
|
|
1444
|
+
UVlist.append([])
|
|
1445
|
+
Wlist.append([])
|
|
1446
|
+
D = B * G * B.T
|
|
1447
|
+
if partial:
|
|
1448
|
+
return D, B
|
|
1449
|
+
# use relations descending in k
|
|
1450
|
+
# we never leave partial normal form
|
|
1451
|
+
# but the homogeneous normal form may be destroyed
|
|
1452
|
+
# it is restored at the end.
|
|
1453
|
+
for k in range(len(UVlist) - 1, 2, -1):
|
|
1454
|
+
# setup notation
|
|
1455
|
+
W = Wlist[k]
|
|
1456
|
+
Wm = Wlist[k - 1]
|
|
1457
|
+
Wmm = Wlist[k - 2]
|
|
1458
|
+
UV = UVlist[k]
|
|
1459
|
+
UVm = UVlist[k - 1]
|
|
1460
|
+
V = UVlist[k][-2:]
|
|
1461
|
+
if V and D[V[0], V[0]] == 0:
|
|
1462
|
+
V = [] # it is U not V
|
|
1463
|
+
# condition b)
|
|
1464
|
+
if Wm:
|
|
1465
|
+
if len(V) == 2:
|
|
1466
|
+
R = Wm[:1] + V
|
|
1467
|
+
B[R, :] = _relations(D[R, R], 7) * B[R, :]
|
|
1468
|
+
V = []
|
|
1469
|
+
D = B * G * B.T
|
|
1470
|
+
E = {3, 7}
|
|
1471
|
+
for w in W:
|
|
1472
|
+
if D[w, w].unit_part() in E:
|
|
1473
|
+
R = Wm[:1] + [w]
|
|
1474
|
+
B[R, :] = _relations(D[R, R], 6) * B[R, :]
|
|
1475
|
+
D = B * G * B.T
|
|
1476
|
+
# condition c)
|
|
1477
|
+
# We want type a or W = []
|
|
1478
|
+
# modify D[w,w] to go from type b to type a
|
|
1479
|
+
x = [len(V)] + [ZZ(mod(w.unit_part(), 8)) for w in D[W, W].diagonal()]
|
|
1480
|
+
if len(x) == 3 and x[1] > x[2]:
|
|
1481
|
+
x[1], x[2] = x[2], x[1]
|
|
1482
|
+
# the first entry of x is either
|
|
1483
|
+
# 0 if there is no type V component or
|
|
1484
|
+
# 2 if there is a single type V component
|
|
1485
|
+
# a = [[0,1], [2,3], [2,5], [0,7], [0,1,1], [2,1,3], [0,7,7], [0,1,7]]
|
|
1486
|
+
b = [[0, 5], [2, 7], [2, 1], [0, 3],
|
|
1487
|
+
[0, 1, 5], [2, 1, 7], [0, 3, 7], [0, 1, 3]]
|
|
1488
|
+
if x in b:
|
|
1489
|
+
w = W[-1]
|
|
1490
|
+
if x == [0, 3, 7]:
|
|
1491
|
+
# relation 10 should be applied to 3 to stay in homogeneous normal form
|
|
1492
|
+
w = W[0]
|
|
1493
|
+
if UVm:
|
|
1494
|
+
R = UVm[-2:] + [w]
|
|
1495
|
+
B[R, :] = _relations(D[R, R], 8) * B[R, :]
|
|
1496
|
+
elif Wmm:
|
|
1497
|
+
R = Wmm[:1] + [w]
|
|
1498
|
+
B[R, :] = _relations(D[R, R], 10) * B[R, :]
|
|
1499
|
+
elif len(Wm) == 2:
|
|
1500
|
+
e0 = D[Wm, Wm][0, 0].unit_part()
|
|
1501
|
+
e1 = D[Wm, Wm][1, 1].unit_part()
|
|
1502
|
+
if mod(e1 - e0, 4) == 0:
|
|
1503
|
+
R = Wm + [w]
|
|
1504
|
+
B[R, :] = _relations(D[R, R], 9) * B[R, :]
|
|
1505
|
+
D = B * G * B.T
|
|
1506
|
+
# condition a) - stay in homogeneous normal form
|
|
1507
|
+
R = UV + W
|
|
1508
|
+
Dk = D[R, R]
|
|
1509
|
+
Bk = _homogeneous_normal_form(Dk, len(W))[1]
|
|
1510
|
+
B[R, :] = Bk * B[R, :]
|
|
1511
|
+
D = B * G * B.T
|
|
1512
|
+
# we need to restore the homogeneous normal form of k-1
|
|
1513
|
+
if Wm:
|
|
1514
|
+
R = UVm + Wm
|
|
1515
|
+
Dkm = D[R, R]
|
|
1516
|
+
Bkm = _homogeneous_normal_form(Dkm, len(Wm))[1]
|
|
1517
|
+
B[R, :] = Bkm * B[R, :]
|
|
1518
|
+
D = B * G * B.T
|
|
1519
|
+
return D, B
|