passagemath-modules 10.6.31rc3__cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_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-modules might be problematic. Click here for more details.
- passagemath_modules-10.6.31rc3.dist-info/METADATA +281 -0
- passagemath_modules-10.6.31rc3.dist-info/RECORD +807 -0
- passagemath_modules-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_modules-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_modules.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
- passagemath_modules.libs/libgmp-6e109695.so.10.5.0 +0 -0
- passagemath_modules.libs/libgsl-cda90e79.so.28.0.0 +0 -0
- passagemath_modules.libs/libmpc-7f678fcf.so.3.3.1 +0 -0
- passagemath_modules.libs/libmpfr-82690d50.so.6.2.1 +0 -0
- passagemath_modules.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
- passagemath_modules.libs/libquadmath-2284e583.so.0.0.0 +0 -0
- sage/algebras/all__sagemath_modules.py +20 -0
- sage/algebras/catalog.py +148 -0
- sage/algebras/clifford_algebra.py +3107 -0
- sage/algebras/clifford_algebra_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/algebras/clifford_algebra_element.pxd +16 -0
- sage/algebras/clifford_algebra_element.pyx +997 -0
- sage/algebras/commutative_dga.py +4252 -0
- sage/algebras/exterior_algebra_groebner.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/algebras/exterior_algebra_groebner.pxd +55 -0
- sage/algebras/exterior_algebra_groebner.pyx +727 -0
- sage/algebras/finite_dimensional_algebras/all.py +2 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +1029 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pxd +12 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx +706 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_ideal.py +196 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_morphism.py +255 -0
- sage/algebras/finite_gca.py +528 -0
- sage/algebras/group_algebra.py +232 -0
- sage/algebras/lie_algebras/abelian.py +197 -0
- sage/algebras/lie_algebras/affine_lie_algebra.py +1213 -0
- sage/algebras/lie_algebras/all.py +25 -0
- sage/algebras/lie_algebras/all__sagemath_modules.py +1 -0
- sage/algebras/lie_algebras/bch.py +177 -0
- sage/algebras/lie_algebras/bgg_dual_module.py +1184 -0
- sage/algebras/lie_algebras/bgg_resolution.py +232 -0
- sage/algebras/lie_algebras/center_uea.py +767 -0
- sage/algebras/lie_algebras/classical_lie_algebra.py +2516 -0
- sage/algebras/lie_algebras/examples.py +683 -0
- sage/algebras/lie_algebras/free_lie_algebra.py +973 -0
- sage/algebras/lie_algebras/heisenberg.py +820 -0
- sage/algebras/lie_algebras/lie_algebra.py +1562 -0
- sage/algebras/lie_algebras/lie_algebra_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/algebras/lie_algebras/lie_algebra_element.pxd +68 -0
- sage/algebras/lie_algebras/lie_algebra_element.pyx +2122 -0
- sage/algebras/lie_algebras/morphism.py +661 -0
- sage/algebras/lie_algebras/nilpotent_lie_algebra.py +457 -0
- sage/algebras/lie_algebras/onsager.py +1324 -0
- sage/algebras/lie_algebras/poincare_birkhoff_witt.py +816 -0
- sage/algebras/lie_algebras/quotient.py +462 -0
- sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py +355 -0
- sage/algebras/lie_algebras/representation.py +1040 -0
- sage/algebras/lie_algebras/structure_coefficients.py +459 -0
- sage/algebras/lie_algebras/subalgebra.py +967 -0
- sage/algebras/lie_algebras/symplectic_derivation.py +289 -0
- sage/algebras/lie_algebras/verma_module.py +1630 -0
- sage/algebras/lie_algebras/virasoro.py +1186 -0
- sage/algebras/octonion_algebra.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/algebras/octonion_algebra.pxd +20 -0
- sage/algebras/octonion_algebra.pyx +987 -0
- sage/algebras/orlik_solomon.py +907 -0
- sage/algebras/orlik_terao.py +779 -0
- sage/algebras/steenrod/all.py +7 -0
- sage/algebras/steenrod/steenrod_algebra.py +4258 -0
- sage/algebras/steenrod/steenrod_algebra_bases.py +1179 -0
- sage/algebras/steenrod/steenrod_algebra_misc.py +1167 -0
- sage/algebras/steenrod/steenrod_algebra_mult.py +954 -0
- sage/algebras/weyl_algebra.py +1126 -0
- sage/all__sagemath_modules.py +62 -0
- sage/calculus/all__sagemath_modules.py +19 -0
- sage/calculus/expr.py +205 -0
- sage/calculus/integration.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/calculus/integration.pyx +698 -0
- sage/calculus/interpolation.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/calculus/interpolation.pxd +13 -0
- sage/calculus/interpolation.pyx +387 -0
- sage/calculus/interpolators.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/calculus/interpolators.pyx +326 -0
- sage/calculus/ode.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/calculus/ode.pxd +5 -0
- sage/calculus/ode.pyx +610 -0
- sage/calculus/riemann.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/calculus/riemann.pyx +1521 -0
- sage/calculus/test_sympy.py +201 -0
- sage/calculus/transforms/all.py +7 -0
- sage/calculus/transforms/dft.py +844 -0
- sage/calculus/transforms/dwt.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/calculus/transforms/dwt.pxd +7 -0
- sage/calculus/transforms/dwt.pyx +160 -0
- sage/calculus/transforms/fft.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/calculus/transforms/fft.pxd +12 -0
- sage/calculus/transforms/fft.pyx +487 -0
- sage/calculus/wester.py +662 -0
- sage/coding/abstract_code.py +1108 -0
- sage/coding/ag_code.py +868 -0
- sage/coding/ag_code_decoders.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/coding/ag_code_decoders.pyx +2639 -0
- sage/coding/all.py +15 -0
- sage/coding/bch_code.py +494 -0
- sage/coding/binary_code.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/coding/binary_code.pxd +124 -0
- sage/coding/binary_code.pyx +4139 -0
- sage/coding/bounds_catalog.py +43 -0
- sage/coding/channel.py +819 -0
- sage/coding/channels_catalog.py +29 -0
- sage/coding/code_bounds.py +755 -0
- sage/coding/code_constructions.py +804 -0
- sage/coding/codes_catalog.py +111 -0
- sage/coding/cyclic_code.py +1329 -0
- sage/coding/databases.py +316 -0
- sage/coding/decoder.py +373 -0
- sage/coding/decoders_catalog.py +88 -0
- sage/coding/delsarte_bounds.py +709 -0
- sage/coding/encoder.py +390 -0
- sage/coding/encoders_catalog.py +64 -0
- sage/coding/extended_code.py +468 -0
- sage/coding/gabidulin_code.py +1058 -0
- sage/coding/golay_code.py +404 -0
- sage/coding/goppa_code.py +441 -0
- sage/coding/grs_code.py +2371 -0
- sage/coding/guava.py +107 -0
- sage/coding/guruswami_sudan/all.py +1 -0
- sage/coding/guruswami_sudan/gs_decoder.py +897 -0
- sage/coding/guruswami_sudan/interpolation.py +409 -0
- sage/coding/guruswami_sudan/utils.py +176 -0
- sage/coding/hamming_code.py +176 -0
- sage/coding/information_set_decoder.py +1032 -0
- sage/coding/kasami_codes.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/coding/kasami_codes.pyx +351 -0
- sage/coding/linear_code.py +3067 -0
- sage/coding/linear_code_no_metric.py +1354 -0
- sage/coding/linear_rank_metric.py +961 -0
- sage/coding/parity_check_code.py +353 -0
- sage/coding/punctured_code.py +719 -0
- sage/coding/reed_muller_code.py +999 -0
- sage/coding/self_dual_codes.py +942 -0
- sage/coding/source_coding/all.py +2 -0
- sage/coding/source_coding/huffman.py +553 -0
- sage/coding/subfield_subcode.py +423 -0
- sage/coding/two_weight_db.py +399 -0
- sage/combinat/all__sagemath_modules.py +7 -0
- sage/combinat/cartesian_product.py +347 -0
- sage/combinat/family.py +11 -0
- sage/combinat/free_module.py +1977 -0
- sage/combinat/root_system/all.py +147 -0
- sage/combinat/root_system/ambient_space.py +527 -0
- sage/combinat/root_system/associahedron.py +471 -0
- sage/combinat/root_system/braid_move_calculator.py +143 -0
- sage/combinat/root_system/braid_orbit.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/combinat/root_system/braid_orbit.pyx +144 -0
- sage/combinat/root_system/branching_rules.py +2301 -0
- sage/combinat/root_system/cartan_matrix.py +1245 -0
- sage/combinat/root_system/cartan_type.py +3069 -0
- sage/combinat/root_system/coxeter_group.py +162 -0
- sage/combinat/root_system/coxeter_matrix.py +1261 -0
- sage/combinat/root_system/coxeter_type.py +681 -0
- sage/combinat/root_system/dynkin_diagram.py +900 -0
- sage/combinat/root_system/extended_affine_weyl_group.py +2993 -0
- sage/combinat/root_system/fundamental_group.py +795 -0
- sage/combinat/root_system/hecke_algebra_representation.py +1203 -0
- sage/combinat/root_system/integrable_representations.py +1227 -0
- sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +1965 -0
- sage/combinat/root_system/pieri_factors.py +1147 -0
- sage/combinat/root_system/plot.py +1615 -0
- sage/combinat/root_system/root_lattice_realization_algebras.py +1214 -0
- sage/combinat/root_system/root_lattice_realizations.py +4628 -0
- sage/combinat/root_system/root_space.py +487 -0
- sage/combinat/root_system/root_system.py +882 -0
- sage/combinat/root_system/type_A.py +348 -0
- sage/combinat/root_system/type_A_affine.py +227 -0
- sage/combinat/root_system/type_A_infinity.py +241 -0
- sage/combinat/root_system/type_B.py +347 -0
- sage/combinat/root_system/type_BC_affine.py +287 -0
- sage/combinat/root_system/type_B_affine.py +216 -0
- sage/combinat/root_system/type_C.py +317 -0
- sage/combinat/root_system/type_C_affine.py +188 -0
- sage/combinat/root_system/type_D.py +357 -0
- sage/combinat/root_system/type_D_affine.py +208 -0
- sage/combinat/root_system/type_E.py +641 -0
- sage/combinat/root_system/type_E_affine.py +231 -0
- sage/combinat/root_system/type_F.py +387 -0
- sage/combinat/root_system/type_F_affine.py +137 -0
- sage/combinat/root_system/type_G.py +293 -0
- sage/combinat/root_system/type_G_affine.py +132 -0
- sage/combinat/root_system/type_H.py +105 -0
- sage/combinat/root_system/type_I.py +110 -0
- sage/combinat/root_system/type_Q.py +150 -0
- sage/combinat/root_system/type_affine.py +509 -0
- sage/combinat/root_system/type_dual.py +704 -0
- sage/combinat/root_system/type_folded.py +301 -0
- sage/combinat/root_system/type_marked.py +748 -0
- sage/combinat/root_system/type_reducible.py +601 -0
- sage/combinat/root_system/type_relabel.py +730 -0
- sage/combinat/root_system/type_super_A.py +837 -0
- sage/combinat/root_system/weight_lattice_realizations.py +1188 -0
- sage/combinat/root_system/weight_space.py +639 -0
- sage/combinat/root_system/weyl_characters.py +2238 -0
- sage/crypto/__init__.py +4 -0
- sage/crypto/all.py +28 -0
- sage/crypto/block_cipher/all.py +7 -0
- sage/crypto/block_cipher/des.py +1065 -0
- sage/crypto/block_cipher/miniaes.py +2171 -0
- sage/crypto/block_cipher/present.py +909 -0
- sage/crypto/block_cipher/sdes.py +1527 -0
- sage/crypto/boolean_function.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/crypto/boolean_function.pxd +10 -0
- sage/crypto/boolean_function.pyx +1487 -0
- sage/crypto/cipher.py +78 -0
- sage/crypto/classical.py +3668 -0
- sage/crypto/classical_cipher.py +569 -0
- sage/crypto/cryptosystem.py +387 -0
- sage/crypto/key_exchange/all.py +7 -0
- sage/crypto/key_exchange/catalog.py +24 -0
- sage/crypto/key_exchange/diffie_hellman.py +323 -0
- sage/crypto/key_exchange/key_exchange_scheme.py +107 -0
- sage/crypto/lattice.py +312 -0
- sage/crypto/lfsr.py +295 -0
- sage/crypto/lwe.py +840 -0
- sage/crypto/mq/__init__.py +4 -0
- sage/crypto/mq/mpolynomialsystemgenerator.py +204 -0
- sage/crypto/mq/rijndael_gf.py +2345 -0
- sage/crypto/mq/sbox.py +7 -0
- sage/crypto/mq/sr.py +3344 -0
- sage/crypto/public_key/all.py +5 -0
- sage/crypto/public_key/blum_goldwasser.py +776 -0
- sage/crypto/sbox.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/crypto/sbox.pyx +2090 -0
- sage/crypto/sboxes.py +2090 -0
- sage/crypto/stream.py +390 -0
- sage/crypto/stream_cipher.py +297 -0
- sage/crypto/util.py +519 -0
- sage/ext/all__sagemath_modules.py +1 -0
- sage/ext/interpreters/__init__.py +1 -0
- sage/ext/interpreters/all__sagemath_modules.py +2 -0
- sage/ext/interpreters/wrapper_cc.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/ext/interpreters/wrapper_cc.pxd +30 -0
- sage/ext/interpreters/wrapper_cc.pyx +252 -0
- sage/ext/interpreters/wrapper_cdf.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/ext/interpreters/wrapper_cdf.pxd +26 -0
- sage/ext/interpreters/wrapper_cdf.pyx +245 -0
- sage/ext/interpreters/wrapper_rdf.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/ext/interpreters/wrapper_rdf.pxd +23 -0
- sage/ext/interpreters/wrapper_rdf.pyx +221 -0
- sage/ext/interpreters/wrapper_rr.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/ext/interpreters/wrapper_rr.pxd +28 -0
- sage/ext/interpreters/wrapper_rr.pyx +335 -0
- sage/geometry/all__sagemath_modules.py +5 -0
- sage/geometry/toric_lattice.py +1745 -0
- sage/geometry/toric_lattice_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/toric_lattice_element.pyx +432 -0
- sage/groups/abelian_gps/abelian_group.py +1925 -0
- sage/groups/abelian_gps/abelian_group_element.py +164 -0
- sage/groups/abelian_gps/all__sagemath_modules.py +5 -0
- sage/groups/abelian_gps/dual_abelian_group.py +421 -0
- sage/groups/abelian_gps/dual_abelian_group_element.py +179 -0
- sage/groups/abelian_gps/element_base.py +341 -0
- sage/groups/abelian_gps/values.py +488 -0
- sage/groups/additive_abelian/additive_abelian_group.py +476 -0
- sage/groups/additive_abelian/additive_abelian_wrapper.py +857 -0
- sage/groups/additive_abelian/all.py +4 -0
- sage/groups/additive_abelian/qmodnz.py +231 -0
- sage/groups/additive_abelian/qmodnz_element.py +349 -0
- sage/groups/affine_gps/affine_group.py +535 -0
- sage/groups/affine_gps/all.py +1 -0
- sage/groups/affine_gps/catalog.py +17 -0
- sage/groups/affine_gps/euclidean_group.py +246 -0
- sage/groups/affine_gps/group_element.py +562 -0
- sage/groups/all__sagemath_modules.py +12 -0
- sage/groups/galois_group.py +479 -0
- sage/groups/matrix_gps/all.py +4 -0
- sage/groups/matrix_gps/all__sagemath_modules.py +13 -0
- sage/groups/matrix_gps/catalog.py +26 -0
- sage/groups/matrix_gps/coxeter_group.py +927 -0
- sage/groups/matrix_gps/finitely_generated.py +487 -0
- sage/groups/matrix_gps/group_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/groups/matrix_gps/group_element.pxd +11 -0
- sage/groups/matrix_gps/group_element.pyx +431 -0
- sage/groups/matrix_gps/linear.py +440 -0
- sage/groups/matrix_gps/matrix_group.py +617 -0
- sage/groups/matrix_gps/named_group.py +296 -0
- sage/groups/matrix_gps/orthogonal.py +544 -0
- sage/groups/matrix_gps/symplectic.py +251 -0
- sage/groups/matrix_gps/unitary.py +436 -0
- sage/groups/misc_gps/all__sagemath_modules.py +1 -0
- sage/groups/misc_gps/argument_groups.py +1905 -0
- sage/groups/misc_gps/imaginary_groups.py +479 -0
- sage/groups/perm_gps/all__sagemath_modules.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_modules.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_binary.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_binary.pxd +41 -0
- sage/groups/perm_gps/partn_ref/refinement_binary.pyx +1167 -0
- sage/groups/perm_gps/partn_ref/refinement_matrices.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_matrices.pxd +31 -0
- sage/groups/perm_gps/partn_ref/refinement_matrices.pyx +385 -0
- sage/homology/algebraic_topological_model.py +595 -0
- sage/homology/all.py +2 -0
- sage/homology/all__sagemath_modules.py +8 -0
- sage/homology/chain_complex.py +2148 -0
- sage/homology/chain_complex_homspace.py +165 -0
- sage/homology/chain_complex_morphism.py +629 -0
- sage/homology/chain_homotopy.py +604 -0
- sage/homology/chains.py +653 -0
- sage/homology/free_resolution.py +923 -0
- sage/homology/graded_resolution.py +567 -0
- sage/homology/hochschild_complex.py +756 -0
- sage/homology/homology_group.py +188 -0
- sage/homology/homology_morphism.py +422 -0
- sage/homology/homology_vector_space_with_basis.py +1454 -0
- sage/homology/koszul_complex.py +169 -0
- sage/homology/matrix_utils.py +205 -0
- sage/libs/all__sagemath_modules.py +1 -0
- sage/libs/gsl/__init__.py +1 -0
- sage/libs/gsl/airy.pxd +56 -0
- sage/libs/gsl/all.pxd +66 -0
- sage/libs/gsl/array.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/libs/gsl/array.pxd +5 -0
- sage/libs/gsl/array.pyx +102 -0
- sage/libs/gsl/bessel.pxd +208 -0
- sage/libs/gsl/blas.pxd +116 -0
- sage/libs/gsl/blas_types.pxd +34 -0
- sage/libs/gsl/block.pxd +52 -0
- sage/libs/gsl/chebyshev.pxd +37 -0
- sage/libs/gsl/clausen.pxd +12 -0
- sage/libs/gsl/combination.pxd +47 -0
- sage/libs/gsl/complex.pxd +151 -0
- sage/libs/gsl/coulomb.pxd +30 -0
- sage/libs/gsl/coupling.pxd +21 -0
- sage/libs/gsl/dawson.pxd +12 -0
- sage/libs/gsl/debye.pxd +24 -0
- sage/libs/gsl/dilog.pxd +14 -0
- sage/libs/gsl/eigen.pxd +46 -0
- sage/libs/gsl/elementary.pxd +12 -0
- sage/libs/gsl/ellint.pxd +48 -0
- sage/libs/gsl/elljac.pxd +8 -0
- sage/libs/gsl/erf.pxd +32 -0
- sage/libs/gsl/errno.pxd +26 -0
- sage/libs/gsl/exp.pxd +44 -0
- sage/libs/gsl/expint.pxd +44 -0
- sage/libs/gsl/fermi_dirac.pxd +44 -0
- sage/libs/gsl/fft.pxd +121 -0
- sage/libs/gsl/fit.pxd +50 -0
- sage/libs/gsl/gamma.pxd +94 -0
- sage/libs/gsl/gegenbauer.pxd +26 -0
- sage/libs/gsl/histogram.pxd +176 -0
- sage/libs/gsl/hyperg.pxd +52 -0
- sage/libs/gsl/integration.pxd +69 -0
- sage/libs/gsl/interp.pxd +109 -0
- sage/libs/gsl/laguerre.pxd +24 -0
- sage/libs/gsl/lambert.pxd +16 -0
- sage/libs/gsl/legendre.pxd +90 -0
- sage/libs/gsl/linalg.pxd +185 -0
- sage/libs/gsl/log.pxd +26 -0
- sage/libs/gsl/math.pxd +43 -0
- sage/libs/gsl/matrix.pxd +143 -0
- sage/libs/gsl/matrix_complex.pxd +130 -0
- sage/libs/gsl/min.pxd +67 -0
- sage/libs/gsl/monte.pxd +56 -0
- sage/libs/gsl/ntuple.pxd +32 -0
- sage/libs/gsl/odeiv.pxd +70 -0
- sage/libs/gsl/permutation.pxd +78 -0
- sage/libs/gsl/poly.pxd +40 -0
- sage/libs/gsl/pow_int.pxd +12 -0
- sage/libs/gsl/psi.pxd +28 -0
- sage/libs/gsl/qrng.pxd +29 -0
- sage/libs/gsl/random.pxd +257 -0
- sage/libs/gsl/rng.pxd +100 -0
- sage/libs/gsl/roots.pxd +72 -0
- sage/libs/gsl/sort.pxd +36 -0
- sage/libs/gsl/statistics.pxd +59 -0
- sage/libs/gsl/sum.pxd +55 -0
- sage/libs/gsl/synchrotron.pxd +16 -0
- sage/libs/gsl/transport.pxd +24 -0
- sage/libs/gsl/trig.pxd +58 -0
- sage/libs/gsl/types.pxd +137 -0
- sage/libs/gsl/vector.pxd +101 -0
- sage/libs/gsl/vector_complex.pxd +83 -0
- sage/libs/gsl/wavelet.pxd +49 -0
- sage/libs/gsl/zeta.pxd +28 -0
- sage/libs/mpc/__init__.pxd +114 -0
- sage/libs/mpc/types.pxd +28 -0
- sage/libs/mpfr/__init__.pxd +299 -0
- sage/libs/mpfr/types.pxd +26 -0
- sage/libs/mpmath/__init__.py +1 -0
- sage/libs/mpmath/all.py +27 -0
- sage/libs/mpmath/all__sagemath_modules.py +1 -0
- sage/libs/mpmath/utils.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/libs/mpmath/utils.pxd +4 -0
- sage/libs/mpmath/utils.pyx +319 -0
- sage/matrix/action.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/action.pxd +26 -0
- sage/matrix/action.pyx +596 -0
- sage/matrix/all.py +9 -0
- sage/matrix/args.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/args.pxd +144 -0
- sage/matrix/args.pyx +1668 -0
- sage/matrix/benchmark.py +1258 -0
- sage/matrix/berlekamp_massey.py +95 -0
- sage/matrix/compute_J_ideal.py +926 -0
- sage/matrix/constructor.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/constructor.pyx +750 -0
- sage/matrix/docs.py +430 -0
- sage/matrix/echelon_matrix.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/echelon_matrix.pyx +155 -0
- sage/matrix/matrix.pxd +2 -0
- sage/matrix/matrix0.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix0.pxd +68 -0
- sage/matrix/matrix0.pyx +6324 -0
- sage/matrix/matrix1.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix1.pxd +8 -0
- sage/matrix/matrix1.pyx +2851 -0
- sage/matrix/matrix2.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix2.pxd +25 -0
- sage/matrix/matrix2.pyx +20181 -0
- sage/matrix/matrix_cdv.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_cdv.pxd +4 -0
- sage/matrix/matrix_cdv.pyx +93 -0
- sage/matrix/matrix_complex_double_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_complex_double_dense.pxd +5 -0
- sage/matrix/matrix_complex_double_dense.pyx +98 -0
- sage/matrix/matrix_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_dense.pxd +5 -0
- sage/matrix/matrix_dense.pyx +343 -0
- sage/matrix/matrix_domain_dense.pxd +5 -0
- sage/matrix/matrix_domain_sparse.pxd +5 -0
- sage/matrix/matrix_double_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_double_dense.pxd +7 -0
- sage/matrix/matrix_double_dense.pyx +3906 -0
- sage/matrix/matrix_double_sparse.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_double_sparse.pxd +6 -0
- sage/matrix/matrix_double_sparse.pyx +248 -0
- sage/matrix/matrix_generic_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_generic_dense.pxd +7 -0
- sage/matrix/matrix_generic_dense.pyx +354 -0
- sage/matrix/matrix_generic_sparse.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_generic_sparse.pxd +7 -0
- sage/matrix/matrix_generic_sparse.pyx +461 -0
- sage/matrix/matrix_laurent_mpolynomial_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_laurent_mpolynomial_dense.pxd +5 -0
- sage/matrix/matrix_laurent_mpolynomial_dense.pyx +115 -0
- sage/matrix/matrix_misc.py +313 -0
- sage/matrix/matrix_numpy_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_numpy_dense.pxd +14 -0
- sage/matrix/matrix_numpy_dense.pyx +450 -0
- sage/matrix/matrix_numpy_integer_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_numpy_integer_dense.pxd +7 -0
- sage/matrix/matrix_numpy_integer_dense.pyx +59 -0
- sage/matrix/matrix_polynomial_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_polynomial_dense.pxd +5 -0
- sage/matrix/matrix_polynomial_dense.pyx +5341 -0
- sage/matrix/matrix_real_double_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_real_double_dense.pxd +7 -0
- sage/matrix/matrix_real_double_dense.pyx +122 -0
- sage/matrix/matrix_space.py +2848 -0
- sage/matrix/matrix_sparse.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_sparse.pxd +5 -0
- sage/matrix/matrix_sparse.pyx +1222 -0
- sage/matrix/matrix_window.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/matrix_window.pxd +37 -0
- sage/matrix/matrix_window.pyx +242 -0
- sage/matrix/misc_mpfr.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/misc_mpfr.pyx +80 -0
- sage/matrix/operation_table.py +1182 -0
- sage/matrix/special.py +3666 -0
- sage/matrix/strassen.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matrix/strassen.pyx +851 -0
- sage/matrix/symplectic_basis.py +541 -0
- sage/matrix/template.pxd +6 -0
- sage/matrix/tests.py +71 -0
- sage/matroids/advanced.py +77 -0
- sage/matroids/all.py +13 -0
- sage/matroids/basis_exchange_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/basis_exchange_matroid.pxd +96 -0
- sage/matroids/basis_exchange_matroid.pyx +2344 -0
- sage/matroids/basis_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/basis_matroid.pxd +45 -0
- sage/matroids/basis_matroid.pyx +1217 -0
- sage/matroids/catalog.py +44 -0
- sage/matroids/chow_ring.py +473 -0
- sage/matroids/chow_ring_ideal.py +849 -0
- sage/matroids/circuit_closures_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/circuit_closures_matroid.pxd +16 -0
- sage/matroids/circuit_closures_matroid.pyx +559 -0
- sage/matroids/circuits_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/circuits_matroid.pxd +38 -0
- sage/matroids/circuits_matroid.pyx +947 -0
- sage/matroids/constructor.py +1086 -0
- sage/matroids/database_collections.py +365 -0
- sage/matroids/database_matroids.py +5338 -0
- sage/matroids/dual_matroid.py +583 -0
- sage/matroids/extension.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/extension.pxd +34 -0
- sage/matroids/extension.pyx +519 -0
- sage/matroids/flats_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/flats_matroid.pxd +28 -0
- sage/matroids/flats_matroid.pyx +715 -0
- sage/matroids/gammoid.py +600 -0
- sage/matroids/graphic_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/graphic_matroid.pxd +39 -0
- sage/matroids/graphic_matroid.pyx +2024 -0
- sage/matroids/lean_matrix.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/lean_matrix.pxd +126 -0
- sage/matroids/lean_matrix.pyx +3667 -0
- sage/matroids/linear_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/linear_matroid.pxd +180 -0
- sage/matroids/linear_matroid.pyx +6649 -0
- sage/matroids/matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/matroid.pxd +243 -0
- sage/matroids/matroid.pyx +8759 -0
- sage/matroids/matroids_catalog.py +190 -0
- sage/matroids/matroids_plot_helpers.py +890 -0
- sage/matroids/minor_matroid.py +480 -0
- sage/matroids/minorfix.h +9 -0
- sage/matroids/named_matroids.py +5 -0
- sage/matroids/rank_matroid.py +268 -0
- sage/matroids/set_system.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/set_system.pxd +38 -0
- sage/matroids/set_system.pyx +800 -0
- sage/matroids/transversal_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/transversal_matroid.pxd +14 -0
- sage/matroids/transversal_matroid.pyx +893 -0
- sage/matroids/union_matroid.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/union_matroid.pxd +20 -0
- sage/matroids/union_matroid.pyx +331 -0
- sage/matroids/unpickling.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/matroids/unpickling.pyx +843 -0
- sage/matroids/utilities.py +809 -0
- sage/misc/all__sagemath_modules.py +20 -0
- sage/misc/c3.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/misc/c3.pyx +238 -0
- sage/misc/compat.py +87 -0
- sage/misc/element_with_label.py +173 -0
- sage/misc/func_persist.py +79 -0
- sage/misc/pickle_old.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/misc/pickle_old.pyx +19 -0
- sage/misc/proof.py +7 -0
- sage/misc/replace_dot_all.py +472 -0
- sage/misc/sagedoc_conf.py +168 -0
- sage/misc/sphinxify.py +167 -0
- sage/misc/test_class_pickling.py +85 -0
- sage/modules/all.py +42 -0
- sage/modules/complex_double_vector.py +25 -0
- sage/modules/diamond_cutting.py +380 -0
- sage/modules/fg_pid/all.py +1 -0
- sage/modules/fg_pid/fgp_element.py +456 -0
- sage/modules/fg_pid/fgp_module.py +2091 -0
- sage/modules/fg_pid/fgp_morphism.py +550 -0
- sage/modules/filtered_vector_space.py +1271 -0
- sage/modules/finite_submodule_iter.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/finite_submodule_iter.pxd +27 -0
- sage/modules/finite_submodule_iter.pyx +452 -0
- sage/modules/fp_graded/all.py +1 -0
- sage/modules/fp_graded/element.py +346 -0
- sage/modules/fp_graded/free_element.py +298 -0
- sage/modules/fp_graded/free_homspace.py +53 -0
- sage/modules/fp_graded/free_module.py +1060 -0
- sage/modules/fp_graded/free_morphism.py +217 -0
- sage/modules/fp_graded/homspace.py +563 -0
- sage/modules/fp_graded/module.py +1340 -0
- sage/modules/fp_graded/morphism.py +1990 -0
- sage/modules/fp_graded/steenrod/all.py +1 -0
- sage/modules/fp_graded/steenrod/homspace.py +65 -0
- sage/modules/fp_graded/steenrod/module.py +477 -0
- sage/modules/fp_graded/steenrod/morphism.py +404 -0
- sage/modules/fp_graded/steenrod/profile.py +241 -0
- sage/modules/free_module.py +8447 -0
- sage/modules/free_module_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/free_module_element.pxd +22 -0
- sage/modules/free_module_element.pyx +5445 -0
- sage/modules/free_module_homspace.py +369 -0
- sage/modules/free_module_integer.py +896 -0
- sage/modules/free_module_morphism.py +823 -0
- sage/modules/free_module_pseudohomspace.py +352 -0
- sage/modules/free_module_pseudomorphism.py +578 -0
- sage/modules/free_quadratic_module.py +1706 -0
- sage/modules/free_quadratic_module_integer_symmetric.py +1790 -0
- sage/modules/matrix_morphism.py +1745 -0
- sage/modules/misc.py +103 -0
- sage/modules/module_functors.py +192 -0
- sage/modules/multi_filtered_vector_space.py +719 -0
- sage/modules/ore_module.py +2208 -0
- sage/modules/ore_module_element.py +178 -0
- sage/modules/ore_module_homspace.py +147 -0
- sage/modules/ore_module_morphism.py +968 -0
- sage/modules/quotient_module.py +699 -0
- sage/modules/real_double_vector.py +22 -0
- sage/modules/submodule.py +255 -0
- sage/modules/tensor_operations.py +567 -0
- sage/modules/torsion_quadratic_module.py +1352 -0
- sage/modules/tutorial_free_modules.py +248 -0
- sage/modules/vector_complex_double_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_complex_double_dense.pxd +6 -0
- sage/modules/vector_complex_double_dense.pyx +117 -0
- sage/modules/vector_double_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_double_dense.pxd +6 -0
- sage/modules/vector_double_dense.pyx +604 -0
- sage/modules/vector_integer_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_integer_dense.pxd +15 -0
- sage/modules/vector_integer_dense.pyx +361 -0
- sage/modules/vector_integer_sparse.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_integer_sparse.pxd +29 -0
- sage/modules/vector_integer_sparse.pyx +406 -0
- sage/modules/vector_modn_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_modn_dense.pxd +12 -0
- sage/modules/vector_modn_dense.pyx +394 -0
- sage/modules/vector_modn_sparse.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_modn_sparse.pxd +21 -0
- sage/modules/vector_modn_sparse.pyx +298 -0
- sage/modules/vector_numpy_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_numpy_dense.pxd +15 -0
- sage/modules/vector_numpy_dense.pyx +304 -0
- sage/modules/vector_numpy_integer_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_numpy_integer_dense.pxd +7 -0
- sage/modules/vector_numpy_integer_dense.pyx +54 -0
- sage/modules/vector_rational_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_rational_dense.pxd +15 -0
- sage/modules/vector_rational_dense.pyx +387 -0
- sage/modules/vector_rational_sparse.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_rational_sparse.pxd +30 -0
- sage/modules/vector_rational_sparse.pyx +413 -0
- sage/modules/vector_real_double_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/vector_real_double_dense.pxd +6 -0
- sage/modules/vector_real_double_dense.pyx +126 -0
- sage/modules/vector_space_homspace.py +430 -0
- sage/modules/vector_space_morphism.py +989 -0
- sage/modules/with_basis/all.py +15 -0
- sage/modules/with_basis/cell_module.py +494 -0
- sage/modules/with_basis/indexed_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/modules/with_basis/indexed_element.pxd +13 -0
- sage/modules/with_basis/indexed_element.pyx +1058 -0
- sage/modules/with_basis/invariant.py +1075 -0
- sage/modules/with_basis/morphism.py +1636 -0
- sage/modules/with_basis/representation.py +2939 -0
- sage/modules/with_basis/subquotient.py +685 -0
- sage/numerical/all__sagemath_modules.py +6 -0
- sage/numerical/gauss_legendre.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/gauss_legendre.pyx +381 -0
- sage/numerical/optimize.py +910 -0
- sage/probability/all.py +10 -0
- sage/probability/probability_distribution.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/probability/probability_distribution.pyx +1242 -0
- sage/probability/random_variable.py +411 -0
- sage/quadratic_forms/all.py +4 -0
- sage/quadratic_forms/all__sagemath_modules.py +15 -0
- sage/quadratic_forms/binary_qf.py +2042 -0
- sage/quadratic_forms/bqf_class_group.py +748 -0
- sage/quadratic_forms/constructions.py +93 -0
- sage/quadratic_forms/count_local_2.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/quadratic_forms/count_local_2.pyx +365 -0
- sage/quadratic_forms/extras.py +195 -0
- sage/quadratic_forms/quadratic_form.py +1753 -0
- sage/quadratic_forms/quadratic_form__count_local_2.py +221 -0
- sage/quadratic_forms/quadratic_form__equivalence_testing.py +708 -0
- sage/quadratic_forms/quadratic_form__evaluate.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/quadratic_forms/quadratic_form__evaluate.pyx +139 -0
- sage/quadratic_forms/quadratic_form__local_density_congruence.py +977 -0
- sage/quadratic_forms/quadratic_form__local_field_invariants.py +1072 -0
- sage/quadratic_forms/quadratic_form__neighbors.py +424 -0
- sage/quadratic_forms/quadratic_form__reduction_theory.py +488 -0
- sage/quadratic_forms/quadratic_form__split_local_covering.py +416 -0
- sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +657 -0
- sage/quadratic_forms/quadratic_form__theta.py +352 -0
- sage/quadratic_forms/quadratic_form__variable_substitutions.py +370 -0
- sage/quadratic_forms/random_quadraticform.py +209 -0
- sage/quadratic_forms/ternary.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/quadratic_forms/ternary.pyx +1154 -0
- sage/quadratic_forms/ternary_qf.py +2027 -0
- sage/rings/all__sagemath_modules.py +28 -0
- sage/rings/asymptotic/all__sagemath_modules.py +1 -0
- sage/rings/asymptotic/misc.py +1252 -0
- sage/rings/cc.py +4 -0
- sage/rings/cfinite_sequence.py +1306 -0
- sage/rings/complex_conversion.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/complex_conversion.pxd +8 -0
- sage/rings/complex_conversion.pyx +23 -0
- sage/rings/complex_double.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/complex_double.pxd +21 -0
- sage/rings/complex_double.pyx +2654 -0
- sage/rings/complex_mpc.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/complex_mpc.pxd +21 -0
- sage/rings/complex_mpc.pyx +2576 -0
- sage/rings/complex_mpfr.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/complex_mpfr.pxd +18 -0
- sage/rings/complex_mpfr.pyx +3602 -0
- sage/rings/derivation.py +2334 -0
- sage/rings/finite_rings/all__sagemath_modules.py +1 -0
- sage/rings/finite_rings/maps_finite_field.py +191 -0
- sage/rings/function_field/all__sagemath_modules.py +8 -0
- sage/rings/function_field/derivations.py +102 -0
- sage/rings/function_field/derivations_rational.py +132 -0
- sage/rings/function_field/differential.py +853 -0
- sage/rings/function_field/divisor.py +1107 -0
- sage/rings/function_field/drinfeld_modules/action.py +199 -0
- sage/rings/function_field/drinfeld_modules/all.py +1 -0
- sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +673 -0
- sage/rings/function_field/drinfeld_modules/drinfeld_module.py +2087 -0
- sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +1131 -0
- sage/rings/function_field/drinfeld_modules/homset.py +420 -0
- sage/rings/function_field/drinfeld_modules/morphism.py +820 -0
- sage/rings/function_field/hermite_form_polynomial.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/function_field/hermite_form_polynomial.pyx +188 -0
- sage/rings/function_field/khuri_makdisi.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/function_field/khuri_makdisi.pyx +935 -0
- sage/rings/invariants/all.py +4 -0
- sage/rings/invariants/invariant_theory.py +4597 -0
- sage/rings/invariants/reconstruction.py +395 -0
- sage/rings/polynomial/all__sagemath_modules.py +17 -0
- sage/rings/polynomial/integer_valued_polynomials.py +1230 -0
- sage/rings/polynomial/laurent_polynomial_mpair.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/polynomial/laurent_polynomial_mpair.pxd +15 -0
- sage/rings/polynomial/laurent_polynomial_mpair.pyx +2023 -0
- sage/rings/polynomial/ore_function_element.py +952 -0
- sage/rings/polynomial/ore_function_field.py +1028 -0
- sage/rings/polynomial/ore_polynomial_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/polynomial/ore_polynomial_element.pxd +48 -0
- sage/rings/polynomial/ore_polynomial_element.pyx +3145 -0
- sage/rings/polynomial/ore_polynomial_ring.py +1334 -0
- sage/rings/polynomial/polynomial_real_mpfr_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +788 -0
- sage/rings/polynomial/q_integer_valued_polynomials.py +1264 -0
- sage/rings/polynomial/skew_polynomial_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/polynomial/skew_polynomial_element.pxd +9 -0
- sage/rings/polynomial/skew_polynomial_element.pyx +684 -0
- sage/rings/polynomial/skew_polynomial_finite_field.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/polynomial/skew_polynomial_finite_field.pxd +19 -0
- sage/rings/polynomial/skew_polynomial_finite_field.pyx +1093 -0
- sage/rings/polynomial/skew_polynomial_finite_order.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/polynomial/skew_polynomial_finite_order.pxd +10 -0
- sage/rings/polynomial/skew_polynomial_finite_order.pyx +567 -0
- sage/rings/polynomial/skew_polynomial_ring.py +908 -0
- sage/rings/real_double_element_gsl.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/real_double_element_gsl.pxd +8 -0
- sage/rings/real_double_element_gsl.pyx +794 -0
- sage/rings/real_field.py +58 -0
- sage/rings/real_mpfr.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/real_mpfr.pxd +29 -0
- sage/rings/real_mpfr.pyx +6122 -0
- sage/rings/ring_extension.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/ring_extension.pxd +42 -0
- sage/rings/ring_extension.pyx +2779 -0
- sage/rings/ring_extension_conversion.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/ring_extension_conversion.pxd +16 -0
- sage/rings/ring_extension_conversion.pyx +462 -0
- sage/rings/ring_extension_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/ring_extension_element.pxd +21 -0
- sage/rings/ring_extension_element.pyx +1635 -0
- sage/rings/ring_extension_homset.py +64 -0
- sage/rings/ring_extension_morphism.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/rings/ring_extension_morphism.pxd +35 -0
- sage/rings/ring_extension_morphism.pyx +920 -0
- sage/schemes/all__sagemath_modules.py +1 -0
- sage/schemes/projective/all__sagemath_modules.py +1 -0
- sage/schemes/projective/coherent_sheaf.py +300 -0
- sage/schemes/projective/cohomology.py +510 -0
- sage/stats/all.py +15 -0
- sage/stats/basic_stats.py +489 -0
- sage/stats/distributions/all.py +7 -0
- sage/stats/distributions/catalog.py +34 -0
- sage/stats/distributions/dgs.h +50 -0
- sage/stats/distributions/dgs.pxd +111 -0
- sage/stats/distributions/dgs_bern.h +400 -0
- sage/stats/distributions/dgs_gauss.h +614 -0
- sage/stats/distributions/dgs_misc.h +104 -0
- sage/stats/distributions/discrete_gaussian_integer.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/stats/distributions/discrete_gaussian_integer.pxd +14 -0
- sage/stats/distributions/discrete_gaussian_integer.pyx +498 -0
- sage/stats/distributions/discrete_gaussian_lattice.py +908 -0
- sage/stats/distributions/discrete_gaussian_polynomial.py +141 -0
- sage/stats/hmm/all.py +15 -0
- sage/stats/hmm/chmm.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/stats/hmm/chmm.pyx +1595 -0
- sage/stats/hmm/distributions.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/stats/hmm/distributions.pxd +29 -0
- sage/stats/hmm/distributions.pyx +531 -0
- sage/stats/hmm/hmm.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/stats/hmm/hmm.pxd +17 -0
- sage/stats/hmm/hmm.pyx +1388 -0
- sage/stats/hmm/util.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/stats/hmm/util.pxd +7 -0
- sage/stats/hmm/util.pyx +165 -0
- sage/stats/intlist.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/stats/intlist.pxd +14 -0
- sage/stats/intlist.pyx +588 -0
- sage/stats/r.py +49 -0
- sage/stats/time_series.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/stats/time_series.pxd +6 -0
- sage/stats/time_series.pyx +2546 -0
- sage/tensor/all.py +2 -0
- sage/tensor/modules/all.py +8 -0
- sage/tensor/modules/alternating_contr_tensor.py +761 -0
- sage/tensor/modules/comp.py +5598 -0
- sage/tensor/modules/ext_pow_free_module.py +824 -0
- sage/tensor/modules/finite_rank_free_module.py +3589 -0
- sage/tensor/modules/format_utilities.py +333 -0
- sage/tensor/modules/free_module_alt_form.py +858 -0
- sage/tensor/modules/free_module_automorphism.py +1207 -0
- sage/tensor/modules/free_module_basis.py +1074 -0
- sage/tensor/modules/free_module_element.py +284 -0
- sage/tensor/modules/free_module_homset.py +652 -0
- sage/tensor/modules/free_module_linear_group.py +564 -0
- sage/tensor/modules/free_module_morphism.py +1581 -0
- sage/tensor/modules/free_module_tensor.py +3289 -0
- sage/tensor/modules/reflexive_module.py +386 -0
- sage/tensor/modules/tensor_free_module.py +780 -0
- sage/tensor/modules/tensor_free_submodule.py +538 -0
- sage/tensor/modules/tensor_free_submodule_basis.py +140 -0
- sage/tensor/modules/tensor_with_indices.py +1043 -0
|
@@ -0,0 +1,2148 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-modules
|
|
2
|
+
r"""
|
|
3
|
+
Chain complexes
|
|
4
|
+
|
|
5
|
+
This module implements bounded chain complexes of free `R`-modules,
|
|
6
|
+
for any commutative ring `R` (although the interesting things, like
|
|
7
|
+
homology, only work if `R` is the integers or a field).
|
|
8
|
+
|
|
9
|
+
Fix a ring `R`. A chain complex over `R` is a collection of
|
|
10
|
+
`R`-modules `\{C_n\}` indexed by the integers, with `R`-module maps
|
|
11
|
+
`d_n : C_n \rightarrow C_{n+1}` such that `d_{n+1} \circ d_n = 0` for
|
|
12
|
+
all `n`. The maps `d_n` are called *differentials*.
|
|
13
|
+
|
|
14
|
+
One can vary this somewhat: the differentials may decrease degree by
|
|
15
|
+
one instead of increasing it: sometimes a chain complex is defined
|
|
16
|
+
with `d_n : C_n \rightarrow C_{n-1}` for each `n`. Indeed, the
|
|
17
|
+
differentials may change dimension by any fixed integer.
|
|
18
|
+
|
|
19
|
+
Also, the modules may be indexed over an abelian group other than the
|
|
20
|
+
integers, e.g., `\ZZ^{m}` for some integer `m \geq 1`, in which case
|
|
21
|
+
the differentials may change the grading by any element of that
|
|
22
|
+
grading group. The elements of the grading group are generally called
|
|
23
|
+
degrees, so `C_n` is the module in degree `n` and so on.
|
|
24
|
+
|
|
25
|
+
In this implementation, the ring `R` must be commutative and the
|
|
26
|
+
modules `C_n` must be free `R`-modules. As noted above, homology
|
|
27
|
+
calculations will only work if the ring `R` is either `\ZZ` or a
|
|
28
|
+
field. The modules may be indexed by any free abelian group. The
|
|
29
|
+
differentials may increase degree by 1 or decrease it, or indeed
|
|
30
|
+
change it by any fixed amount: this is controlled by the
|
|
31
|
+
``degree_of_differential`` parameter used in defining the chain
|
|
32
|
+
complex.
|
|
33
|
+
|
|
34
|
+
AUTHORS:
|
|
35
|
+
|
|
36
|
+
- John H. Palmieri (2009-04): initial implementation
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# ****************************************************************************
|
|
40
|
+
# Copyright (C) 2013 John H. Palmieri <palmieri@math.washington.edu>
|
|
41
|
+
# Volker Braun <vbraun.name@gmail.com>
|
|
42
|
+
#
|
|
43
|
+
# This program is free software: you can redistribute it and/or modify
|
|
44
|
+
# it under the terms of the GNU General Public License as published by
|
|
45
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
46
|
+
# (at your option) any later version.
|
|
47
|
+
# https://www.gnu.org/licenses/
|
|
48
|
+
# ****************************************************************************
|
|
49
|
+
|
|
50
|
+
from copy import copy
|
|
51
|
+
from functools import reduce
|
|
52
|
+
|
|
53
|
+
from sage.structure.parent import Parent
|
|
54
|
+
from sage.structure.element import ModuleElement, Vector, coercion_model
|
|
55
|
+
from sage.misc.cachefunc import cached_method
|
|
56
|
+
|
|
57
|
+
from sage.rings.integer_ring import ZZ
|
|
58
|
+
from sage.modules.free_module import FreeModule
|
|
59
|
+
from sage.modules.free_module_element import vector
|
|
60
|
+
from sage.matrix.matrix0 import Matrix
|
|
61
|
+
from sage.matrix.constructor import matrix
|
|
62
|
+
from sage.misc.latex import latex
|
|
63
|
+
from sage.rings.fast_arith import prime_range
|
|
64
|
+
from sage.homology.homology_group import HomologyGroup
|
|
65
|
+
from sage.misc.persist import register_unpickle_override
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _latex_module(R, m):
|
|
69
|
+
"""
|
|
70
|
+
LaTeX string representing a free module over ``R`` of rank ``m``.
|
|
71
|
+
|
|
72
|
+
INPUT:
|
|
73
|
+
|
|
74
|
+
- ``R`` -- a commutative ring
|
|
75
|
+
- ``m`` -- nonnegative integer
|
|
76
|
+
|
|
77
|
+
This is used by the ``_latex_`` method for chain complexes.
|
|
78
|
+
|
|
79
|
+
EXAMPLES::
|
|
80
|
+
|
|
81
|
+
sage: from sage.homology.chain_complex import _latex_module
|
|
82
|
+
sage: _latex_module(ZZ, 3)
|
|
83
|
+
'\\Bold{Z}^{3}'
|
|
84
|
+
sage: _latex_module(ZZ, 0)
|
|
85
|
+
'0'
|
|
86
|
+
sage: _latex_module(GF(3), 1)
|
|
87
|
+
'\\Bold{F}_{3}^{1}'
|
|
88
|
+
"""
|
|
89
|
+
if m == 0:
|
|
90
|
+
return str(latex(0))
|
|
91
|
+
return str(latex(FreeModule(R, m)))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def ChainComplex(data=None, base_ring=None, grading_group=None,
|
|
95
|
+
degree_of_differential=1, degree=1,
|
|
96
|
+
check=True):
|
|
97
|
+
r"""
|
|
98
|
+
Define a chain complex.
|
|
99
|
+
|
|
100
|
+
INPUT:
|
|
101
|
+
|
|
102
|
+
- ``data`` -- the data defining the chain complex; see below for
|
|
103
|
+
more details
|
|
104
|
+
|
|
105
|
+
The following keyword arguments are supported:
|
|
106
|
+
|
|
107
|
+
- ``base_ring`` -- a commutative ring (optional); the ring over
|
|
108
|
+
which the chain complex is defined. If this is not specified,
|
|
109
|
+
it is determined by the data defining the chain complex.
|
|
110
|
+
|
|
111
|
+
- ``grading_group`` -- a additive free abelian group (optional,
|
|
112
|
+
default ``ZZ``); the group over which the chain complex is
|
|
113
|
+
indexed
|
|
114
|
+
|
|
115
|
+
- ``degree_of_differential`` -- element of grading_group
|
|
116
|
+
(default: ``1``); the degree of the differential
|
|
117
|
+
|
|
118
|
+
- ``degree`` -- alias for ``degree_of_differential``
|
|
119
|
+
|
|
120
|
+
- ``check`` -- boolean (default: ``True``); if ``True``,
|
|
121
|
+
check that each consecutive pair of differentials are
|
|
122
|
+
composable and have composite equal to zero
|
|
123
|
+
|
|
124
|
+
OUTPUT: a chain complex
|
|
125
|
+
|
|
126
|
+
.. WARNING::
|
|
127
|
+
|
|
128
|
+
Right now, homology calculations will only work if the base
|
|
129
|
+
ring is either `\ZZ` or a field, so please take this into account
|
|
130
|
+
when defining a chain complex.
|
|
131
|
+
|
|
132
|
+
Use data to define the chain complex. This may be in any of the
|
|
133
|
+
following forms.
|
|
134
|
+
|
|
135
|
+
1. a dictionary with integers (or more generally, elements of
|
|
136
|
+
grading_group) for keys, and with ``data[n]`` a matrix representing
|
|
137
|
+
(via left multiplication) the differential coming from degree
|
|
138
|
+
`n`. (Note that the shape of the matrix then determines the
|
|
139
|
+
rank of the free modules `C_n` and `C_{n+d}`.)
|
|
140
|
+
|
|
141
|
+
2. a list/tuple/iterable of the form `[C_0, d_0, C_1, d_1, C_2,
|
|
142
|
+
d_2, ...]`, where each `C_i` is a free module and each `d_i` is
|
|
143
|
+
a matrix, as above. This only makes sense if ``grading_group``
|
|
144
|
+
is `\ZZ` and ``degree`` is 1.
|
|
145
|
+
|
|
146
|
+
3. a list/tuple/iterable of the form `[r_0, d_0, r_1, d_1, r_2,
|
|
147
|
+
d_2, \ldots]`, where `r_i` is the rank of the free module `C_i`
|
|
148
|
+
and each `d_i` is a matrix, as above. This only makes sense if
|
|
149
|
+
``grading_group`` is `\ZZ` and ``degree`` is 1.
|
|
150
|
+
|
|
151
|
+
4. a list/tuple/iterable of the form `[d_0, d_1, d_2, \ldots]` where
|
|
152
|
+
each `d_i` is a matrix, as above. This only makes sense if
|
|
153
|
+
``grading_group`` is `\ZZ` and ``degree`` is 1.
|
|
154
|
+
|
|
155
|
+
.. NOTE::
|
|
156
|
+
|
|
157
|
+
In fact, the free modules `C_i` in case 2 and the ranks `r_i`
|
|
158
|
+
in case 3 are ignored: only the matrices are kept, and from
|
|
159
|
+
their shapes, the ranks of the modules are determined.
|
|
160
|
+
(Indeed, if ``data`` is a list or tuple, then any element which
|
|
161
|
+
is not a matrix is discarded; thus the list may have any number
|
|
162
|
+
of different things in it, and all of the non-matrices will be
|
|
163
|
+
ignored.) No error checking is done to make sure, for
|
|
164
|
+
instance, that the given modules have the appropriate ranks for
|
|
165
|
+
the given matrices. However, as long as ``check`` is True, the
|
|
166
|
+
code checks to see if the matrices are composable and that each
|
|
167
|
+
appropriate composite is zero.
|
|
168
|
+
|
|
169
|
+
If the base ring is not specified, then the matrices are examined
|
|
170
|
+
to determine a ring over which they are all naturally defined, and
|
|
171
|
+
this becomes the base ring for the complex. If no such ring can
|
|
172
|
+
be found, an error is raised. If the base ring is specified, then
|
|
173
|
+
the matrices are converted automatically to this ring when
|
|
174
|
+
defining the chain complex. If some matrix cannot be converted,
|
|
175
|
+
then an error is raised.
|
|
176
|
+
|
|
177
|
+
EXAMPLES::
|
|
178
|
+
|
|
179
|
+
sage: ChainComplex()
|
|
180
|
+
Trivial chain complex over Integer Ring
|
|
181
|
+
|
|
182
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
183
|
+
sage: C
|
|
184
|
+
Chain complex with at most 2 nonzero terms over Integer Ring
|
|
185
|
+
|
|
186
|
+
sage: m = matrix(ZZ, 2, 2, [0, 1, 0, 0])
|
|
187
|
+
sage: D = ChainComplex([m, m], base_ring=GF(2)); D
|
|
188
|
+
Chain complex with at most 3 nonzero terms over Finite Field of size 2
|
|
189
|
+
sage: D == loads(dumps(D))
|
|
190
|
+
True
|
|
191
|
+
sage: D.differential(0)==m, m.is_immutable(), D.differential(0).is_immutable()
|
|
192
|
+
(True, False, True)
|
|
193
|
+
|
|
194
|
+
Note that when a chain complex is defined in Sage, new
|
|
195
|
+
differentials may be created: every nonzero module in the chain
|
|
196
|
+
complex must have a differential coming from it, even if that
|
|
197
|
+
differential is zero::
|
|
198
|
+
|
|
199
|
+
sage: IZ = ChainComplex({0: identity_matrix(ZZ, 1)})
|
|
200
|
+
sage: diff = IZ.differential() # the differentials in the chain complex
|
|
201
|
+
sage: diff[-1], diff[0], diff[1]
|
|
202
|
+
([], [1], [])
|
|
203
|
+
sage: IZ.differential(1).parent()
|
|
204
|
+
Full MatrixSpace of 0 by 1 dense matrices over Integer Ring
|
|
205
|
+
sage: mat = ChainComplex({0: matrix(ZZ, 3, 4)}).differential(1)
|
|
206
|
+
sage: mat.nrows(), mat.ncols()
|
|
207
|
+
(0, 3)
|
|
208
|
+
|
|
209
|
+
Defining the base ring implicitly::
|
|
210
|
+
|
|
211
|
+
sage: ChainComplex([matrix(QQ, 3, 1), matrix(ZZ, 4, 3)])
|
|
212
|
+
Chain complex with at most 3 nonzero terms over Rational Field
|
|
213
|
+
sage: ChainComplex([matrix(GF(125, 'a'), 3, 1), matrix(ZZ, 4, 3)]) # needs sage.rings.finite_rings
|
|
214
|
+
Chain complex with at most 3 nonzero terms over Finite Field in a of size 5^3
|
|
215
|
+
|
|
216
|
+
If the matrices are defined over incompatible rings, an error results::
|
|
217
|
+
|
|
218
|
+
sage: ChainComplex([matrix(GF(125, 'a'), 3, 1), matrix(QQ, 4, 3)]) # needs sage.rings.finite_rings
|
|
219
|
+
Traceback (most recent call last):
|
|
220
|
+
...
|
|
221
|
+
TypeError: no common canonical parent for objects with parents:
|
|
222
|
+
'Finite Field in a of size 5^3' and 'Rational Field'
|
|
223
|
+
|
|
224
|
+
If the base ring is given explicitly but is not compatible with
|
|
225
|
+
the matrices, an error results::
|
|
226
|
+
|
|
227
|
+
sage: ChainComplex([matrix(GF(125, 'a'), 3, 1)], base_ring=QQ) # needs sage.rings.finite_rings
|
|
228
|
+
Traceback (most recent call last):
|
|
229
|
+
...
|
|
230
|
+
TypeError: unable to convert 0 to a rational
|
|
231
|
+
"""
|
|
232
|
+
if grading_group is None:
|
|
233
|
+
grading_group = ZZ
|
|
234
|
+
if degree_of_differential != 1 and degree != 1:
|
|
235
|
+
raise ValueError('specify only one of degree_of_differential or degree, not both')
|
|
236
|
+
if degree_of_differential != 1:
|
|
237
|
+
degree = degree_of_differential
|
|
238
|
+
try:
|
|
239
|
+
degree = grading_group(degree)
|
|
240
|
+
except Exception:
|
|
241
|
+
raise ValueError('degree is not an element of the grading group')
|
|
242
|
+
|
|
243
|
+
# transform data into data_dict
|
|
244
|
+
if data is None or (isinstance(data, (list, tuple)) and len(data) == 0):
|
|
245
|
+
data_dict = {}
|
|
246
|
+
elif isinstance(data, dict): # data is dictionary
|
|
247
|
+
data_dict = data
|
|
248
|
+
else: # data is list/tuple/iterable
|
|
249
|
+
data_matrices = [x for x in data if isinstance(x, Matrix)]
|
|
250
|
+
if degree != 1:
|
|
251
|
+
raise ValueError('degree must be +1 if the data argument is a list or tuple')
|
|
252
|
+
if grading_group != ZZ:
|
|
253
|
+
raise ValueError('grading_group must be ZZ if the data argument is a list or tuple')
|
|
254
|
+
data_dict = {grading_group(i): m for i, m in enumerate(data_matrices)}
|
|
255
|
+
|
|
256
|
+
if base_ring is None:
|
|
257
|
+
if not data_dict:
|
|
258
|
+
base_ring = ZZ
|
|
259
|
+
else:
|
|
260
|
+
bases = tuple(x.base_ring() for x in data_dict.values())
|
|
261
|
+
base_ring = coercion_model.common_parent(*bases)
|
|
262
|
+
|
|
263
|
+
# make sure values in data_dict are appropriate matrices
|
|
264
|
+
for n in list(data_dict):
|
|
265
|
+
if n not in grading_group:
|
|
266
|
+
raise ValueError('one of the dictionary keys is not an element of the grading group')
|
|
267
|
+
mat = data_dict[n]
|
|
268
|
+
if not isinstance(mat, Matrix):
|
|
269
|
+
raise TypeError('one of the differentials in the data is not a matrix')
|
|
270
|
+
if mat.base_ring() is base_ring:
|
|
271
|
+
if not mat.is_immutable():
|
|
272
|
+
mat = copy(mat) # do not make any arguments passed immutable
|
|
273
|
+
mat.set_immutable()
|
|
274
|
+
else:
|
|
275
|
+
mat = mat.change_ring(base_ring)
|
|
276
|
+
mat.set_immutable()
|
|
277
|
+
data_dict[n] = mat
|
|
278
|
+
|
|
279
|
+
# include any "obvious" zero matrices that are not 0x0
|
|
280
|
+
for n in list(data_dict): # note: data_dict will be mutated in this loop
|
|
281
|
+
mat1 = data_dict[n]
|
|
282
|
+
if (mat1.nrows(), mat1.ncols()) == (0, 0):
|
|
283
|
+
del data_dict[n]
|
|
284
|
+
if (mat1.nrows() != 0) and (n+degree not in data_dict):
|
|
285
|
+
if n+2*degree in data_dict:
|
|
286
|
+
mat2 = matrix(base_ring, data_dict[n+2*degree].ncols(), mat1.nrows())
|
|
287
|
+
else:
|
|
288
|
+
mat2 = matrix(base_ring, 0, mat1.nrows())
|
|
289
|
+
mat2.set_immutable()
|
|
290
|
+
data_dict[n+degree] = mat2
|
|
291
|
+
if (mat1.ncols() != 0) and (n-degree not in data_dict):
|
|
292
|
+
if n-2*degree in data_dict:
|
|
293
|
+
mat0 = matrix(base_ring, mat1.ncols(), data_dict[n-2*degree].nrows())
|
|
294
|
+
else:
|
|
295
|
+
mat0 = matrix(base_ring, mat1.ncols(), 0)
|
|
296
|
+
mat0.set_immutable()
|
|
297
|
+
data_dict[n-degree] = mat0
|
|
298
|
+
|
|
299
|
+
# check that this is a complex: going twice is zero
|
|
300
|
+
if check:
|
|
301
|
+
for n in data_dict:
|
|
302
|
+
mat0 = data_dict[n]
|
|
303
|
+
try:
|
|
304
|
+
mat1 = data_dict[n+degree]
|
|
305
|
+
except KeyError:
|
|
306
|
+
continue
|
|
307
|
+
try:
|
|
308
|
+
prod = mat1 * mat0
|
|
309
|
+
except TypeError:
|
|
310
|
+
raise TypeError('the differentials d_{{{}}} and d_{{{}}} are not compatible: '
|
|
311
|
+
'their product is not defined'.format(n, n+degree))
|
|
312
|
+
if not prod.is_zero():
|
|
313
|
+
raise ValueError('the differentials d_{{{}}} and d_{{{}}} are not compatible: '
|
|
314
|
+
'their composition is not zero.'.format(n, n+degree))
|
|
315
|
+
|
|
316
|
+
return ChainComplex_class(grading_group, degree, base_ring, data_dict)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class Chain_class(ModuleElement):
|
|
320
|
+
|
|
321
|
+
def __init__(self, parent, vectors, check=True):
|
|
322
|
+
r"""
|
|
323
|
+
A Chain in a Chain Complex.
|
|
324
|
+
|
|
325
|
+
A chain is collection of module elements for each module `C_n`
|
|
326
|
+
of the chain complex `(C_n, d_n)`. There is no restriction on
|
|
327
|
+
how the differentials `d_n` act on the elements of the chain.
|
|
328
|
+
|
|
329
|
+
.. NOTE::
|
|
330
|
+
|
|
331
|
+
You must use the chain complex to construct chains.
|
|
332
|
+
|
|
333
|
+
EXAMPLES::
|
|
334
|
+
|
|
335
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])},
|
|
336
|
+
....: base_ring=GF(7))
|
|
337
|
+
sage: C.category()
|
|
338
|
+
Category of chain complexes over Finite Field of size 7
|
|
339
|
+
|
|
340
|
+
TESTS::
|
|
341
|
+
|
|
342
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
343
|
+
sage: c = C({0: vector([0, 1, 2]), 1: vector([3, 4])})
|
|
344
|
+
sage: TestSuite(c).run()
|
|
345
|
+
"""
|
|
346
|
+
# only nonzero vectors shall be stored, ensuring this is the
|
|
347
|
+
# job of the _element constructor_
|
|
348
|
+
assert all(v.is_immutable() and not v.is_zero()
|
|
349
|
+
and v.base_ring() is parent.base_ring()
|
|
350
|
+
for v in vectors.values())
|
|
351
|
+
self._vec = vectors
|
|
352
|
+
super().__init__(parent)
|
|
353
|
+
|
|
354
|
+
def vector(self, degree):
|
|
355
|
+
"""
|
|
356
|
+
Return the free module element in ``degree``.
|
|
357
|
+
|
|
358
|
+
EXAMPLES::
|
|
359
|
+
|
|
360
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
361
|
+
sage: c = C({0: vector([1, 2, 3]), 1: vector([4, 5])})
|
|
362
|
+
sage: c.vector(0)
|
|
363
|
+
(1, 2, 3)
|
|
364
|
+
sage: c.vector(1)
|
|
365
|
+
(4, 5)
|
|
366
|
+
sage: c.vector(2)
|
|
367
|
+
()
|
|
368
|
+
"""
|
|
369
|
+
try:
|
|
370
|
+
return self._vec[degree]
|
|
371
|
+
except KeyError:
|
|
372
|
+
return self.parent().free_module(degree).zero()
|
|
373
|
+
|
|
374
|
+
def _repr_(self):
|
|
375
|
+
"""
|
|
376
|
+
Print representation.
|
|
377
|
+
|
|
378
|
+
EXAMPLES::
|
|
379
|
+
|
|
380
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
381
|
+
sage: C()
|
|
382
|
+
Trivial chain
|
|
383
|
+
sage: C({0: vector([1, 2, 3])})
|
|
384
|
+
Chain(0:(1, 2, 3))
|
|
385
|
+
sage: c = C({0: vector([1, 2, 3]), 1: vector([4, 5])}); c
|
|
386
|
+
Chain with 2 nonzero terms over Integer Ring
|
|
387
|
+
sage: c._repr_()
|
|
388
|
+
'Chain with 2 nonzero terms over Integer Ring'
|
|
389
|
+
"""
|
|
390
|
+
n = len(self._vec)
|
|
391
|
+
if n == 0:
|
|
392
|
+
return 'Trivial chain'
|
|
393
|
+
|
|
394
|
+
if n == 1:
|
|
395
|
+
deg, vec = next(iter(self._vec.items()))
|
|
396
|
+
return 'Chain({0}:{1})'.format(deg, vec)
|
|
397
|
+
|
|
398
|
+
return 'Chain with {0} nonzero terms over {1}'.format(
|
|
399
|
+
n, self.parent().base_ring())
|
|
400
|
+
|
|
401
|
+
def _ascii_art_(self):
|
|
402
|
+
"""
|
|
403
|
+
Return an ascii art representation.
|
|
404
|
+
|
|
405
|
+
Note that arrows go to the left so that composition of
|
|
406
|
+
differentials is the usual matrix multiplication.
|
|
407
|
+
|
|
408
|
+
EXAMPLES::
|
|
409
|
+
|
|
410
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]),
|
|
411
|
+
....: 1: zero_matrix(1,2)})
|
|
412
|
+
sage: c = C({0: vector([1, 2, 3]), 1: vector([4, 5])})
|
|
413
|
+
sage: ascii_art(c)
|
|
414
|
+
d_2 d_1 d_0 [1] d_-1
|
|
415
|
+
0 <---- [0] <---- [4] <---- [2] <----- 0
|
|
416
|
+
[5] [3]
|
|
417
|
+
|
|
418
|
+
TESTS:
|
|
419
|
+
|
|
420
|
+
check that :issue:`37678` is fixed::
|
|
421
|
+
|
|
422
|
+
sage: C = ChainComplex(base_ring=ZZ)
|
|
423
|
+
sage: ascii_art(C())
|
|
424
|
+
0
|
|
425
|
+
"""
|
|
426
|
+
from sage.typeset.ascii_art import AsciiArt
|
|
427
|
+
|
|
428
|
+
def arrow_art(d):
|
|
429
|
+
d_str = [' d_{0} '.format(d)]
|
|
430
|
+
arrow = ' <' + '-'*(len(d_str[0])-3) + ' '
|
|
431
|
+
d_str.append(arrow)
|
|
432
|
+
return AsciiArt(d_str, baseline=0)
|
|
433
|
+
|
|
434
|
+
def vector_art(d):
|
|
435
|
+
v = self.vector(d)
|
|
436
|
+
if v.degree() == 0:
|
|
437
|
+
return AsciiArt(['0'])
|
|
438
|
+
v = str(v.column()).splitlines()
|
|
439
|
+
return AsciiArt(v, baseline=len(v)//2)
|
|
440
|
+
|
|
441
|
+
result = []
|
|
442
|
+
chain_complex = self.parent()
|
|
443
|
+
for ordered in chain_complex.ordered_degrees():
|
|
444
|
+
ordered = list(reversed(ordered))
|
|
445
|
+
if len(ordered) == 0:
|
|
446
|
+
return AsciiArt(['0'])
|
|
447
|
+
result_ordered = vector_art(ordered[0] + chain_complex.degree_of_differential())
|
|
448
|
+
for n in ordered:
|
|
449
|
+
result_ordered += arrow_art(n) + vector_art(n)
|
|
450
|
+
result = [result_ordered] + result
|
|
451
|
+
if len(result) == 0:
|
|
452
|
+
return AsciiArt(['0'])
|
|
453
|
+
concatenated = result[0]
|
|
454
|
+
for r in result[1:]:
|
|
455
|
+
concatenated += AsciiArt([' ... ']) + r
|
|
456
|
+
return concatenated
|
|
457
|
+
|
|
458
|
+
def _unicode_art_(self):
|
|
459
|
+
"""
|
|
460
|
+
Return a unicode art representation.
|
|
461
|
+
|
|
462
|
+
Note that arrows go to the left so that composition of
|
|
463
|
+
differentials is the usual matrix multiplication.
|
|
464
|
+
|
|
465
|
+
EXAMPLES::
|
|
466
|
+
|
|
467
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]),
|
|
468
|
+
....: 1: zero_matrix(1,2)})
|
|
469
|
+
sage: c = C({0: vector([1, 2, 3]), 1: vector([4, 5])})
|
|
470
|
+
sage: unicode_art(c)
|
|
471
|
+
⎛1⎞
|
|
472
|
+
d_2 d_1 ⎛4⎞ d_0 ⎜2⎟ d_-1
|
|
473
|
+
0 <──── (0) <──── ⎝5⎠ <──── ⎝3⎠ <───── 0
|
|
474
|
+
sage: unicode_art(C())
|
|
475
|
+
⎛0⎞
|
|
476
|
+
d_2 d_1 ⎛0⎞ d_0 ⎜0⎟ d_-1
|
|
477
|
+
0 <──── (0) <──── ⎝0⎠ <──── ⎝0⎠ <───── 0
|
|
478
|
+
sage: unicode_art(ChainComplex())
|
|
479
|
+
0
|
|
480
|
+
"""
|
|
481
|
+
from sage.typeset.unicode_art import UnicodeArt
|
|
482
|
+
|
|
483
|
+
def arrow_art(d):
|
|
484
|
+
d_str = [' d_{0} '.format(d)]
|
|
485
|
+
arrow = ' <' + '─' * (len(d_str[0]) - 3) + ' '
|
|
486
|
+
d_str.append(arrow)
|
|
487
|
+
return UnicodeArt(d_str, baseline=0)
|
|
488
|
+
|
|
489
|
+
def vector_art(d):
|
|
490
|
+
v = self.vector(d)
|
|
491
|
+
if not v.degree():
|
|
492
|
+
return UnicodeArt(['0'])
|
|
493
|
+
w = matrix(v).transpose()
|
|
494
|
+
return w._unicode_art_()
|
|
495
|
+
|
|
496
|
+
result = []
|
|
497
|
+
chain_complex = self.parent()
|
|
498
|
+
for ordered in chain_complex.ordered_degrees():
|
|
499
|
+
ordered = list(reversed(ordered))
|
|
500
|
+
if not ordered:
|
|
501
|
+
return UnicodeArt(['0'])
|
|
502
|
+
result_ordered = vector_art(ordered[0] +
|
|
503
|
+
chain_complex.degree_of_differential())
|
|
504
|
+
for n in ordered:
|
|
505
|
+
result_ordered += arrow_art(n) + vector_art(n)
|
|
506
|
+
result = [result_ordered] + result
|
|
507
|
+
if len(result) == 0:
|
|
508
|
+
return UnicodeArt(['0'])
|
|
509
|
+
concatenated = result[0]
|
|
510
|
+
for r in result[1:]:
|
|
511
|
+
concatenated += UnicodeArt([' ... ']) + r
|
|
512
|
+
return concatenated
|
|
513
|
+
|
|
514
|
+
def is_cycle(self):
|
|
515
|
+
"""
|
|
516
|
+
Return whether the chain is a cycle.
|
|
517
|
+
|
|
518
|
+
OUTPUT: boolean; whether the elements of the chain are in the kernel
|
|
519
|
+
of the differentials
|
|
520
|
+
|
|
521
|
+
EXAMPLES::
|
|
522
|
+
|
|
523
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
524
|
+
sage: c = C({0: vector([0, 1, 2]), 1: vector([3, 4])})
|
|
525
|
+
sage: c.is_cycle()
|
|
526
|
+
True
|
|
527
|
+
"""
|
|
528
|
+
chain_complex = self.parent()
|
|
529
|
+
for d, v in self._vec.items():
|
|
530
|
+
dv = chain_complex.differential(d) * v
|
|
531
|
+
if not dv.is_zero():
|
|
532
|
+
return False
|
|
533
|
+
return True
|
|
534
|
+
|
|
535
|
+
def is_boundary(self):
|
|
536
|
+
"""
|
|
537
|
+
Return whether the chain is a boundary.
|
|
538
|
+
|
|
539
|
+
OUTPUT:
|
|
540
|
+
|
|
541
|
+
boolean; whether the elements of the chain are in the image of
|
|
542
|
+
the differentials.
|
|
543
|
+
|
|
544
|
+
EXAMPLES::
|
|
545
|
+
|
|
546
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
547
|
+
sage: c = C({0: vector([0, 1, 2]), 1: vector([3, 4])})
|
|
548
|
+
sage: c.is_boundary()
|
|
549
|
+
False
|
|
550
|
+
sage: z3 = C({1:(1, 0)})
|
|
551
|
+
sage: z3.is_cycle()
|
|
552
|
+
True
|
|
553
|
+
sage: (2*z3).is_boundary()
|
|
554
|
+
False
|
|
555
|
+
sage: (3*z3).is_boundary()
|
|
556
|
+
True
|
|
557
|
+
"""
|
|
558
|
+
chain_complex = self.parent()
|
|
559
|
+
for d, v in self._vec.items():
|
|
560
|
+
d = chain_complex.differential(d - chain_complex.degree_of_differential()).transpose()
|
|
561
|
+
if v not in d.image():
|
|
562
|
+
return False
|
|
563
|
+
return True
|
|
564
|
+
|
|
565
|
+
def _add_(self, other):
|
|
566
|
+
"""
|
|
567
|
+
Module addition.
|
|
568
|
+
|
|
569
|
+
EXAMPLES::
|
|
570
|
+
|
|
571
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
572
|
+
sage: c = C({0: vector([0, 1, 2]), 1: vector([3, 4])})
|
|
573
|
+
sage: c + c
|
|
574
|
+
Chain with 2 nonzero terms over Integer Ring
|
|
575
|
+
sage: ascii_art(c + c)
|
|
576
|
+
d_1 d_0 [0] d_-1
|
|
577
|
+
0 <---- [6] <---- [2] <----- 0
|
|
578
|
+
[8] [4]
|
|
579
|
+
"""
|
|
580
|
+
vectors = {}
|
|
581
|
+
for d in set(list(self._vec) + list(other._vec)):
|
|
582
|
+
v = self.vector(d) + other.vector(d)
|
|
583
|
+
if not v.is_zero():
|
|
584
|
+
v.set_immutable()
|
|
585
|
+
vectors[d] = v
|
|
586
|
+
parent = self.parent()
|
|
587
|
+
return parent.element_class(parent, vectors)
|
|
588
|
+
|
|
589
|
+
def _lmul_(self, scalar):
|
|
590
|
+
"""
|
|
591
|
+
Scalar multiplication.
|
|
592
|
+
|
|
593
|
+
EXAMPLES::
|
|
594
|
+
|
|
595
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
596
|
+
sage: c = C({0: vector([0, 1, 2]), 1: vector([3, 4])})
|
|
597
|
+
sage: 2 * c
|
|
598
|
+
Chain with 2 nonzero terms over Integer Ring
|
|
599
|
+
sage: 2 * c == c + c == c * 2
|
|
600
|
+
True
|
|
601
|
+
"""
|
|
602
|
+
vectors = dict()
|
|
603
|
+
for d, v in self._vec.items():
|
|
604
|
+
v = scalar * v
|
|
605
|
+
if not v.is_zero():
|
|
606
|
+
v.set_immutable()
|
|
607
|
+
vectors[d] = v
|
|
608
|
+
parent = self.parent()
|
|
609
|
+
return parent.element_class(parent, vectors)
|
|
610
|
+
|
|
611
|
+
def __eq__(self, other):
|
|
612
|
+
"""
|
|
613
|
+
Return ``True`` if this chain is equal to ``other``.
|
|
614
|
+
|
|
615
|
+
EXAMPLES::
|
|
616
|
+
|
|
617
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
618
|
+
sage: c = C({0: vector([0, 1, 2]), 1: vector([3, 4])})
|
|
619
|
+
sage: c == c
|
|
620
|
+
True
|
|
621
|
+
sage: c == C(0)
|
|
622
|
+
False
|
|
623
|
+
"""
|
|
624
|
+
if type(self) is not type(other) or self.parent() != other.parent():
|
|
625
|
+
return False
|
|
626
|
+
return self._vec == other._vec
|
|
627
|
+
|
|
628
|
+
def __ne__(self, other):
|
|
629
|
+
"""
|
|
630
|
+
Return ``True`` if this chain is not equal to ``other``.
|
|
631
|
+
|
|
632
|
+
EXAMPLES::
|
|
633
|
+
|
|
634
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
635
|
+
sage: c = C({0: vector([0, 1, 2]), 1: vector([3, 4])})
|
|
636
|
+
sage: c != c
|
|
637
|
+
False
|
|
638
|
+
sage: c != C(0)
|
|
639
|
+
True
|
|
640
|
+
"""
|
|
641
|
+
return not self == other
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
class ChainComplex_class(Parent):
|
|
645
|
+
r"""
|
|
646
|
+
See :func:`ChainComplex` for full documentation.
|
|
647
|
+
|
|
648
|
+
The differentials are required to be in the following canonical form:
|
|
649
|
+
|
|
650
|
+
* All differentials that are not `0 \times 0` must be specified
|
|
651
|
+
(even if they have zero rows or zero columns), and
|
|
652
|
+
|
|
653
|
+
* Differentials that are `0 \times 0` must not be specified.
|
|
654
|
+
|
|
655
|
+
* Immutable matrices over the ``base_ring``
|
|
656
|
+
|
|
657
|
+
This and more is ensured by the assertions in the
|
|
658
|
+
constructor. The :func:`ChainComplex` factory function must
|
|
659
|
+
ensure that only valid input is passed.
|
|
660
|
+
|
|
661
|
+
EXAMPLES::
|
|
662
|
+
|
|
663
|
+
sage: C = ChainComplex(); C
|
|
664
|
+
Trivial chain complex over Integer Ring
|
|
665
|
+
|
|
666
|
+
sage: D = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
667
|
+
sage: D
|
|
668
|
+
Chain complex with at most 2 nonzero terms over Integer Ring
|
|
669
|
+
"""
|
|
670
|
+
def __init__(self, grading_group, degree_of_differential, base_ring, differentials):
|
|
671
|
+
"""
|
|
672
|
+
Initialize ``self``.
|
|
673
|
+
|
|
674
|
+
TESTS::
|
|
675
|
+
|
|
676
|
+
sage: ChainComplex().base_ring()
|
|
677
|
+
Integer Ring
|
|
678
|
+
|
|
679
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
680
|
+
sage: TestSuite(C).run()
|
|
681
|
+
"""
|
|
682
|
+
if any(d.base_ring() != base_ring or not d.is_immutable() or
|
|
683
|
+
(d.ncols(), d.nrows()) == (0, 0)
|
|
684
|
+
for d in differentials.values()):
|
|
685
|
+
raise ValueError('invalid differentials')
|
|
686
|
+
if degree_of_differential.parent() is not grading_group:
|
|
687
|
+
raise ValueError('the degree_of_differential.parent() must be grading_group')
|
|
688
|
+
if grading_group is not ZZ and grading_group.is_multiplicative():
|
|
689
|
+
raise ValueError('grading_group must be either ZZ or multiplicative')
|
|
690
|
+
# all differentials (excluding the 0x0 ones) must be specified to the constructor
|
|
691
|
+
if any(dim+degree_of_differential not in differentials and d.nrows() != 0
|
|
692
|
+
for dim, d in differentials.items()):
|
|
693
|
+
raise ValueError('invalid differentials')
|
|
694
|
+
if any(dim-degree_of_differential not in differentials and d.ncols() != 0
|
|
695
|
+
for dim, d in differentials.items()):
|
|
696
|
+
raise ValueError('invalid differentials')
|
|
697
|
+
self._grading_group = grading_group
|
|
698
|
+
self._degree_of_differential = degree_of_differential
|
|
699
|
+
self._diff = differentials
|
|
700
|
+
|
|
701
|
+
from sage.categories.chain_complexes import ChainComplexes
|
|
702
|
+
category = ChainComplexes(base_ring)
|
|
703
|
+
super().__init__(base=base_ring, category=category)
|
|
704
|
+
|
|
705
|
+
Element = Chain_class
|
|
706
|
+
|
|
707
|
+
def _element_constructor_(self, vectors, check=True):
|
|
708
|
+
"""
|
|
709
|
+
The element constructor.
|
|
710
|
+
|
|
711
|
+
This is part of the Parent/Element framework. Calling the
|
|
712
|
+
parent uses this method to construct elements.
|
|
713
|
+
|
|
714
|
+
TESTS::
|
|
715
|
+
|
|
716
|
+
sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])})
|
|
717
|
+
sage: D._element_constructor_(0)
|
|
718
|
+
Trivial chain
|
|
719
|
+
sage: D({0:[2, 3]})
|
|
720
|
+
Chain(0:(2, 3))
|
|
721
|
+
"""
|
|
722
|
+
if not vectors: # special case: the zero chain
|
|
723
|
+
return self.element_class(self, {})
|
|
724
|
+
if isinstance(vectors, Chain_class):
|
|
725
|
+
vectors = vectors._vec
|
|
726
|
+
data = dict()
|
|
727
|
+
for degree, vec in vectors.items():
|
|
728
|
+
if not isinstance(vec, Vector):
|
|
729
|
+
vec = vector(self.base_ring(), vec)
|
|
730
|
+
vec.set_immutable()
|
|
731
|
+
if check and vec.degree() != self.free_module_rank(degree):
|
|
732
|
+
raise ValueError('vector dimension does not match module dimension')
|
|
733
|
+
if vec.is_zero():
|
|
734
|
+
continue
|
|
735
|
+
if vec.base_ring() != self.base_ring():
|
|
736
|
+
vec = vec.change_ring(self.base_ring())
|
|
737
|
+
if not vec.is_immutable():
|
|
738
|
+
vec = copy(vec)
|
|
739
|
+
vec.set_immutable()
|
|
740
|
+
data[degree] = vec
|
|
741
|
+
return self.element_class(self, data)
|
|
742
|
+
|
|
743
|
+
def random_element(self):
|
|
744
|
+
"""
|
|
745
|
+
Return a random element.
|
|
746
|
+
|
|
747
|
+
EXAMPLES::
|
|
748
|
+
|
|
749
|
+
sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])})
|
|
750
|
+
sage: D.random_element() # random output
|
|
751
|
+
Chain with 1 nonzero terms over Integer Ring
|
|
752
|
+
"""
|
|
753
|
+
vec = dict()
|
|
754
|
+
for d in self.nonzero_degrees():
|
|
755
|
+
vec[d] = self.free_module(d).random_element()
|
|
756
|
+
return self(vec)
|
|
757
|
+
|
|
758
|
+
_an_element_ = random_element
|
|
759
|
+
|
|
760
|
+
@cached_method
|
|
761
|
+
def rank(self, degree, ring=None):
|
|
762
|
+
r"""
|
|
763
|
+
Return the rank of a differential.
|
|
764
|
+
|
|
765
|
+
INPUT:
|
|
766
|
+
|
|
767
|
+
- ``degree`` -- an element `\delta` of the grading
|
|
768
|
+
group. Which differential `d_{\delta}` we want to know the
|
|
769
|
+
rank of
|
|
770
|
+
|
|
771
|
+
- ``ring`` -- (optional) a commutative ring `S`;
|
|
772
|
+
if specified, the rank is computed after changing to this ring
|
|
773
|
+
|
|
774
|
+
OUTPUT:
|
|
775
|
+
|
|
776
|
+
The rank of the differential `d_{\delta} \otimes_R S`, where
|
|
777
|
+
`R` is the base ring of the chain complex.
|
|
778
|
+
|
|
779
|
+
EXAMPLES::
|
|
780
|
+
|
|
781
|
+
sage: C = ChainComplex({0:matrix(ZZ, [[2]])})
|
|
782
|
+
sage: C.differential(0)
|
|
783
|
+
[2]
|
|
784
|
+
sage: C.rank(0)
|
|
785
|
+
1
|
|
786
|
+
sage: C.rank(0, ring=GF(2))
|
|
787
|
+
0
|
|
788
|
+
"""
|
|
789
|
+
degree = self.grading_group()(degree)
|
|
790
|
+
try:
|
|
791
|
+
d = self._diff[degree]
|
|
792
|
+
except IndexError:
|
|
793
|
+
return ZZ.zero()
|
|
794
|
+
if d.nrows() == 0 or d.ncols() == 0:
|
|
795
|
+
return ZZ.zero()
|
|
796
|
+
if ring is None:
|
|
797
|
+
return d.rank()
|
|
798
|
+
return d.change_ring(ring).rank()
|
|
799
|
+
|
|
800
|
+
def grading_group(self):
|
|
801
|
+
r"""
|
|
802
|
+
Return the grading group.
|
|
803
|
+
|
|
804
|
+
OUTPUT:
|
|
805
|
+
|
|
806
|
+
The discrete abelian group that indexes the individual modules
|
|
807
|
+
of the complex. Usually `\ZZ`.
|
|
808
|
+
|
|
809
|
+
EXAMPLES::
|
|
810
|
+
|
|
811
|
+
sage: G = AdditiveAbelianGroup([0, 3])
|
|
812
|
+
sage: C = ChainComplex(grading_group=G, degree=G(vector([1,2])))
|
|
813
|
+
sage: C.grading_group() # needs sage.libs.pari
|
|
814
|
+
Additive abelian group isomorphic to Z + Z/3
|
|
815
|
+
sage: C.degree_of_differential()
|
|
816
|
+
(1, 2)
|
|
817
|
+
"""
|
|
818
|
+
return self._grading_group
|
|
819
|
+
|
|
820
|
+
@cached_method
|
|
821
|
+
def nonzero_degrees(self):
|
|
822
|
+
r"""
|
|
823
|
+
Return the degrees in which the module is non-trivial.
|
|
824
|
+
|
|
825
|
+
See also :meth:`ordered_degrees`.
|
|
826
|
+
|
|
827
|
+
OUTPUT:
|
|
828
|
+
|
|
829
|
+
The tuple containing all degrees `n` (grading group elements)
|
|
830
|
+
such that the module `C_n` of the chain is non-trivial.
|
|
831
|
+
|
|
832
|
+
EXAMPLES::
|
|
833
|
+
|
|
834
|
+
sage: one = matrix(ZZ, [[1]])
|
|
835
|
+
sage: D = ChainComplex({0: one, 2: one, 6:one})
|
|
836
|
+
sage: ascii_art(D)
|
|
837
|
+
[1] [1] [0] [1]
|
|
838
|
+
0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0
|
|
839
|
+
sage: D.nonzero_degrees()
|
|
840
|
+
(0, 1, 2, 3, 6, 7)
|
|
841
|
+
"""
|
|
842
|
+
return tuple(sorted(n for n, d in self._diff.items()
|
|
843
|
+
if d.ncols()))
|
|
844
|
+
|
|
845
|
+
@cached_method
|
|
846
|
+
def ordered_degrees(self, start=None, exclude_first=False):
|
|
847
|
+
r"""
|
|
848
|
+
Sort the degrees in the order determined by the differential.
|
|
849
|
+
|
|
850
|
+
INPUT:
|
|
851
|
+
|
|
852
|
+
- ``start`` -- (default: ``None``) a degree (element of the grading
|
|
853
|
+
group) or ``None``
|
|
854
|
+
|
|
855
|
+
- ``exclude_first`` -- boolean (optional; default:
|
|
856
|
+
``False``); whether to exclude the lowest degree -- this is a
|
|
857
|
+
handy way to just get the degrees of the nonzero modules,
|
|
858
|
+
as the domain of the first differential is zero.
|
|
859
|
+
|
|
860
|
+
OUTPUT: if ``start`` has been specified, the longest tuple of degrees
|
|
861
|
+
|
|
862
|
+
* containing ``start`` (unless ``start`` would be the first
|
|
863
|
+
and ``exclude_first=True``),
|
|
864
|
+
|
|
865
|
+
* in ascending order relative to :meth:`degree_of_differential`, and
|
|
866
|
+
|
|
867
|
+
* such that none of the corresponding differentials are `0\times 0`.
|
|
868
|
+
|
|
869
|
+
If ``start`` has not been specified, a tuple of such tuples of
|
|
870
|
+
degrees. One for each sequence of nonzero differentials. They
|
|
871
|
+
are returned in sort order.
|
|
872
|
+
|
|
873
|
+
EXAMPLES::
|
|
874
|
+
|
|
875
|
+
sage: one = matrix(ZZ, [[1]])
|
|
876
|
+
sage: D = ChainComplex({0: one, 2: one, 6:one})
|
|
877
|
+
sage: ascii_art(D)
|
|
878
|
+
[1] [1] [0] [1]
|
|
879
|
+
0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0
|
|
880
|
+
sage: D.ordered_degrees()
|
|
881
|
+
((-1, 0, 1, 2, 3), (5, 6, 7))
|
|
882
|
+
sage: D.ordered_degrees(exclude_first=True)
|
|
883
|
+
((0, 1, 2, 3), (6, 7))
|
|
884
|
+
sage: D.ordered_degrees(6)
|
|
885
|
+
(5, 6, 7)
|
|
886
|
+
sage: D.ordered_degrees(5, exclude_first=True)
|
|
887
|
+
(6, 7)
|
|
888
|
+
"""
|
|
889
|
+
if start is None:
|
|
890
|
+
result = []
|
|
891
|
+
degrees = set(self._diff)
|
|
892
|
+
while degrees:
|
|
893
|
+
ordered = self.ordered_degrees(degrees.pop())
|
|
894
|
+
degrees.difference_update(ordered)
|
|
895
|
+
if exclude_first:
|
|
896
|
+
ordered = tuple(ordered[1:])
|
|
897
|
+
result.append(ordered)
|
|
898
|
+
result.sort()
|
|
899
|
+
return tuple(result)
|
|
900
|
+
|
|
901
|
+
from collections import deque
|
|
902
|
+
result = deque()
|
|
903
|
+
result.append(start)
|
|
904
|
+
|
|
905
|
+
next_deg = start + self.degree_of_differential()
|
|
906
|
+
while next_deg in self._diff:
|
|
907
|
+
result.append(next_deg)
|
|
908
|
+
next_deg += self.degree_of_differential()
|
|
909
|
+
|
|
910
|
+
prev_deg = start - self.degree_of_differential()
|
|
911
|
+
while prev_deg in self._diff:
|
|
912
|
+
result.appendleft(prev_deg)
|
|
913
|
+
prev_deg -= self.degree_of_differential()
|
|
914
|
+
|
|
915
|
+
if exclude_first:
|
|
916
|
+
result.popleft()
|
|
917
|
+
return tuple(result)
|
|
918
|
+
|
|
919
|
+
def degree_of_differential(self):
|
|
920
|
+
"""
|
|
921
|
+
Return the degree of the differentials of the complex.
|
|
922
|
+
|
|
923
|
+
OUTPUT: an element of the grading group
|
|
924
|
+
|
|
925
|
+
EXAMPLES::
|
|
926
|
+
|
|
927
|
+
sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])})
|
|
928
|
+
sage: D.degree_of_differential()
|
|
929
|
+
1
|
|
930
|
+
"""
|
|
931
|
+
return self._degree_of_differential
|
|
932
|
+
|
|
933
|
+
def differential(self, dim=None):
|
|
934
|
+
"""
|
|
935
|
+
The differentials which make up the chain complex.
|
|
936
|
+
|
|
937
|
+
INPUT:
|
|
938
|
+
|
|
939
|
+
- ``dim`` -- element of the grading group (default:
|
|
940
|
+
``None``); if this is ``None``, return a dictionary of all
|
|
941
|
+
of the differentials, or if this is a single element, return
|
|
942
|
+
the differential starting in that dimension
|
|
943
|
+
|
|
944
|
+
OUTPUT:
|
|
945
|
+
|
|
946
|
+
Either a dictionary of all of the differentials or a single
|
|
947
|
+
differential (i.e., a matrix).
|
|
948
|
+
|
|
949
|
+
EXAMPLES::
|
|
950
|
+
|
|
951
|
+
sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])})
|
|
952
|
+
sage: D.differential(0)
|
|
953
|
+
[1 0]
|
|
954
|
+
[0 2]
|
|
955
|
+
sage: D.differential(-1)
|
|
956
|
+
[]
|
|
957
|
+
sage: C = ChainComplex({0: identity_matrix(ZZ, 40)})
|
|
958
|
+
sage: diff = C.differential()
|
|
959
|
+
sage: diff[-1]
|
|
960
|
+
40 x 0 dense matrix over Integer Ring (use the '.str()' method to see the entries)
|
|
961
|
+
sage: diff[0]
|
|
962
|
+
40 x 40 dense matrix over Integer Ring (use the '.str()' method to see the entries)
|
|
963
|
+
sage: diff[1]
|
|
964
|
+
[]
|
|
965
|
+
"""
|
|
966
|
+
if dim is None:
|
|
967
|
+
return copy(self._diff)
|
|
968
|
+
dim = self.grading_group()(dim)
|
|
969
|
+
try:
|
|
970
|
+
return self._diff[dim]
|
|
971
|
+
except KeyError:
|
|
972
|
+
pass
|
|
973
|
+
# all differentials that are not 0x0 are in self._diff
|
|
974
|
+
# TODO: turn differentials into morphisms between free modules?
|
|
975
|
+
return matrix(self.base_ring(), 0, 0)
|
|
976
|
+
|
|
977
|
+
def dual(self):
|
|
978
|
+
"""
|
|
979
|
+
The dual chain complex to ``self``.
|
|
980
|
+
|
|
981
|
+
Since all modules in ``self`` are free of finite rank, the
|
|
982
|
+
dual in dimension `n` is isomorphic to the original chain
|
|
983
|
+
complex in dimension `n`, and the corresponding boundary
|
|
984
|
+
matrix is the transpose of the matrix in the original complex.
|
|
985
|
+
This converts a chain complex to a cochain complex and vice versa.
|
|
986
|
+
|
|
987
|
+
EXAMPLES::
|
|
988
|
+
|
|
989
|
+
sage: C = ChainComplex({2: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
990
|
+
sage: C.degree_of_differential()
|
|
991
|
+
1
|
|
992
|
+
sage: C.differential(2)
|
|
993
|
+
[3 0 0]
|
|
994
|
+
[0 0 0]
|
|
995
|
+
sage: C.dual().degree_of_differential()
|
|
996
|
+
-1
|
|
997
|
+
sage: C.dual().differential(3)
|
|
998
|
+
[3 0]
|
|
999
|
+
[0 0]
|
|
1000
|
+
[0 0]
|
|
1001
|
+
"""
|
|
1002
|
+
data = {}
|
|
1003
|
+
deg = self.degree_of_differential()
|
|
1004
|
+
for d in self.differential():
|
|
1005
|
+
data[(d+deg)] = self.differential()[d].transpose()
|
|
1006
|
+
return ChainComplex(data, degree=-deg)
|
|
1007
|
+
|
|
1008
|
+
def free_module_rank(self, degree):
|
|
1009
|
+
r"""
|
|
1010
|
+
Return the rank of the free module at the given ``degree``.
|
|
1011
|
+
|
|
1012
|
+
INPUT:
|
|
1013
|
+
|
|
1014
|
+
- ``degree`` -- an element of the grading group
|
|
1015
|
+
|
|
1016
|
+
OUTPUT:
|
|
1017
|
+
|
|
1018
|
+
Integer. The rank of the free module `C_n` at the given degree
|
|
1019
|
+
`n`.
|
|
1020
|
+
|
|
1021
|
+
EXAMPLES::
|
|
1022
|
+
|
|
1023
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1: matrix(ZZ, [[0, 1]])})
|
|
1024
|
+
sage: [C.free_module_rank(i) for i in range(-2, 5)]
|
|
1025
|
+
[0, 0, 3, 2, 1, 0, 0]
|
|
1026
|
+
"""
|
|
1027
|
+
try:
|
|
1028
|
+
return self._diff[degree].ncols()
|
|
1029
|
+
except KeyError:
|
|
1030
|
+
return ZZ.zero()
|
|
1031
|
+
|
|
1032
|
+
def free_module(self, degree=None):
|
|
1033
|
+
r"""
|
|
1034
|
+
Return the free module at fixed ``degree``, or their sum.
|
|
1035
|
+
|
|
1036
|
+
INPUT:
|
|
1037
|
+
|
|
1038
|
+
- ``degree`` -- an element of the grading group or ``None`` (default)
|
|
1039
|
+
|
|
1040
|
+
OUTPUT:
|
|
1041
|
+
|
|
1042
|
+
The free module `C_n` at the given degree `n`. If the degree
|
|
1043
|
+
is not specified, the sum `\bigoplus C_n` is returned.
|
|
1044
|
+
|
|
1045
|
+
EXAMPLES::
|
|
1046
|
+
|
|
1047
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1: matrix(ZZ, [[0, 1]])})
|
|
1048
|
+
sage: C.free_module()
|
|
1049
|
+
Ambient free module of rank 6 over the principal ideal domain Integer Ring
|
|
1050
|
+
sage: C.free_module(0)
|
|
1051
|
+
Ambient free module of rank 3 over the principal ideal domain Integer Ring
|
|
1052
|
+
sage: C.free_module(1)
|
|
1053
|
+
Ambient free module of rank 2 over the principal ideal domain Integer Ring
|
|
1054
|
+
sage: C.free_module(2)
|
|
1055
|
+
Ambient free module of rank 1 over the principal ideal domain Integer Ring
|
|
1056
|
+
"""
|
|
1057
|
+
if degree is None:
|
|
1058
|
+
rank = sum([mat.ncols() for mat in self.differential().values()])
|
|
1059
|
+
else:
|
|
1060
|
+
rank = self.free_module_rank(degree)
|
|
1061
|
+
return FreeModule(self.base_ring(), rank)
|
|
1062
|
+
|
|
1063
|
+
def __hash__(self):
|
|
1064
|
+
"""
|
|
1065
|
+
The hash is formed by combining the hashes of.
|
|
1066
|
+
|
|
1067
|
+
- the base ring
|
|
1068
|
+
- the differentials -- the matrices and their degrees
|
|
1069
|
+
- the degree of the differential of the chain complex
|
|
1070
|
+
|
|
1071
|
+
EXAMPLES::
|
|
1072
|
+
|
|
1073
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1074
|
+
sage: D = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1075
|
+
sage: hash(C) == hash(D)
|
|
1076
|
+
True
|
|
1077
|
+
"""
|
|
1078
|
+
return (hash(self.base_ring())
|
|
1079
|
+
^ hash(tuple(self.differential().items()))
|
|
1080
|
+
^ hash(self.degree_of_differential()))
|
|
1081
|
+
|
|
1082
|
+
def __eq__(self, other):
|
|
1083
|
+
"""
|
|
1084
|
+
Return ``True`` iff this chain complex is the same as other: that
|
|
1085
|
+
is, if the base rings and the matrices of the two are the
|
|
1086
|
+
same.
|
|
1087
|
+
|
|
1088
|
+
EXAMPLES::
|
|
1089
|
+
|
|
1090
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])},
|
|
1091
|
+
....: base_ring=GF(2))
|
|
1092
|
+
sage: D = ChainComplex({0: matrix(GF(2), 2, 3, [1, 0, 0, 0, 0, 0]),
|
|
1093
|
+
....: 1: matrix(ZZ, 0, 2),
|
|
1094
|
+
....: 3: matrix(ZZ, 0, 0)}) # base_ring determined from the matrices
|
|
1095
|
+
sage: C == D
|
|
1096
|
+
True
|
|
1097
|
+
"""
|
|
1098
|
+
if not isinstance(other, ChainComplex_class) or self.base_ring() != other.base_ring():
|
|
1099
|
+
return False
|
|
1100
|
+
R = self.base_ring()
|
|
1101
|
+
equal = True
|
|
1102
|
+
for d, mat in self.differential().items():
|
|
1103
|
+
if d not in other.differential():
|
|
1104
|
+
equal = equal and mat.ncols() == 0 and mat.nrows() == 0
|
|
1105
|
+
else:
|
|
1106
|
+
equal = (equal and
|
|
1107
|
+
other.differential()[d].change_ring(R) == mat.change_ring(R))
|
|
1108
|
+
for d, mat in other.differential().items():
|
|
1109
|
+
if d not in self.differential():
|
|
1110
|
+
equal = equal and mat.ncols() == 0 and mat.nrows() == 0
|
|
1111
|
+
return equal
|
|
1112
|
+
|
|
1113
|
+
def __ne__(self, other):
|
|
1114
|
+
"""
|
|
1115
|
+
Return ``True`` iff this chain complex is not the same as other.
|
|
1116
|
+
|
|
1117
|
+
EXAMPLES::
|
|
1118
|
+
|
|
1119
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])},
|
|
1120
|
+
....: base_ring=GF(2))
|
|
1121
|
+
sage: D = ChainComplex({0: matrix(GF(2), 2, 3, [1, 0, 0, 0, 0, 0]),
|
|
1122
|
+
....: 1: matrix(ZZ, 0, 2),
|
|
1123
|
+
....: 3: matrix(ZZ, 0, 0)}) # base_ring determined from the matrices
|
|
1124
|
+
sage: C != D
|
|
1125
|
+
False
|
|
1126
|
+
sage: E = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])},
|
|
1127
|
+
....: base_ring=ZZ)
|
|
1128
|
+
sage: C != E
|
|
1129
|
+
True
|
|
1130
|
+
"""
|
|
1131
|
+
return not self == other
|
|
1132
|
+
|
|
1133
|
+
def homology(self, deg=None, base_ring=None, generators=False,
|
|
1134
|
+
verbose=False, algorithm='pari'):
|
|
1135
|
+
r"""
|
|
1136
|
+
The homology of the chain complex.
|
|
1137
|
+
|
|
1138
|
+
INPUT:
|
|
1139
|
+
|
|
1140
|
+
- ``deg`` -- an element of the grading group for the chain
|
|
1141
|
+
complex (default: ``None``); the degree in which
|
|
1142
|
+
to compute homology -- if this is ``None``, return the
|
|
1143
|
+
homology in every degree in which the chain complex is
|
|
1144
|
+
possibly nonzero.
|
|
1145
|
+
|
|
1146
|
+
- ``base_ring`` -- a commutative ring (default: the
|
|
1147
|
+
base ring for the chain complex); must be either the
|
|
1148
|
+
integers `\ZZ` or a field
|
|
1149
|
+
|
|
1150
|
+
- ``generators`` -- boolean (default: ``False``); if
|
|
1151
|
+
``True``, return generators for the homology groups along with
|
|
1152
|
+
the groups. See :issue:`6100`
|
|
1153
|
+
|
|
1154
|
+
- ``verbose`` -- boolean (default: ``False``); if
|
|
1155
|
+
``True``, print some messages as the homology is computed
|
|
1156
|
+
|
|
1157
|
+
- ``algorithm`` -- string (default: ``'pari'``); the
|
|
1158
|
+
options are:
|
|
1159
|
+
|
|
1160
|
+
* ``'auto'``
|
|
1161
|
+
* ``'dhsw'``
|
|
1162
|
+
* ``'pari'``
|
|
1163
|
+
|
|
1164
|
+
See below for descriptions.
|
|
1165
|
+
|
|
1166
|
+
OUTPUT:
|
|
1167
|
+
|
|
1168
|
+
If the degree is specified, the homology in degree ``deg``.
|
|
1169
|
+
Otherwise, the homology in every dimension as a dictionary
|
|
1170
|
+
indexed by dimension.
|
|
1171
|
+
|
|
1172
|
+
ALGORITHM:
|
|
1173
|
+
|
|
1174
|
+
Over a
|
|
1175
|
+
field, just compute ranks and nullities, thus obtaining
|
|
1176
|
+
dimensions of the homology groups as vector spaces. Over the
|
|
1177
|
+
integers, compute Smith normal form of the boundary matrices
|
|
1178
|
+
defining the chain complex according to the value of
|
|
1179
|
+
``algorithm``. If ``algorithm`` is ``'auto'``,
|
|
1180
|
+
then for each relatively small matrix, use the standard Sage
|
|
1181
|
+
method, which calls the Pari package. For any large matrix,
|
|
1182
|
+
reduce it using the Dumas, Heckenbach, Saunders, and Welker
|
|
1183
|
+
elimination algorithm [DHSW2003]_: see
|
|
1184
|
+
:func:`~sage.homology.matrix_utils.dhsw_snf` for details.
|
|
1185
|
+
|
|
1186
|
+
``'no_chomp'`` is a synonym for ``'auto'``, maintained for
|
|
1187
|
+
backward-compatibility.
|
|
1188
|
+
|
|
1189
|
+
``algorithm`` may also be ``'pari'`` or ``'dhsw'``, which
|
|
1190
|
+
forces the named algorithm to be used regardless of the size
|
|
1191
|
+
of the matrices.
|
|
1192
|
+
|
|
1193
|
+
As of this writing, ``'pari'`` is the fastest standard option.
|
|
1194
|
+
|
|
1195
|
+
.. WARNING::
|
|
1196
|
+
|
|
1197
|
+
This only works if the base ring is the integers or a
|
|
1198
|
+
field. Other values will return an error.
|
|
1199
|
+
|
|
1200
|
+
EXAMPLES::
|
|
1201
|
+
|
|
1202
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1203
|
+
sage: C.homology() # needs sage.libs.pari
|
|
1204
|
+
{0: Z x Z, 1: Z x C3}
|
|
1205
|
+
sage: C.homology(deg=1, base_ring=GF(3)) # needs sage.libs.pari
|
|
1206
|
+
Vector space of dimension 2 over Finite Field of size 3
|
|
1207
|
+
sage: D = ChainComplex({0: identity_matrix(ZZ, 4), 4: identity_matrix(ZZ, 30)})
|
|
1208
|
+
sage: D.homology() # needs sage.libs.pari
|
|
1209
|
+
{0: 0, 1: 0, 4: 0, 5: 0}
|
|
1210
|
+
|
|
1211
|
+
Generators: generators are given as a list of cycles, each of
|
|
1212
|
+
which is an element in the appropriate free module, and hence
|
|
1213
|
+
is represented as a vector. Each summand of the homology is
|
|
1214
|
+
listed separately, with a corresponding generator::
|
|
1215
|
+
|
|
1216
|
+
sage: C.homology(1, generators=True) # needs sage.libs.pari
|
|
1217
|
+
[(C3, Chain(1:(1, 0))), (Z, Chain(1:(0, 1)))]
|
|
1218
|
+
|
|
1219
|
+
Tests for :issue:`6100`, the Klein bottle with generators::
|
|
1220
|
+
|
|
1221
|
+
sage: d0 = matrix(ZZ, 0,1)
|
|
1222
|
+
sage: d1 = matrix(ZZ, 1,3, [[0,0,0]])
|
|
1223
|
+
sage: d2 = matrix(ZZ, 3,2, [[1,1], [1,-1], [-1,1]])
|
|
1224
|
+
sage: C_k = ChainComplex({0:d0, 1:d1, 2:d2}, degree=-1)
|
|
1225
|
+
sage: C_k.homology(generators=true) # needs sage.libs.pari
|
|
1226
|
+
{0: [(Z, Chain(0:(1)))],
|
|
1227
|
+
1: [(C2, Chain(1:(0, 1, -1))), (Z, Chain(1:(0, 1, 0)))],
|
|
1228
|
+
2: []}
|
|
1229
|
+
|
|
1230
|
+
From a torus using a field::
|
|
1231
|
+
|
|
1232
|
+
sage: T = simplicial_complexes.Torus() # needs sage.graphs
|
|
1233
|
+
sage: C_t = T.chain_complex() # needs sage.graphs
|
|
1234
|
+
sage: C_t.homology(base_ring=QQ, generators=True) # needs sage.graphs sage.libs.pari
|
|
1235
|
+
{0: [(Vector space of dimension 1 over Rational Field,
|
|
1236
|
+
Chain(0:(0, 0, 0, 0, 0, 0, 1)))],
|
|
1237
|
+
1: [(Vector space of dimension 1 over Rational Field,
|
|
1238
|
+
Chain(1:(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 1))),
|
|
1239
|
+
(Vector space of dimension 1 over Rational Field,
|
|
1240
|
+
Chain(1:(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, -1, 0)))],
|
|
1241
|
+
2: [(Vector space of dimension 1 over Rational Field,
|
|
1242
|
+
Chain(2:(1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1)))]}
|
|
1243
|
+
"""
|
|
1244
|
+
if deg is not None and deg not in self.grading_group():
|
|
1245
|
+
raise ValueError('degree is not an element of the grading group')
|
|
1246
|
+
|
|
1247
|
+
if base_ring is None:
|
|
1248
|
+
base_ring = self.base_ring()
|
|
1249
|
+
if not (base_ring.is_field() or base_ring is ZZ):
|
|
1250
|
+
raise NotImplementedError('can only compute homology if the base ring is the integers or a field')
|
|
1251
|
+
|
|
1252
|
+
if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp']:
|
|
1253
|
+
raise NotImplementedError('algorithm not recognized')
|
|
1254
|
+
|
|
1255
|
+
if deg is None:
|
|
1256
|
+
deg = self.nonzero_degrees()
|
|
1257
|
+
if isinstance(deg, (list, tuple)):
|
|
1258
|
+
answer = {}
|
|
1259
|
+
for deg in self.nonzero_degrees():
|
|
1260
|
+
answer[deg] = self._homology_in_degree(deg, base_ring, verbose, generators, algorithm)
|
|
1261
|
+
return answer
|
|
1262
|
+
else:
|
|
1263
|
+
return self._homology_in_degree(deg, base_ring, verbose, generators, algorithm)
|
|
1264
|
+
|
|
1265
|
+
def _homology_in_degree(self, deg, base_ring, verbose, generators, algorithm):
|
|
1266
|
+
"""
|
|
1267
|
+
Helper method for :meth:`homology`.
|
|
1268
|
+
|
|
1269
|
+
EXAMPLES::
|
|
1270
|
+
|
|
1271
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1272
|
+
sage: C.homology(1) == C._homology_in_degree(1, ZZ, False, False, 'auto') # needs sage.libs.pari
|
|
1273
|
+
True
|
|
1274
|
+
"""
|
|
1275
|
+
if deg not in self.nonzero_degrees():
|
|
1276
|
+
zero_homology = HomologyGroup(0, base_ring)
|
|
1277
|
+
if generators:
|
|
1278
|
+
return (zero_homology, vector(base_ring, []))
|
|
1279
|
+
else:
|
|
1280
|
+
return zero_homology
|
|
1281
|
+
if verbose:
|
|
1282
|
+
print('Computing homology of the chain complex in dimension %s...' % deg)
|
|
1283
|
+
|
|
1284
|
+
fraction_field = base_ring.fraction_field()
|
|
1285
|
+
|
|
1286
|
+
def change_ring(X):
|
|
1287
|
+
if X.base_ring() is base_ring:
|
|
1288
|
+
return X
|
|
1289
|
+
return X.change_ring(base_ring)
|
|
1290
|
+
|
|
1291
|
+
# d_out is the differential going out of degree deg,
|
|
1292
|
+
# d_in is the differential entering degree deg
|
|
1293
|
+
differential = self.degree_of_differential()
|
|
1294
|
+
d_in = change_ring(self.differential(deg - differential))
|
|
1295
|
+
d_out = change_ring(self.differential(deg))
|
|
1296
|
+
d_out_rank = self.rank(deg, ring=fraction_field)
|
|
1297
|
+
d_out_nullity = d_out.ncols() - d_out_rank
|
|
1298
|
+
|
|
1299
|
+
if d_in.is_zero():
|
|
1300
|
+
if generators: # Include the generators of the nullspace
|
|
1301
|
+
return [(HomologyGroup(1, base_ring), self({deg: gen}))
|
|
1302
|
+
for gen in d_out.right_kernel().basis()]
|
|
1303
|
+
else:
|
|
1304
|
+
return HomologyGroup(d_out_nullity, base_ring)
|
|
1305
|
+
|
|
1306
|
+
if generators:
|
|
1307
|
+
orders, gens = self._homology_generators_snf(d_in, d_out, d_out_rank)
|
|
1308
|
+
answer = [(HomologyGroup(1, base_ring, [order]), self({deg: gen}))
|
|
1309
|
+
for order, gen in zip(orders, gens)]
|
|
1310
|
+
else:
|
|
1311
|
+
if base_ring.is_field():
|
|
1312
|
+
d_in_rank = self.rank(deg-differential, ring=base_ring)
|
|
1313
|
+
answer = HomologyGroup(d_out_nullity - d_in_rank, base_ring)
|
|
1314
|
+
elif base_ring == ZZ:
|
|
1315
|
+
if d_in.ncols() == 0:
|
|
1316
|
+
all_divs = [0] * d_out_nullity
|
|
1317
|
+
else:
|
|
1318
|
+
if algorithm in ['auto', 'no_chomp']:
|
|
1319
|
+
if ((d_in.ncols() > 300 and d_in.nrows() > 300)
|
|
1320
|
+
or (min(d_in.ncols(), d_in.nrows()) > 100 and
|
|
1321
|
+
d_in.ncols() + d_in.nrows() > 600)):
|
|
1322
|
+
algorithm = 'dhsw'
|
|
1323
|
+
else:
|
|
1324
|
+
algorithm = 'pari'
|
|
1325
|
+
if algorithm == 'dhsw':
|
|
1326
|
+
from sage.homology.matrix_utils import dhsw_snf
|
|
1327
|
+
all_divs = dhsw_snf(d_in, verbose=verbose)
|
|
1328
|
+
elif algorithm == 'pari':
|
|
1329
|
+
all_divs = d_in.elementary_divisors(algorithm)
|
|
1330
|
+
else:
|
|
1331
|
+
raise ValueError('unsupported algorithm')
|
|
1332
|
+
all_divs = all_divs[:d_out_nullity]
|
|
1333
|
+
# divisors equal to 1 produce trivial
|
|
1334
|
+
# summands, so filter them out
|
|
1335
|
+
divisors = [x for x in all_divs if x != 1]
|
|
1336
|
+
answer = HomologyGroup(len(divisors), base_ring, divisors)
|
|
1337
|
+
else:
|
|
1338
|
+
raise NotImplementedError('only base rings ZZ and fields are supported')
|
|
1339
|
+
return answer
|
|
1340
|
+
|
|
1341
|
+
def _homology_generators_snf(self, d_in, d_out, d_out_rank):
|
|
1342
|
+
"""
|
|
1343
|
+
Compute the homology generators using the Smith normal form.
|
|
1344
|
+
|
|
1345
|
+
EXAMPLES::
|
|
1346
|
+
|
|
1347
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1348
|
+
sage: C.homology(1) # needs sage.libs.pari
|
|
1349
|
+
Z x C3
|
|
1350
|
+
sage: C._homology_generators_snf(C.differential(0), C.differential(1), 0) # needs sage.libs.pari
|
|
1351
|
+
([3, 0], [(1, 0), (0, 1)])
|
|
1352
|
+
"""
|
|
1353
|
+
# Find the kernel of the out-going differential.
|
|
1354
|
+
K = d_out.right_kernel().matrix().transpose().change_ring(d_out.base_ring())
|
|
1355
|
+
|
|
1356
|
+
# Compute the induced map to the kernel
|
|
1357
|
+
S = K.augment(d_in).hermite_form()
|
|
1358
|
+
d_in_induced = S.submatrix(row=0, nrows=d_in.nrows()-d_out_rank,
|
|
1359
|
+
col=d_in.nrows()-d_out_rank, ncols=d_in.ncols())
|
|
1360
|
+
|
|
1361
|
+
# Find the SNF of the induced matrix and appropriate generators
|
|
1362
|
+
(N, P, Q) = d_in_induced.smith_form()
|
|
1363
|
+
all_divs = [0]*N.nrows()
|
|
1364
|
+
non_triv = 0
|
|
1365
|
+
for i in range(0, N.nrows()):
|
|
1366
|
+
if i >= N.ncols():
|
|
1367
|
+
break
|
|
1368
|
+
all_divs[i] = N[i][i]
|
|
1369
|
+
if N[i][i] == 1:
|
|
1370
|
+
non_triv = non_triv + 1
|
|
1371
|
+
divisors = [x for x in all_divs if x != 1]
|
|
1372
|
+
gens = (K * P.inverse().submatrix(col=non_triv)).columns()
|
|
1373
|
+
return divisors, gens
|
|
1374
|
+
|
|
1375
|
+
def betti(self, deg=None, base_ring=None):
|
|
1376
|
+
"""
|
|
1377
|
+
The Betti number of the chain complex.
|
|
1378
|
+
|
|
1379
|
+
That is, write the homology in this degree as a direct sum
|
|
1380
|
+
of a free module and a torsion module; the Betti number is the
|
|
1381
|
+
rank of the free summand.
|
|
1382
|
+
|
|
1383
|
+
INPUT:
|
|
1384
|
+
|
|
1385
|
+
- ``deg`` -- an element of the grading group for the chain
|
|
1386
|
+
complex or ``None`` (default: ``None``); if ``None``,
|
|
1387
|
+
then return every Betti number, as a dictionary indexed by
|
|
1388
|
+
degree, or if an element of the grading group, then return
|
|
1389
|
+
the Betti number in that degree
|
|
1390
|
+
|
|
1391
|
+
- ``base_ring`` -- a commutative ring (default: the
|
|
1392
|
+
base ring for the chain complex); compute homology with
|
|
1393
|
+
these coefficients -- must be either the integers or a
|
|
1394
|
+
field
|
|
1395
|
+
|
|
1396
|
+
OUTPUT:
|
|
1397
|
+
|
|
1398
|
+
The Betti number in degree ``deg`` -- the rank of the free
|
|
1399
|
+
part of the homology module in this degree.
|
|
1400
|
+
|
|
1401
|
+
EXAMPLES::
|
|
1402
|
+
|
|
1403
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1404
|
+
sage: C.betti(0)
|
|
1405
|
+
2
|
|
1406
|
+
sage: [C.betti(n) for n in range(5)]
|
|
1407
|
+
[2, 1, 0, 0, 0]
|
|
1408
|
+
sage: C.betti()
|
|
1409
|
+
{0: 2, 1: 1}
|
|
1410
|
+
|
|
1411
|
+
sage: D = ChainComplex({0: matrix(GF(5), [[3, 1],[1, 2]])})
|
|
1412
|
+
sage: D.betti()
|
|
1413
|
+
{0: 1, 1: 1}
|
|
1414
|
+
"""
|
|
1415
|
+
if base_ring is None:
|
|
1416
|
+
base_ring = self.base_ring()
|
|
1417
|
+
try:
|
|
1418
|
+
base_ring = base_ring.fraction_field()
|
|
1419
|
+
except AttributeError:
|
|
1420
|
+
raise NotImplementedError('only implemented if the base ring is ZZ or a field')
|
|
1421
|
+
H = self.homology(deg, base_ring=base_ring)
|
|
1422
|
+
if isinstance(H, dict):
|
|
1423
|
+
return {deg: homology_group.dimension()
|
|
1424
|
+
for deg, homology_group in H.items()}
|
|
1425
|
+
else:
|
|
1426
|
+
return H.dimension()
|
|
1427
|
+
|
|
1428
|
+
def torsion_list(self, max_prime, min_prime=2):
|
|
1429
|
+
r"""
|
|
1430
|
+
Look for torsion in this chain complex by computing its mod `p`
|
|
1431
|
+
homology for a range of primes `p`.
|
|
1432
|
+
|
|
1433
|
+
INPUT:
|
|
1434
|
+
|
|
1435
|
+
- ``max_prime`` -- prime number; search for torsion mod `p` for
|
|
1436
|
+
all `p` strictly less than this number
|
|
1437
|
+
|
|
1438
|
+
- ``min_prime`` -- prime (default: 2); search for
|
|
1439
|
+
torsion mod `p` for primes at least as big as this
|
|
1440
|
+
|
|
1441
|
+
Return a list of pairs `(p, d)` where `p` is a prime at which
|
|
1442
|
+
there is torsion and `d` is a list of dimensions in which this
|
|
1443
|
+
torsion occurs.
|
|
1444
|
+
|
|
1445
|
+
The base ring for the chain complex must be the integers; if
|
|
1446
|
+
not, an error is raised.
|
|
1447
|
+
|
|
1448
|
+
ALGORITHM:
|
|
1449
|
+
|
|
1450
|
+
Let `C` denote the chain complex. Let `P` equal
|
|
1451
|
+
``max_prime``. Compute the mod `P` homology of `C`, and use
|
|
1452
|
+
this as the base-line computation: the assumption is that this
|
|
1453
|
+
is isomorphic to the integral homology tensored with
|
|
1454
|
+
`\GF{P}`. Then compute the mod `p` homology for a range of
|
|
1455
|
+
primes `p`, and record whenever the answer differs from the
|
|
1456
|
+
base-line answer.
|
|
1457
|
+
|
|
1458
|
+
EXAMPLES::
|
|
1459
|
+
|
|
1460
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1461
|
+
sage: C.homology() # needs sage.libs.pari
|
|
1462
|
+
{0: Z x Z, 1: Z x C3}
|
|
1463
|
+
sage: C.torsion_list(11) # needs sage.rings.finite_rings
|
|
1464
|
+
[(3, [1])]
|
|
1465
|
+
sage: C = ChainComplex([matrix(ZZ, 1, 1, [2]), matrix(ZZ, 1, 1), matrix(1, 1, [3])])
|
|
1466
|
+
sage: C.homology(1) # needs sage.libs.pari
|
|
1467
|
+
C2
|
|
1468
|
+
sage: C.homology(3) # needs sage.libs.pari
|
|
1469
|
+
C3
|
|
1470
|
+
sage: C.torsion_list(5) # needs sage.rings.finite_rings
|
|
1471
|
+
[(2, [1]), (3, [3])]
|
|
1472
|
+
"""
|
|
1473
|
+
if self.base_ring() != ZZ:
|
|
1474
|
+
raise NotImplementedError('only implemented for base ring the integers')
|
|
1475
|
+
|
|
1476
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
|
|
1477
|
+
|
|
1478
|
+
answer = []
|
|
1479
|
+
torsion_free = self.betti(base_ring=GF(max_prime))
|
|
1480
|
+
for p in prime_range(min_prime, max_prime):
|
|
1481
|
+
mod_p_betti = self.betti(base_ring=GF(p))
|
|
1482
|
+
if mod_p_betti != torsion_free:
|
|
1483
|
+
diff_dict = {}
|
|
1484
|
+
temp_diff = {}
|
|
1485
|
+
D = self.degree_of_differential()
|
|
1486
|
+
for i in torsion_free:
|
|
1487
|
+
temp_diff[i] = mod_p_betti.get(i, 0) - torsion_free[i]
|
|
1488
|
+
for i in temp_diff:
|
|
1489
|
+
if temp_diff[i] > 0:
|
|
1490
|
+
if i+D in diff_dict:
|
|
1491
|
+
lower = diff_dict[i+D]
|
|
1492
|
+
else:
|
|
1493
|
+
lower = 0
|
|
1494
|
+
current = temp_diff[i]
|
|
1495
|
+
if current > lower:
|
|
1496
|
+
diff_dict[i] = current - lower
|
|
1497
|
+
if i-D in diff_dict:
|
|
1498
|
+
diff_dict[i-D] -= current - lower
|
|
1499
|
+
differences = [i for i, di in diff_dict.items() if di != 0]
|
|
1500
|
+
answer.append((p, differences))
|
|
1501
|
+
return answer
|
|
1502
|
+
|
|
1503
|
+
def _Hom_(self, other, category=None):
|
|
1504
|
+
"""
|
|
1505
|
+
Return the set of chain maps between chain complexes ``self``
|
|
1506
|
+
and ``other``.
|
|
1507
|
+
|
|
1508
|
+
EXAMPLES::
|
|
1509
|
+
|
|
1510
|
+
sage: # needs sage.graphs
|
|
1511
|
+
sage: S = simplicial_complexes.Sphere(2)
|
|
1512
|
+
sage: T = simplicial_complexes.Torus()
|
|
1513
|
+
sage: C = S.chain_complex(augmented=True, cochain=True)
|
|
1514
|
+
sage: D = T.chain_complex(augmented=True, cochain=True)
|
|
1515
|
+
sage: Hom(C, D) # indirect doctest
|
|
1516
|
+
Set of Morphisms from Chain complex with at most 4 nonzero terms over
|
|
1517
|
+
Integer Ring to Chain complex with at most 4 nonzero terms over Integer
|
|
1518
|
+
Ring in Category of chain complexes over Integer Ring
|
|
1519
|
+
"""
|
|
1520
|
+
from sage.homology.chain_complex_homspace import ChainComplexHomspace
|
|
1521
|
+
return ChainComplexHomspace(self, other)
|
|
1522
|
+
|
|
1523
|
+
def _flip_(self):
|
|
1524
|
+
"""
|
|
1525
|
+
Flip chain complex upside down (degree `n` gets changed to
|
|
1526
|
+
degree `-n`), thus turning a chain complex into a cochain complex
|
|
1527
|
+
without changing the homology (except for flipping it, too).
|
|
1528
|
+
|
|
1529
|
+
EXAMPLES::
|
|
1530
|
+
|
|
1531
|
+
sage: C = ChainComplex({2: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1532
|
+
sage: C.degree_of_differential()
|
|
1533
|
+
1
|
|
1534
|
+
sage: C.differential(2)
|
|
1535
|
+
[3 0 0]
|
|
1536
|
+
[0 0 0]
|
|
1537
|
+
sage: C._flip_().degree_of_differential()
|
|
1538
|
+
-1
|
|
1539
|
+
sage: C._flip_().differential(-2)
|
|
1540
|
+
[3 0 0]
|
|
1541
|
+
[0 0 0]
|
|
1542
|
+
"""
|
|
1543
|
+
data = {}
|
|
1544
|
+
deg = self.degree_of_differential()
|
|
1545
|
+
for d in self.differential():
|
|
1546
|
+
data[-d] = self.differential()[d]
|
|
1547
|
+
return ChainComplex(data, degree=-deg)
|
|
1548
|
+
|
|
1549
|
+
def shift(self, n=1):
|
|
1550
|
+
"""
|
|
1551
|
+
Shift this chain complex `n` times.
|
|
1552
|
+
|
|
1553
|
+
INPUT:
|
|
1554
|
+
|
|
1555
|
+
- ``n`` -- integer (default: 1)
|
|
1556
|
+
|
|
1557
|
+
The *shift* operation is also sometimes called *translation* or
|
|
1558
|
+
*suspension*.
|
|
1559
|
+
|
|
1560
|
+
To shift a chain complex by `n`, shift its entries up by `n`
|
|
1561
|
+
(if it is a chain complex) or down by `n` (if it is a cochain
|
|
1562
|
+
complex); that is, shifting by 1 always shifts in the opposite
|
|
1563
|
+
direction of the differential. In symbols, if `C` is a chain
|
|
1564
|
+
complex and `C[n]` is its `n`-th shift, then `C[n]_j =
|
|
1565
|
+
C_{j-n}`. The differential in the shift `C[n]` is obtained by
|
|
1566
|
+
multiplying each differential in `C` by `(-1)^n`.
|
|
1567
|
+
|
|
1568
|
+
Caveat: different sources use different conventions for
|
|
1569
|
+
shifting: what we call `C[n]` might be called `C[-n]` in some
|
|
1570
|
+
places. See for example.
|
|
1571
|
+
https://ncatlab.org/nlab/show/suspension+of+a+chain+complex
|
|
1572
|
+
(which uses `C[n]` as we do but acknowledges `C[-n]`) or 1.2.8
|
|
1573
|
+
in [Wei1994]_ (which uses `C[-n]`).
|
|
1574
|
+
|
|
1575
|
+
EXAMPLES::
|
|
1576
|
+
|
|
1577
|
+
sage: # needs sage.graphs
|
|
1578
|
+
sage: S1 = simplicial_complexes.Sphere(1).chain_complex()
|
|
1579
|
+
sage: S1.shift(1).differential(2) == -S1.differential(1)
|
|
1580
|
+
True
|
|
1581
|
+
sage: S1.shift(2).differential(3) == S1.differential(1)
|
|
1582
|
+
True
|
|
1583
|
+
sage: S1.shift(3).homology(4)
|
|
1584
|
+
Z
|
|
1585
|
+
|
|
1586
|
+
For cochain complexes, shifting goes in the other
|
|
1587
|
+
direction. Topologically, this makes sense if we grade the
|
|
1588
|
+
cochain complex for a space negatively::
|
|
1589
|
+
|
|
1590
|
+
sage: # needs sage.graphs
|
|
1591
|
+
sage: T = simplicial_complexes.Torus()
|
|
1592
|
+
sage: co_T = T.chain_complex()._flip_()
|
|
1593
|
+
sage: co_T.homology()
|
|
1594
|
+
{-2: Z, -1: Z x Z, 0: Z}
|
|
1595
|
+
sage: co_T.degree_of_differential()
|
|
1596
|
+
1
|
|
1597
|
+
sage: co_T.shift(2).homology()
|
|
1598
|
+
{-4: Z, -3: Z x Z, -2: Z}
|
|
1599
|
+
|
|
1600
|
+
You can achieve the same result by tensoring (on the left, to
|
|
1601
|
+
get the signs right) with a rank one free module in degree
|
|
1602
|
+
``-n * deg``, if ``deg`` is the degree of the differential::
|
|
1603
|
+
|
|
1604
|
+
sage: C = ChainComplex({-2: matrix(ZZ, 0, 1)})
|
|
1605
|
+
sage: C.tensor(co_T).homology() # needs sage.graphs
|
|
1606
|
+
{-4: Z, -3: Z x Z, -2: Z}
|
|
1607
|
+
"""
|
|
1608
|
+
deg = self.degree_of_differential()
|
|
1609
|
+
shift = n * deg
|
|
1610
|
+
sgn = (-1)**n
|
|
1611
|
+
return ChainComplex({k-shift: sgn * self._diff[k] for k in self._diff},
|
|
1612
|
+
degree_of_differential=deg)
|
|
1613
|
+
|
|
1614
|
+
def _repr_(self):
|
|
1615
|
+
"""
|
|
1616
|
+
Print representation.
|
|
1617
|
+
|
|
1618
|
+
EXAMPLES::
|
|
1619
|
+
|
|
1620
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1621
|
+
sage: C
|
|
1622
|
+
Chain complex with at most 2 nonzero terms over Integer Ring
|
|
1623
|
+
"""
|
|
1624
|
+
diffs = [mat for mat in self._diff.values() if mat.nrows() + mat.ncols() > 0]
|
|
1625
|
+
if len(diffs) == 0:
|
|
1626
|
+
s = 'Trivial chain complex'
|
|
1627
|
+
else:
|
|
1628
|
+
s = 'Chain complex with at most {0} nonzero terms'.format(len(diffs)-1)
|
|
1629
|
+
s += ' over {0}'.format(self.base_ring())
|
|
1630
|
+
return s
|
|
1631
|
+
|
|
1632
|
+
def _ascii_art_(self):
|
|
1633
|
+
"""
|
|
1634
|
+
Return an ascii art representation.
|
|
1635
|
+
|
|
1636
|
+
Note that arrows go to the left so that composition of
|
|
1637
|
+
differentials is the usual matrix multiplication.
|
|
1638
|
+
|
|
1639
|
+
EXAMPLES::
|
|
1640
|
+
|
|
1641
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1:zero_matrix(1,2)})
|
|
1642
|
+
sage: ascii_art(C)
|
|
1643
|
+
[3 0 0]
|
|
1644
|
+
[0 0] [0 0 0]
|
|
1645
|
+
0 <-- C_2 <------ C_1 <-------- C_0 <-- 0
|
|
1646
|
+
|
|
1647
|
+
sage: one = matrix(ZZ, [[1]])
|
|
1648
|
+
sage: D = ChainComplex({0: one, 2: one, 6:one})
|
|
1649
|
+
sage: ascii_art(D)
|
|
1650
|
+
[1] [1] [0] [1]
|
|
1651
|
+
0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0
|
|
1652
|
+
sage: ascii_art(ChainComplex(base_ring=ZZ))
|
|
1653
|
+
0
|
|
1654
|
+
"""
|
|
1655
|
+
from sage.typeset.ascii_art import AsciiArt
|
|
1656
|
+
|
|
1657
|
+
def arrow_art(n):
|
|
1658
|
+
d_n = self.differential(n)
|
|
1659
|
+
if d_n.nrows() == 0 or d_n.ncols() == 0:
|
|
1660
|
+
return AsciiArt(['<--'])
|
|
1661
|
+
d_str = [' '+line+' ' for line in str(d_n).splitlines()]
|
|
1662
|
+
arrow = '<' + '-'*(len(d_str[0])-1)
|
|
1663
|
+
d_str.append(arrow)
|
|
1664
|
+
return AsciiArt(d_str)
|
|
1665
|
+
|
|
1666
|
+
def module_art(n):
|
|
1667
|
+
C_n = self.free_module(n)
|
|
1668
|
+
if C_n.rank() == 0:
|
|
1669
|
+
return AsciiArt([' 0 '])
|
|
1670
|
+
else:
|
|
1671
|
+
return AsciiArt([' C_{0} '.format(n)])
|
|
1672
|
+
|
|
1673
|
+
result = []
|
|
1674
|
+
for ordered in self.ordered_degrees():
|
|
1675
|
+
ordered = list(reversed(ordered))
|
|
1676
|
+
if len(ordered) == 0:
|
|
1677
|
+
return AsciiArt(['0'])
|
|
1678
|
+
result_ordered = module_art(ordered[0] + self.degree_of_differential())
|
|
1679
|
+
for n in ordered:
|
|
1680
|
+
result_ordered += arrow_art(n) + module_art(n)
|
|
1681
|
+
result = [result_ordered] + result
|
|
1682
|
+
if len(result) == 0:
|
|
1683
|
+
return AsciiArt(['0'])
|
|
1684
|
+
concatenated = result[0]
|
|
1685
|
+
for r in result[1:]:
|
|
1686
|
+
concatenated += AsciiArt([' ... ']) + r
|
|
1687
|
+
return concatenated
|
|
1688
|
+
|
|
1689
|
+
def _unicode_art_(self):
|
|
1690
|
+
"""
|
|
1691
|
+
Return a unicode art representation.
|
|
1692
|
+
|
|
1693
|
+
Note that arrows go to the left so that composition of
|
|
1694
|
+
differentials is the usual matrix multiplication.
|
|
1695
|
+
|
|
1696
|
+
EXAMPLES::
|
|
1697
|
+
|
|
1698
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1:zero_matrix(1,2)})
|
|
1699
|
+
sage: unicode_art(C)
|
|
1700
|
+
⎛3 0 0⎞
|
|
1701
|
+
(0 0) ⎝0 0 0⎠
|
|
1702
|
+
0 <── C_2 <──── C_1 <────── C_0 <── 0
|
|
1703
|
+
|
|
1704
|
+
sage: one = matrix(ZZ, [[1]])
|
|
1705
|
+
sage: D = ChainComplex({0: one, 2: one, 6:one})
|
|
1706
|
+
sage: unicode_art(D)
|
|
1707
|
+
(1) (1) (0) (1)
|
|
1708
|
+
0 <── C_7 <── C_6 <── 0 ... 0 <── C_3 <── C_2 <── C_1 <── C_0 <── 0
|
|
1709
|
+
|
|
1710
|
+
TESTS:
|
|
1711
|
+
|
|
1712
|
+
check that :issue:`37678` is fixed::
|
|
1713
|
+
|
|
1714
|
+
sage: C = ChainComplex(base_ring=ZZ)
|
|
1715
|
+
sage: unicode_art(C)
|
|
1716
|
+
0
|
|
1717
|
+
"""
|
|
1718
|
+
from sage.typeset.unicode_art import UnicodeArt
|
|
1719
|
+
|
|
1720
|
+
def arrow_art(n):
|
|
1721
|
+
d_n = self.differential(n)
|
|
1722
|
+
if not d_n.nrows() or not d_n.ncols():
|
|
1723
|
+
return UnicodeArt(['<──'])
|
|
1724
|
+
d_str = list(d_n._unicode_art_())
|
|
1725
|
+
arrow = '<' + '─' * (len(d_str[0]) - 1)
|
|
1726
|
+
d_str.append(arrow)
|
|
1727
|
+
return UnicodeArt(d_str)
|
|
1728
|
+
|
|
1729
|
+
def module_art(n):
|
|
1730
|
+
C_n = self.free_module(n)
|
|
1731
|
+
if not C_n.rank():
|
|
1732
|
+
return UnicodeArt([' 0 '])
|
|
1733
|
+
else:
|
|
1734
|
+
return UnicodeArt([' C_{0} '.format(n)])
|
|
1735
|
+
|
|
1736
|
+
result = []
|
|
1737
|
+
for ordered in self.ordered_degrees():
|
|
1738
|
+
ordered = list(reversed(ordered))
|
|
1739
|
+
if not ordered:
|
|
1740
|
+
return UnicodeArt(['0'])
|
|
1741
|
+
result_ordered = module_art(ordered[0] + self.degree_of_differential())
|
|
1742
|
+
for n in ordered:
|
|
1743
|
+
result_ordered += arrow_art(n) + module_art(n)
|
|
1744
|
+
result = [result_ordered] + result
|
|
1745
|
+
if len(result) == 0:
|
|
1746
|
+
return UnicodeArt(['0'])
|
|
1747
|
+
concatenated = result[0]
|
|
1748
|
+
for r in result[1:]:
|
|
1749
|
+
concatenated += UnicodeArt([' ... ']) + r
|
|
1750
|
+
return concatenated
|
|
1751
|
+
|
|
1752
|
+
def _latex_(self):
|
|
1753
|
+
"""
|
|
1754
|
+
LaTeX print representation.
|
|
1755
|
+
|
|
1756
|
+
EXAMPLES::
|
|
1757
|
+
|
|
1758
|
+
sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])})
|
|
1759
|
+
sage: C._latex_()
|
|
1760
|
+
'\\Bold{Z}^{3} \\xrightarrow{d_{0}} \\Bold{Z}^{2}'
|
|
1761
|
+
|
|
1762
|
+
sage: ChainComplex()._latex_()
|
|
1763
|
+
'0'
|
|
1764
|
+
|
|
1765
|
+
sage: G = AdditiveAbelianGroup([0, 0])
|
|
1766
|
+
sage: m = matrix([0])
|
|
1767
|
+
sage: C = ChainComplex(grading_group=G, degree=G(vector([1,2])), data={G.zero(): m}) # needs sage.libs.pari
|
|
1768
|
+
sage: C._latex_() # needs sage.libs.pari
|
|
1769
|
+
'\\Bold{Z}^{1} \\xrightarrow{d_{\\text{\\texttt{(0,{ }0)}}}} \\Bold{Z}^{1}'
|
|
1770
|
+
"""
|
|
1771
|
+
# Warning: this is likely to screw up if, for example, the
|
|
1772
|
+
# degree of the differential is 2 and there are nonzero terms
|
|
1773
|
+
# in consecutive dimensions (e.g., in dimensions 0 and 1). In
|
|
1774
|
+
# such cases, the representation might show a differential
|
|
1775
|
+
# connecting these terms, although the differential goes from
|
|
1776
|
+
# dimension 0 to dimension 2, and from dimension 1 to
|
|
1777
|
+
# dimension 3, etc. I don't know how much effort should be
|
|
1778
|
+
# put into trying to fix this.
|
|
1779
|
+
string = ""
|
|
1780
|
+
diffs = self._diff
|
|
1781
|
+
if len(diffs) == 0:
|
|
1782
|
+
return "0"
|
|
1783
|
+
deg = self.degree_of_differential()
|
|
1784
|
+
ring = self.base_ring()
|
|
1785
|
+
backwards = bool(deg < 0)
|
|
1786
|
+
sorted_list = sorted(diffs.keys(), reverse=backwards)
|
|
1787
|
+
if len(diffs) <= 6:
|
|
1788
|
+
for n in sorted_list[1:-1]:
|
|
1789
|
+
mat = diffs[n]
|
|
1790
|
+
string += _latex_module(ring, mat.ncols())
|
|
1791
|
+
string += " \\xrightarrow{d_{%s}} " % latex(n)
|
|
1792
|
+
mat = diffs[sorted_list[-1]]
|
|
1793
|
+
string += _latex_module(ring, mat.ncols())
|
|
1794
|
+
else:
|
|
1795
|
+
for n in sorted_list[:2]:
|
|
1796
|
+
mat = diffs[n]
|
|
1797
|
+
string += _latex_module(ring, mat.ncols())
|
|
1798
|
+
string += " \\xrightarrow{d_{%s}} " % latex(n)
|
|
1799
|
+
string += "\\dots "
|
|
1800
|
+
n = sorted_list[-2]
|
|
1801
|
+
string += "\\xrightarrow{d_{%s}} " % latex(n)
|
|
1802
|
+
mat = diffs[sorted_list[-1]]
|
|
1803
|
+
string += _latex_module(ring, mat.ncols())
|
|
1804
|
+
return string
|
|
1805
|
+
|
|
1806
|
+
def cartesian_product(self, *factors, **kwds):
|
|
1807
|
+
r"""
|
|
1808
|
+
Return the direct sum (Cartesian product) of ``self`` with ``D``.
|
|
1809
|
+
|
|
1810
|
+
Let `C` and `D` be two chain complexes with differentials
|
|
1811
|
+
`\partial_C` and `\partial_D`, respectively, of the same degree (so
|
|
1812
|
+
they must also have the same grading group).
|
|
1813
|
+
The direct sum `S = C \oplus D` is a chain complex given by
|
|
1814
|
+
`S_i = C_i \oplus D_i` with differential
|
|
1815
|
+
`\partial = \partial_C \oplus \partial_D`.
|
|
1816
|
+
|
|
1817
|
+
INPUT:
|
|
1818
|
+
|
|
1819
|
+
- ``subdivide`` -- boolean (default: ``False``); whether to subdivide the
|
|
1820
|
+
the differential matrices
|
|
1821
|
+
|
|
1822
|
+
EXAMPLES::
|
|
1823
|
+
|
|
1824
|
+
sage: R.<x,y> = QQ[]
|
|
1825
|
+
sage: C = ChainComplex([matrix([[-y],[x]]), matrix([[x, y]])])
|
|
1826
|
+
sage: D = ChainComplex([matrix([[x-y]]), matrix([[0], [0]])])
|
|
1827
|
+
sage: ascii_art(C.cartesian_product(D))
|
|
1828
|
+
[x y 0] [ -y 0]
|
|
1829
|
+
[0 0 0] [ x 0]
|
|
1830
|
+
[0 0 0] [ 0 x - y]
|
|
1831
|
+
0 <-- C_2 <-------- C_1 <-------------- C_0 <-- 0
|
|
1832
|
+
|
|
1833
|
+
sage: D = ChainComplex({1:matrix([[x-y]]), 4:matrix([[x], [y]])})
|
|
1834
|
+
sage: ascii_art(D)
|
|
1835
|
+
[x]
|
|
1836
|
+
[y] [x - y]
|
|
1837
|
+
0 <-- C_5 <---- C_4 <-- 0 <-- C_2 <-------- C_1 <-- 0
|
|
1838
|
+
sage: ascii_art(cartesian_product([C, D]))
|
|
1839
|
+
[-y]
|
|
1840
|
+
[x] [ x y 0] [ x]
|
|
1841
|
+
[y] [ 0 0 x - y] [ 0]
|
|
1842
|
+
0 <-- C_5 <---- C_4 <-- 0 <-- C_2 <-------------------- C_1 <----- C_0 <-- 0
|
|
1843
|
+
|
|
1844
|
+
The degrees of the differentials must agree::
|
|
1845
|
+
|
|
1846
|
+
sage: C = ChainComplex({1:matrix([[x]])}, degree_of_differential=-1)
|
|
1847
|
+
sage: D = ChainComplex({1:matrix([[x]])}, degree_of_differential=1)
|
|
1848
|
+
sage: C.cartesian_product(D)
|
|
1849
|
+
Traceback (most recent call last):
|
|
1850
|
+
...
|
|
1851
|
+
ValueError: the degrees of the differentials must match
|
|
1852
|
+
|
|
1853
|
+
TESTS::
|
|
1854
|
+
|
|
1855
|
+
sage: C = ChainComplex({2:matrix([[-1],[2]]), 1:matrix([[2, 1]])},
|
|
1856
|
+
....: degree_of_differential=-1)
|
|
1857
|
+
sage: ascii_art(C.cartesian_product(C, subdivide=True))
|
|
1858
|
+
[-1| 0]
|
|
1859
|
+
[ 2| 0]
|
|
1860
|
+
[2 1|0 0] [--+--]
|
|
1861
|
+
[---+---] [ 0|-1]
|
|
1862
|
+
[0 0|2 1] [ 0| 2]
|
|
1863
|
+
0 <-- C_0 <---------- C_1 <-------- C_2 <-- 0
|
|
1864
|
+
|
|
1865
|
+
::
|
|
1866
|
+
|
|
1867
|
+
sage: R.<x,y,z> = QQ[]
|
|
1868
|
+
sage: C1 = ChainComplex({1:matrix([[x]])})
|
|
1869
|
+
sage: C2 = ChainComplex({1:matrix([[y]])})
|
|
1870
|
+
sage: C3 = ChainComplex({1:matrix([[z]])})
|
|
1871
|
+
sage: ascii_art(cartesian_product([C1, C2, C3]))
|
|
1872
|
+
[x 0 0]
|
|
1873
|
+
[0 y 0]
|
|
1874
|
+
[0 0 z]
|
|
1875
|
+
0 <-- C_2 <-------- C_1 <-- 0
|
|
1876
|
+
sage: ascii_art(C1.cartesian_product([C2, C3], subdivide=True))
|
|
1877
|
+
[x|0|0]
|
|
1878
|
+
[-+-+-]
|
|
1879
|
+
[0|y|0]
|
|
1880
|
+
[-+-+-]
|
|
1881
|
+
[0|0|z]
|
|
1882
|
+
0 <-- C_2 <-------- C_1 <-- 0
|
|
1883
|
+
|
|
1884
|
+
::
|
|
1885
|
+
|
|
1886
|
+
sage: # needs sage.libs.pari
|
|
1887
|
+
sage: R.<x> = ZZ[]
|
|
1888
|
+
sage: G = AdditiveAbelianGroup([0,7])
|
|
1889
|
+
sage: d = {G(vector([1,1])):matrix([[x]])}
|
|
1890
|
+
sage: C = ChainComplex(d, grading_group=G, degree=G(vector([2,1])))
|
|
1891
|
+
sage: ascii_art(C.cartesian_product(C))
|
|
1892
|
+
[x 0]
|
|
1893
|
+
[0 x]
|
|
1894
|
+
0 <-- C_(3, 2) <------ C_(1, 1) <-- 0
|
|
1895
|
+
"""
|
|
1896
|
+
if not factors:
|
|
1897
|
+
return self
|
|
1898
|
+
if isinstance(factors[0], (list, tuple)):
|
|
1899
|
+
factors = factors[0]
|
|
1900
|
+
deg_diff = self.degree_of_differential()
|
|
1901
|
+
if any(D.degree_of_differential() != deg_diff for D in factors):
|
|
1902
|
+
raise ValueError("the degrees of the differentials must match")
|
|
1903
|
+
if any(D.grading_group() != self._grading_group for D in factors):
|
|
1904
|
+
raise ValueError("the grading groups must match")
|
|
1905
|
+
|
|
1906
|
+
factors = [self] + list(factors)
|
|
1907
|
+
R = self.base_ring()
|
|
1908
|
+
zero = matrix(R, [])
|
|
1909
|
+
subdivide = kwds.get('subdivide', False)
|
|
1910
|
+
diffs = [D.differential() for D in factors]
|
|
1911
|
+
keys = reduce(lambda X, d: X.union(d.keys()), diffs, set())
|
|
1912
|
+
ret = {k: matrix.block_diagonal([d.get(k, zero) for d in diffs],
|
|
1913
|
+
subdivide=subdivide)
|
|
1914
|
+
for k in keys}
|
|
1915
|
+
return ChainComplex(ret, degree_of_differential=deg_diff,
|
|
1916
|
+
grading_group=self._grading_group)
|
|
1917
|
+
|
|
1918
|
+
def tensor(self, *factors, **kwds):
|
|
1919
|
+
r"""
|
|
1920
|
+
Return the tensor product of ``self`` with ``D``.
|
|
1921
|
+
|
|
1922
|
+
Let `C` and `D` be two chain complexes with differentials
|
|
1923
|
+
`\partial_C` and `\partial_D`, respectively, of the same degree (so
|
|
1924
|
+
they must also have the same grading group).
|
|
1925
|
+
The tensor product `S = C \otimes D` is a chain complex given by
|
|
1926
|
+
|
|
1927
|
+
.. MATH::
|
|
1928
|
+
|
|
1929
|
+
S_i = \bigoplus_{a+b=i} C_a \otimes D_b
|
|
1930
|
+
|
|
1931
|
+
with differential
|
|
1932
|
+
|
|
1933
|
+
.. MATH::
|
|
1934
|
+
|
|
1935
|
+
\partial(x \otimes y) = \partial_C x \otimes y
|
|
1936
|
+
+ (-1)^{|a| \cdot |\partial_D|} x \otimes \partial_D y
|
|
1937
|
+
|
|
1938
|
+
for `x \in C_a` and `y \in D_b`, where `|a|` is the degree of `a` and
|
|
1939
|
+
`|\partial_D|` is the degree of `\partial_D`.
|
|
1940
|
+
|
|
1941
|
+
.. WARNING::
|
|
1942
|
+
|
|
1943
|
+
If the degree of the differential is even, then this may not
|
|
1944
|
+
result in a valid chain complex.
|
|
1945
|
+
|
|
1946
|
+
INPUT:
|
|
1947
|
+
|
|
1948
|
+
- ``subdivide`` -- boolean (default: ``False``); whether to subdivide the
|
|
1949
|
+
the differential matrices
|
|
1950
|
+
|
|
1951
|
+
.. TODO::
|
|
1952
|
+
|
|
1953
|
+
Make subdivision work correctly on multiple factors.
|
|
1954
|
+
|
|
1955
|
+
EXAMPLES::
|
|
1956
|
+
|
|
1957
|
+
sage: R.<x,y,z> = QQ[]
|
|
1958
|
+
sage: C1 = ChainComplex({1:matrix([[x]])}, degree_of_differential=-1)
|
|
1959
|
+
sage: C2 = ChainComplex({1:matrix([[y]])}, degree_of_differential=-1)
|
|
1960
|
+
sage: C3 = ChainComplex({1:matrix([[z]])}, degree_of_differential=-1)
|
|
1961
|
+
sage: ascii_art(C1.tensor(C2))
|
|
1962
|
+
[ x]
|
|
1963
|
+
[y x] [-y]
|
|
1964
|
+
0 <-- C_0 <------ C_1 <----- C_2 <-- 0
|
|
1965
|
+
sage: ascii_art(C1.tensor(C2).tensor(C3))
|
|
1966
|
+
[ y x 0] [ x]
|
|
1967
|
+
[-z 0 x] [-y]
|
|
1968
|
+
[z y x] [ 0 -z -y] [ z]
|
|
1969
|
+
0 <-- C_0 <-------- C_1 <----------- C_2 <----- C_3 <-- 0
|
|
1970
|
+
|
|
1971
|
+
::
|
|
1972
|
+
|
|
1973
|
+
sage: C = ChainComplex({2:matrix([[-y],[x]]), 1:matrix([[x, y]])},
|
|
1974
|
+
....: degree_of_differential=-1); ascii_art(C)
|
|
1975
|
+
[-y]
|
|
1976
|
+
[x y] [ x]
|
|
1977
|
+
0 <-- C_0 <------ C_1 <----- C_2 <-- 0
|
|
1978
|
+
sage: T = C.tensor(C)
|
|
1979
|
+
sage: T.differential(1)
|
|
1980
|
+
[x y x y]
|
|
1981
|
+
sage: T.differential(2)
|
|
1982
|
+
[-y x 0 y 0 0]
|
|
1983
|
+
[ x 0 x 0 y 0]
|
|
1984
|
+
[ 0 -x -y 0 0 -y]
|
|
1985
|
+
[ 0 0 0 -x -y x]
|
|
1986
|
+
sage: T.differential(3)
|
|
1987
|
+
[ x y 0 0]
|
|
1988
|
+
[ y 0 -y 0]
|
|
1989
|
+
[-x 0 0 -y]
|
|
1990
|
+
[ 0 y x 0]
|
|
1991
|
+
[ 0 -x 0 x]
|
|
1992
|
+
[ 0 0 x y]
|
|
1993
|
+
sage: T.differential(4)
|
|
1994
|
+
[-y]
|
|
1995
|
+
[ x]
|
|
1996
|
+
[-y]
|
|
1997
|
+
[ x]
|
|
1998
|
+
|
|
1999
|
+
The degrees of the differentials must agree::
|
|
2000
|
+
|
|
2001
|
+
sage: C1p = ChainComplex({1:matrix([[x]])}, degree_of_differential=1)
|
|
2002
|
+
sage: C1.tensor(C1p)
|
|
2003
|
+
Traceback (most recent call last):
|
|
2004
|
+
...
|
|
2005
|
+
ValueError: the degrees of the differentials must match
|
|
2006
|
+
|
|
2007
|
+
TESTS::
|
|
2008
|
+
|
|
2009
|
+
sage: R.<x,y,z> = QQ[]
|
|
2010
|
+
sage: C1 = ChainComplex({1:matrix([[x]])})
|
|
2011
|
+
sage: C2 = ChainComplex({1:matrix([[y]])})
|
|
2012
|
+
sage: C3 = ChainComplex({1:matrix([[z]])})
|
|
2013
|
+
sage: ascii_art(tensor([C1, C2, C3]))
|
|
2014
|
+
[-y -z 0] [ z]
|
|
2015
|
+
[ x 0 -z] [-y]
|
|
2016
|
+
[x y z] [ 0 x y] [ x]
|
|
2017
|
+
0 <-- C_6 <-------- C_5 <----------- C_4 <----- C_3 <-- 0
|
|
2018
|
+
|
|
2019
|
+
::
|
|
2020
|
+
|
|
2021
|
+
sage: # needs sage.libs.pari
|
|
2022
|
+
sage: R.<x,y> = ZZ[]
|
|
2023
|
+
sage: G = AdditiveAbelianGroup([0,7])
|
|
2024
|
+
sage: d1 = {G(vector([1,1])):matrix([[x]])}
|
|
2025
|
+
sage: C1 = ChainComplex(d1, grading_group=G, degree=G(vector([2,1])))
|
|
2026
|
+
sage: d2 = {G(vector([3,0])):matrix([[y]])}
|
|
2027
|
+
sage: C2 = ChainComplex(d2, grading_group=G, degree=G(vector([2,1])))
|
|
2028
|
+
sage: ascii_art(C1.tensor(C2))
|
|
2029
|
+
[y]
|
|
2030
|
+
[ x -y] [x]
|
|
2031
|
+
0 <-- C_(8, 3) <-------- C_(6, 2) <---- C_(4, 1) <-- 0
|
|
2032
|
+
|
|
2033
|
+
Check that :issue:`21760` is fixed::
|
|
2034
|
+
|
|
2035
|
+
sage: C = ChainComplex({0: matrix(ZZ, 0, 2)}, degree=-1)
|
|
2036
|
+
sage: ascii_art(C)
|
|
2037
|
+
0 <-- C_0 <-- 0
|
|
2038
|
+
sage: T = C.tensor(C)
|
|
2039
|
+
sage: ascii_art(T)
|
|
2040
|
+
0 <-- C_0 <-- 0
|
|
2041
|
+
sage: T.free_module_rank(0)
|
|
2042
|
+
4
|
|
2043
|
+
"""
|
|
2044
|
+
if not factors:
|
|
2045
|
+
return self
|
|
2046
|
+
if isinstance(factors[0], (list, tuple)):
|
|
2047
|
+
factors = factors[0]
|
|
2048
|
+
deg_diff = self.degree_of_differential()
|
|
2049
|
+
if any(D.degree_of_differential() != deg_diff for D in factors):
|
|
2050
|
+
raise ValueError("the degrees of the differentials must match")
|
|
2051
|
+
if any(D.grading_group() != self._grading_group for D in factors):
|
|
2052
|
+
raise ValueError("the grading groups must match")
|
|
2053
|
+
|
|
2054
|
+
R = self.base_ring()
|
|
2055
|
+
zero = R.zero()
|
|
2056
|
+
subdivide = kwds.get('subdivide', False)
|
|
2057
|
+
ret = self
|
|
2058
|
+
|
|
2059
|
+
if self._grading_group is ZZ:
|
|
2060
|
+
def scalar(a):
|
|
2061
|
+
return (-1)**(a * deg_diff)
|
|
2062
|
+
else:
|
|
2063
|
+
def scalar(a):
|
|
2064
|
+
return (-1)**(sum(a) * sum(deg_diff))
|
|
2065
|
+
|
|
2066
|
+
for D in factors:
|
|
2067
|
+
# Setup
|
|
2068
|
+
d = ret.differential()
|
|
2069
|
+
dD = D.differential()
|
|
2070
|
+
deg = sorted((k, ret.free_module_rank(k)) for k in d
|
|
2071
|
+
if ret.free_module_rank(k) > 0)
|
|
2072
|
+
degD = sorted((k, D.free_module_rank(k)) for k in dD
|
|
2073
|
+
if D.free_module_rank(k) > 0)
|
|
2074
|
+
diff = {}
|
|
2075
|
+
|
|
2076
|
+
# Our choice for tensor products will be x # y = x1 * y + x2 * y + ...
|
|
2077
|
+
|
|
2078
|
+
# Generate the data for the differential
|
|
2079
|
+
for a, r in deg:
|
|
2080
|
+
for b, s in degD:
|
|
2081
|
+
rp = d[a].nrows()
|
|
2082
|
+
sp = dD[b].nrows()
|
|
2083
|
+
if a+b not in diff:
|
|
2084
|
+
diff[a+b] = {}
|
|
2085
|
+
mor = diff[a+b]
|
|
2086
|
+
cur = {}
|
|
2087
|
+
cur[(a+deg_diff, b)] = []
|
|
2088
|
+
cur[(a, b+deg_diff)] = []
|
|
2089
|
+
|
|
2090
|
+
for i in range(r):
|
|
2091
|
+
for j in range(s):
|
|
2092
|
+
# \partial x_i \otimes y_j
|
|
2093
|
+
vec = [zero]*(rp*s)
|
|
2094
|
+
for k, val in enumerate(d[a].column(i)):
|
|
2095
|
+
vec[s*k+j] += val
|
|
2096
|
+
cur[(a+deg_diff, b)].append(vec)
|
|
2097
|
+
|
|
2098
|
+
# (-1)^a x_i \otimes \partial y_j
|
|
2099
|
+
vec = [zero]*(r*sp)
|
|
2100
|
+
for k, val in enumerate(dD[b].column(j)):
|
|
2101
|
+
vec[sp*i+k] += scalar(a) * val
|
|
2102
|
+
cur[(a, b+deg_diff)].append(vec)
|
|
2103
|
+
|
|
2104
|
+
mor[a, b] = cur
|
|
2105
|
+
|
|
2106
|
+
# Parse the data into matrices
|
|
2107
|
+
to_delete = []
|
|
2108
|
+
for k in diff:
|
|
2109
|
+
# Get the data and interchange the indices
|
|
2110
|
+
mor = diff[k]
|
|
2111
|
+
row_keys = sorted(mor.keys())
|
|
2112
|
+
cols = {}
|
|
2113
|
+
col_widths = {}
|
|
2114
|
+
for dom in mor:
|
|
2115
|
+
c = mor[dom]
|
|
2116
|
+
for im in c:
|
|
2117
|
+
if im not in cols:
|
|
2118
|
+
cols[im] = {}
|
|
2119
|
+
col_widths[im] = len(c[im])
|
|
2120
|
+
cols[im][dom] = c[im]
|
|
2121
|
+
col_keys = sorted(cols.keys())
|
|
2122
|
+
# Now build the matrix
|
|
2123
|
+
M = []
|
|
2124
|
+
for ck in col_keys:
|
|
2125
|
+
M.append([])
|
|
2126
|
+
col = cols[ck]
|
|
2127
|
+
for rk in row_keys:
|
|
2128
|
+
if rk in col:
|
|
2129
|
+
M[-1].append(matrix(R, col[rk]).transpose())
|
|
2130
|
+
else:
|
|
2131
|
+
M[-1].append(zero)
|
|
2132
|
+
diff[k] = matrix.block(M, subdivide=subdivide)
|
|
2133
|
+
|
|
2134
|
+
# Flag for removal any 0x0 matrices
|
|
2135
|
+
if diff[k].nrows() == 0 and diff[k].ncols() == 0:
|
|
2136
|
+
to_delete.append(k)
|
|
2137
|
+
|
|
2138
|
+
# Delete the 0x0 matrices
|
|
2139
|
+
for k in to_delete:
|
|
2140
|
+
del diff[k]
|
|
2141
|
+
|
|
2142
|
+
ret = ChainComplex(diff, degree_of_differential=deg_diff,
|
|
2143
|
+
grading_group=self._grading_group)
|
|
2144
|
+
|
|
2145
|
+
return ret
|
|
2146
|
+
|
|
2147
|
+
|
|
2148
|
+
register_unpickle_override('sage.homology.chain_complex', 'ChainComplex', ChainComplex_class)
|