passagemath-categories 10.6.32__cp314-cp314t-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_categories-10.6.32.dist-info/METADATA +156 -0
- passagemath_categories-10.6.32.dist-info/RECORD +719 -0
- passagemath_categories-10.6.32.dist-info/WHEEL +5 -0
- passagemath_categories-10.6.32.dist-info/top_level.txt +2 -0
- passagemath_categories.libs/libgcc_s-2d945d6c.so.1 +0 -0
- passagemath_categories.libs/libgmp-28992bcb.so.10.5.0 +0 -0
- passagemath_categories.libs/libstdc++-85f2cd6d.so.6.0.33 +0 -0
- sage/all__sagemath_categories.py +28 -0
- sage/arith/all.py +38 -0
- sage/arith/constants.pxd +27 -0
- sage/arith/functions.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/arith/functions.pxd +4 -0
- sage/arith/functions.pyx +221 -0
- sage/arith/misc.py +6552 -0
- sage/arith/multi_modular.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/arith/multi_modular.pxd +39 -0
- sage/arith/multi_modular.pyx +994 -0
- sage/arith/rational_reconstruction.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/arith/rational_reconstruction.pxd +4 -0
- sage/arith/rational_reconstruction.pyx +115 -0
- sage/arith/srange.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/arith/srange.pyx +571 -0
- sage/calculus/all__sagemath_categories.py +2 -0
- sage/calculus/functional.py +481 -0
- sage/calculus/functions.py +151 -0
- sage/categories/additive_groups.py +73 -0
- sage/categories/additive_magmas.py +1044 -0
- sage/categories/additive_monoids.py +114 -0
- sage/categories/additive_semigroups.py +184 -0
- sage/categories/affine_weyl_groups.py +238 -0
- sage/categories/algebra_ideals.py +95 -0
- sage/categories/algebra_modules.py +96 -0
- sage/categories/algebras.py +349 -0
- sage/categories/algebras_with_basis.py +377 -0
- sage/categories/all.py +160 -0
- sage/categories/aperiodic_semigroups.py +29 -0
- sage/categories/associative_algebras.py +47 -0
- sage/categories/bialgebras.py +101 -0
- sage/categories/bialgebras_with_basis.py +414 -0
- sage/categories/bimodules.py +206 -0
- sage/categories/chain_complexes.py +268 -0
- sage/categories/classical_crystals.py +480 -0
- sage/categories/coalgebras.py +405 -0
- sage/categories/coalgebras_with_basis.py +232 -0
- sage/categories/coercion_methods.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/categories/coercion_methods.pyx +52 -0
- sage/categories/commutative_additive_groups.py +104 -0
- sage/categories/commutative_additive_monoids.py +45 -0
- sage/categories/commutative_additive_semigroups.py +48 -0
- sage/categories/commutative_algebra_ideals.py +87 -0
- sage/categories/commutative_algebras.py +94 -0
- sage/categories/commutative_ring_ideals.py +58 -0
- sage/categories/commutative_rings.py +736 -0
- sage/categories/complete_discrete_valuation.py +293 -0
- sage/categories/complex_reflection_groups.py +145 -0
- sage/categories/complex_reflection_or_generalized_coxeter_groups.py +1249 -0
- sage/categories/coxeter_group_algebras.py +186 -0
- sage/categories/coxeter_groups.py +3402 -0
- sage/categories/crystals.py +2628 -0
- sage/categories/cw_complexes.py +216 -0
- sage/categories/dedekind_domains.py +137 -0
- sage/categories/discrete_valuation.py +325 -0
- sage/categories/distributive_magmas_and_additive_magmas.py +100 -0
- sage/categories/division_rings.py +114 -0
- sage/categories/domains.py +95 -0
- sage/categories/drinfeld_modules.py +789 -0
- sage/categories/dual.py +42 -0
- sage/categories/enumerated_sets.py +1146 -0
- sage/categories/euclidean_domains.py +271 -0
- sage/categories/examples/algebras_with_basis.py +102 -0
- sage/categories/examples/all.py +1 -0
- sage/categories/examples/commutative_additive_monoids.py +130 -0
- sage/categories/examples/commutative_additive_semigroups.py +199 -0
- sage/categories/examples/coxeter_groups.py +8 -0
- sage/categories/examples/crystals.py +236 -0
- sage/categories/examples/cw_complexes.py +163 -0
- sage/categories/examples/facade_sets.py +187 -0
- sage/categories/examples/filtered_algebras_with_basis.py +204 -0
- sage/categories/examples/filtered_modules_with_basis.py +154 -0
- sage/categories/examples/finite_coxeter_groups.py +252 -0
- sage/categories/examples/finite_dimensional_algebras_with_basis.py +148 -0
- sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py +495 -0
- sage/categories/examples/finite_enumerated_sets.py +208 -0
- sage/categories/examples/finite_monoids.py +150 -0
- sage/categories/examples/finite_semigroups.py +190 -0
- sage/categories/examples/finite_weyl_groups.py +191 -0
- sage/categories/examples/graded_connected_hopf_algebras_with_basis.py +152 -0
- sage/categories/examples/graded_modules_with_basis.py +168 -0
- sage/categories/examples/graphs.py +122 -0
- sage/categories/examples/hopf_algebras_with_basis.py +145 -0
- sage/categories/examples/infinite_enumerated_sets.py +190 -0
- sage/categories/examples/lie_algebras.py +352 -0
- sage/categories/examples/lie_algebras_with_basis.py +196 -0
- sage/categories/examples/magmas.py +162 -0
- sage/categories/examples/manifolds.py +94 -0
- sage/categories/examples/monoids.py +144 -0
- sage/categories/examples/posets.py +178 -0
- sage/categories/examples/semigroups.py +580 -0
- sage/categories/examples/semigroups_cython.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/categories/examples/semigroups_cython.pyx +221 -0
- sage/categories/examples/semirings.py +249 -0
- sage/categories/examples/sets_cat.py +706 -0
- sage/categories/examples/sets_with_grading.py +101 -0
- sage/categories/examples/with_realizations.py +542 -0
- sage/categories/fields.py +991 -0
- sage/categories/filtered_algebras.py +63 -0
- sage/categories/filtered_algebras_with_basis.py +548 -0
- sage/categories/filtered_hopf_algebras_with_basis.py +138 -0
- sage/categories/filtered_modules.py +210 -0
- sage/categories/filtered_modules_with_basis.py +1209 -0
- sage/categories/finite_complex_reflection_groups.py +1506 -0
- sage/categories/finite_coxeter_groups.py +1138 -0
- sage/categories/finite_crystals.py +103 -0
- sage/categories/finite_dimensional_algebras_with_basis.py +1860 -0
- sage/categories/finite_dimensional_bialgebras_with_basis.py +33 -0
- sage/categories/finite_dimensional_coalgebras_with_basis.py +33 -0
- sage/categories/finite_dimensional_graded_lie_algebras_with_basis.py +231 -0
- sage/categories/finite_dimensional_hopf_algebras_with_basis.py +38 -0
- sage/categories/finite_dimensional_lie_algebras_with_basis.py +2774 -0
- sage/categories/finite_dimensional_modules_with_basis.py +1407 -0
- sage/categories/finite_dimensional_nilpotent_lie_algebras_with_basis.py +167 -0
- sage/categories/finite_dimensional_semisimple_algebras_with_basis.py +270 -0
- sage/categories/finite_enumerated_sets.py +769 -0
- sage/categories/finite_fields.py +252 -0
- sage/categories/finite_groups.py +256 -0
- sage/categories/finite_lattice_posets.py +242 -0
- sage/categories/finite_monoids.py +316 -0
- sage/categories/finite_permutation_groups.py +339 -0
- sage/categories/finite_posets.py +1994 -0
- sage/categories/finite_semigroups.py +136 -0
- sage/categories/finite_sets.py +93 -0
- sage/categories/finite_weyl_groups.py +39 -0
- sage/categories/finitely_generated_lambda_bracket_algebras.py +112 -0
- sage/categories/finitely_generated_lie_conformal_algebras.py +114 -0
- sage/categories/finitely_generated_magmas.py +57 -0
- sage/categories/finitely_generated_semigroups.py +214 -0
- sage/categories/function_fields.py +76 -0
- sage/categories/g_sets.py +77 -0
- sage/categories/gcd_domains.py +65 -0
- sage/categories/generalized_coxeter_groups.py +94 -0
- sage/categories/graded_algebras.py +85 -0
- sage/categories/graded_algebras_with_basis.py +258 -0
- sage/categories/graded_bialgebras.py +32 -0
- sage/categories/graded_bialgebras_with_basis.py +32 -0
- sage/categories/graded_coalgebras.py +65 -0
- sage/categories/graded_coalgebras_with_basis.py +51 -0
- sage/categories/graded_hopf_algebras.py +41 -0
- sage/categories/graded_hopf_algebras_with_basis.py +169 -0
- sage/categories/graded_lie_algebras.py +91 -0
- sage/categories/graded_lie_algebras_with_basis.py +44 -0
- sage/categories/graded_lie_conformal_algebras.py +74 -0
- sage/categories/graded_modules.py +133 -0
- sage/categories/graded_modules_with_basis.py +329 -0
- sage/categories/graphs.py +138 -0
- sage/categories/group_algebras.py +430 -0
- sage/categories/groupoid.py +94 -0
- sage/categories/groups.py +667 -0
- sage/categories/h_trivial_semigroups.py +64 -0
- sage/categories/hecke_modules.py +185 -0
- sage/categories/highest_weight_crystals.py +980 -0
- sage/categories/hopf_algebras.py +219 -0
- sage/categories/hopf_algebras_with_basis.py +309 -0
- sage/categories/infinite_enumerated_sets.py +115 -0
- sage/categories/integral_domains.py +203 -0
- sage/categories/j_trivial_semigroups.py +29 -0
- sage/categories/kac_moody_algebras.py +82 -0
- sage/categories/kahler_algebras.py +203 -0
- sage/categories/l_trivial_semigroups.py +63 -0
- sage/categories/lambda_bracket_algebras.py +280 -0
- sage/categories/lambda_bracket_algebras_with_basis.py +107 -0
- sage/categories/lattice_posets.py +89 -0
- sage/categories/left_modules.py +49 -0
- sage/categories/lie_algebras.py +1070 -0
- sage/categories/lie_algebras_with_basis.py +261 -0
- sage/categories/lie_conformal_algebras.py +350 -0
- sage/categories/lie_conformal_algebras_with_basis.py +147 -0
- sage/categories/lie_groups.py +73 -0
- sage/categories/loop_crystals.py +1290 -0
- sage/categories/magmas.py +1189 -0
- sage/categories/magmas_and_additive_magmas.py +149 -0
- sage/categories/magmatic_algebras.py +365 -0
- sage/categories/manifolds.py +352 -0
- sage/categories/matrix_algebras.py +40 -0
- sage/categories/metric_spaces.py +387 -0
- sage/categories/modular_abelian_varieties.py +78 -0
- sage/categories/modules.py +989 -0
- sage/categories/modules_with_basis.py +2794 -0
- sage/categories/monoid_algebras.py +38 -0
- sage/categories/monoids.py +739 -0
- sage/categories/noetherian_rings.py +87 -0
- sage/categories/number_fields.py +242 -0
- sage/categories/ore_modules.py +189 -0
- sage/categories/partially_ordered_monoids.py +49 -0
- sage/categories/permutation_groups.py +63 -0
- sage/categories/pointed_sets.py +42 -0
- sage/categories/polyhedra.py +74 -0
- sage/categories/poor_man_map.py +270 -0
- sage/categories/posets.py +722 -0
- sage/categories/principal_ideal_domains.py +270 -0
- sage/categories/quantum_group_representations.py +543 -0
- sage/categories/quotient_fields.py +728 -0
- sage/categories/r_trivial_semigroups.py +45 -0
- sage/categories/regular_crystals.py +898 -0
- sage/categories/regular_supercrystals.py +170 -0
- sage/categories/right_modules.py +49 -0
- sage/categories/ring_ideals.py +74 -0
- sage/categories/rings.py +1904 -0
- sage/categories/rngs.py +175 -0
- sage/categories/schemes.py +393 -0
- sage/categories/semigroups.py +1060 -0
- sage/categories/semirings.py +71 -0
- sage/categories/semisimple_algebras.py +114 -0
- sage/categories/sets_with_grading.py +235 -0
- sage/categories/shephard_groups.py +43 -0
- sage/categories/signed_tensor.py +120 -0
- sage/categories/simplicial_complexes.py +134 -0
- sage/categories/simplicial_sets.py +1206 -0
- sage/categories/super_algebras.py +149 -0
- sage/categories/super_algebras_with_basis.py +144 -0
- sage/categories/super_hopf_algebras_with_basis.py +126 -0
- sage/categories/super_lie_conformal_algebras.py +193 -0
- sage/categories/super_modules.py +229 -0
- sage/categories/super_modules_with_basis.py +193 -0
- sage/categories/supercommutative_algebras.py +99 -0
- sage/categories/supercrystals.py +406 -0
- sage/categories/tensor.py +110 -0
- sage/categories/topological_spaces.py +170 -0
- sage/categories/triangular_kac_moody_algebras.py +439 -0
- sage/categories/tutorial.py +58 -0
- sage/categories/unique_factorization_domains.py +318 -0
- sage/categories/unital_algebras.py +426 -0
- sage/categories/vector_bundles.py +159 -0
- sage/categories/vector_spaces.py +357 -0
- sage/categories/weyl_groups.py +853 -0
- sage/combinat/all__sagemath_categories.py +34 -0
- sage/combinat/backtrack.py +180 -0
- sage/combinat/combinat.py +2269 -0
- sage/combinat/combinat_cython.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/combinat_cython.pxd +6 -0
- sage/combinat/combinat_cython.pyx +390 -0
- sage/combinat/combination.py +796 -0
- sage/combinat/combinatorial_map.py +416 -0
- sage/combinat/composition.py +2192 -0
- sage/combinat/dlx.py +510 -0
- sage/combinat/integer_lists/__init__.py +7 -0
- sage/combinat/integer_lists/base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/integer_lists/base.pxd +16 -0
- sage/combinat/integer_lists/base.pyx +713 -0
- sage/combinat/integer_lists/invlex.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/integer_lists/invlex.pxd +4 -0
- sage/combinat/integer_lists/invlex.pyx +1650 -0
- sage/combinat/integer_lists/lists.py +328 -0
- sage/combinat/integer_lists/nn.py +48 -0
- sage/combinat/integer_vector.py +1818 -0
- sage/combinat/integer_vector_weighted.py +413 -0
- sage/combinat/matrices/all__sagemath_categories.py +5 -0
- sage/combinat/matrices/dancing_links.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/matrices/dancing_links.pyx +1159 -0
- sage/combinat/matrices/dancing_links_c.h +380 -0
- sage/combinat/matrices/dlxcpp.py +136 -0
- sage/combinat/partition.py +10070 -0
- sage/combinat/partitions.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/partitions.pyx +743 -0
- sage/combinat/permutation.py +10168 -0
- sage/combinat/permutation_cython.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/permutation_cython.pxd +11 -0
- sage/combinat/permutation_cython.pyx +407 -0
- sage/combinat/q_analogues.py +1090 -0
- sage/combinat/ranker.py +268 -0
- sage/combinat/subset.py +1561 -0
- sage/combinat/subsets_hereditary.py +202 -0
- sage/combinat/subsets_pairwise.py +184 -0
- sage/combinat/tools.py +63 -0
- sage/combinat/tuple.py +348 -0
- sage/data_structures/all.py +2 -0
- sage/data_structures/all__sagemath_categories.py +2 -0
- sage/data_structures/binary_matrix.pxd +138 -0
- sage/data_structures/binary_search.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/binary_search.pxd +3 -0
- sage/data_structures/binary_search.pyx +66 -0
- sage/data_structures/bitset.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/bitset.pxd +40 -0
- sage/data_structures/bitset.pyx +2385 -0
- sage/data_structures/bitset_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/bitset_base.pxd +926 -0
- sage/data_structures/bitset_base.pyx +117 -0
- sage/data_structures/bitset_intrinsics.h +487 -0
- sage/data_structures/blas_dict.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/blas_dict.pxd +12 -0
- sage/data_structures/blas_dict.pyx +469 -0
- sage/data_structures/list_of_pairs.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/list_of_pairs.pxd +16 -0
- sage/data_structures/list_of_pairs.pyx +122 -0
- sage/data_structures/mutable_poset.py +3312 -0
- sage/data_structures/pairing_heap.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/pairing_heap.h +346 -0
- sage/data_structures/pairing_heap.pxd +88 -0
- sage/data_structures/pairing_heap.pyx +1464 -0
- sage/data_structures/sparse_bitset.pxd +62 -0
- sage/data_structures/stream.py +5070 -0
- sage/databases/all__sagemath_categories.py +7 -0
- sage/databases/sql_db.py +2236 -0
- sage/ext/all__sagemath_categories.py +3 -0
- sage/ext/fast_callable.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/ext/fast_callable.pxd +4 -0
- sage/ext/fast_callable.pyx +2746 -0
- sage/ext/fast_eval.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/ext/fast_eval.pxd +1 -0
- sage/ext/fast_eval.pyx +102 -0
- sage/ext/interpreters/__init__.py +1 -0
- sage/ext/interpreters/all__sagemath_categories.py +2 -0
- sage/ext/interpreters/wrapper_el.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/ext/interpreters/wrapper_el.pxd +18 -0
- sage/ext/interpreters/wrapper_el.pyx +148 -0
- sage/ext/interpreters/wrapper_py.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/ext/interpreters/wrapper_py.pxd +17 -0
- sage/ext/interpreters/wrapper_py.pyx +133 -0
- sage/functions/airy.py +937 -0
- sage/functions/all.py +97 -0
- sage/functions/bessel.py +2102 -0
- sage/functions/error.py +784 -0
- sage/functions/exp_integral.py +1529 -0
- sage/functions/gamma.py +1087 -0
- sage/functions/generalized.py +672 -0
- sage/functions/hyperbolic.py +747 -0
- sage/functions/hypergeometric.py +1156 -0
- sage/functions/jacobi.py +1705 -0
- sage/functions/log.py +1402 -0
- sage/functions/min_max.py +338 -0
- sage/functions/orthogonal_polys.py +3106 -0
- sage/functions/other.py +2303 -0
- sage/functions/piecewise.py +1505 -0
- sage/functions/prime_pi.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/functions/prime_pi.pyx +262 -0
- sage/functions/special.py +1212 -0
- sage/functions/spike_function.py +278 -0
- sage/functions/transcendental.py +690 -0
- sage/functions/trig.py +1062 -0
- sage/functions/wigner.py +726 -0
- sage/geometry/abc.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/geometry/abc.pyx +82 -0
- sage/geometry/all__sagemath_categories.py +1 -0
- sage/groups/all__sagemath_categories.py +11 -0
- sage/groups/generic.py +1733 -0
- sage/groups/groups_catalog.py +113 -0
- sage/groups/perm_gps/all__sagemath_categories.py +1 -0
- sage/groups/perm_gps/partn_ref/all.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_categories.py +1 -0
- sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pxd +52 -0
- sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx +906 -0
- sage/groups/perm_gps/partn_ref/canonical_augmentation.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/canonical_augmentation.pxd +85 -0
- sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx +534 -0
- sage/groups/perm_gps/partn_ref/data_structures.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/data_structures.pxd +576 -0
- sage/groups/perm_gps/partn_ref/data_structures.pyx +1792 -0
- sage/groups/perm_gps/partn_ref/double_coset.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/double_coset.pxd +45 -0
- sage/groups/perm_gps/partn_ref/double_coset.pyx +739 -0
- sage/groups/perm_gps/partn_ref/refinement_lists.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_lists.pxd +18 -0
- sage/groups/perm_gps/partn_ref/refinement_lists.pyx +82 -0
- sage/groups/perm_gps/partn_ref/refinement_python.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_python.pxd +16 -0
- sage/groups/perm_gps/partn_ref/refinement_python.pyx +564 -0
- sage/groups/perm_gps/partn_ref/refinement_sets.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_sets.pxd +60 -0
- sage/groups/perm_gps/partn_ref/refinement_sets.pyx +858 -0
- sage/interfaces/abc.py +140 -0
- sage/interfaces/all.py +58 -0
- sage/interfaces/all__sagemath_categories.py +1 -0
- sage/interfaces/expect.py +1643 -0
- sage/interfaces/interface.py +1682 -0
- sage/interfaces/process.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/interfaces/process.pxd +5 -0
- sage/interfaces/process.pyx +288 -0
- sage/interfaces/quit.py +167 -0
- sage/interfaces/sage0.py +604 -0
- sage/interfaces/sagespawn.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/interfaces/sagespawn.pyx +308 -0
- sage/interfaces/tab_completion.py +101 -0
- sage/misc/all__sagemath_categories.py +78 -0
- sage/misc/allocator.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/allocator.pxd +6 -0
- sage/misc/allocator.pyx +47 -0
- sage/misc/binary_tree.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/binary_tree.pxd +29 -0
- sage/misc/binary_tree.pyx +537 -0
- sage/misc/callable_dict.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/callable_dict.pyx +89 -0
- sage/misc/citation.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/citation.pyx +159 -0
- sage/misc/converting_dict.py +293 -0
- sage/misc/defaults.py +129 -0
- sage/misc/derivative.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/derivative.pyx +223 -0
- sage/misc/functional.py +2005 -0
- sage/misc/html.py +589 -0
- sage/misc/latex.py +2673 -0
- sage/misc/latex_macros.py +236 -0
- sage/misc/latex_standalone.py +1833 -0
- sage/misc/map_threaded.py +38 -0
- sage/misc/mathml.py +76 -0
- sage/misc/method_decorator.py +88 -0
- sage/misc/mrange.py +755 -0
- sage/misc/multireplace.py +41 -0
- sage/misc/object_multiplexer.py +92 -0
- sage/misc/parser.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/parser.pyx +1107 -0
- sage/misc/random_testing.py +264 -0
- sage/misc/rest_index_of_methods.py +377 -0
- sage/misc/search.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/search.pxd +2 -0
- sage/misc/search.pyx +68 -0
- sage/misc/stopgap.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/stopgap.pyx +95 -0
- sage/misc/table.py +853 -0
- sage/monoids/all__sagemath_categories.py +1 -0
- sage/monoids/indexed_free_monoid.py +1071 -0
- sage/monoids/monoid.py +82 -0
- sage/numerical/all__sagemath_categories.py +1 -0
- sage/numerical/backends/all__sagemath_categories.py +1 -0
- sage/numerical/backends/generic_backend.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/generic_backend.pxd +61 -0
- sage/numerical/backends/generic_backend.pyx +1893 -0
- sage/numerical/backends/generic_sdp_backend.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/generic_sdp_backend.pxd +38 -0
- sage/numerical/backends/generic_sdp_backend.pyx +755 -0
- sage/parallel/all.py +6 -0
- sage/parallel/decorate.py +575 -0
- sage/parallel/map_reduce.py +1997 -0
- sage/parallel/multiprocessing_sage.py +76 -0
- sage/parallel/ncpus.py +35 -0
- sage/parallel/parallelism.py +364 -0
- sage/parallel/reference.py +47 -0
- sage/parallel/use_fork.py +333 -0
- sage/rings/abc.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/abc.pxd +31 -0
- sage/rings/abc.pyx +526 -0
- sage/rings/algebraic_closure_finite_field.py +1154 -0
- sage/rings/all__sagemath_categories.py +91 -0
- sage/rings/big_oh.py +227 -0
- sage/rings/continued_fraction.py +2754 -0
- sage/rings/continued_fraction_gosper.py +220 -0
- sage/rings/factorint.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/factorint.pyx +295 -0
- sage/rings/fast_arith.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/fast_arith.pxd +21 -0
- sage/rings/fast_arith.pyx +535 -0
- sage/rings/finite_rings/all__sagemath_categories.py +9 -0
- sage/rings/finite_rings/conway_polynomials.py +542 -0
- sage/rings/finite_rings/element_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/element_base.pxd +12 -0
- sage/rings/finite_rings/element_base.pyx +1176 -0
- sage/rings/finite_rings/finite_field_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/finite_field_base.pxd +7 -0
- sage/rings/finite_rings/finite_field_base.pyx +2171 -0
- sage/rings/finite_rings/finite_field_constructor.py +827 -0
- sage/rings/finite_rings/finite_field_prime_modn.py +372 -0
- sage/rings/finite_rings/galois_group.py +154 -0
- sage/rings/finite_rings/hom_finite_field.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/hom_finite_field.pxd +23 -0
- sage/rings/finite_rings/hom_finite_field.pyx +856 -0
- sage/rings/finite_rings/hom_prime_finite_field.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/hom_prime_finite_field.pxd +15 -0
- sage/rings/finite_rings/hom_prime_finite_field.pyx +164 -0
- sage/rings/finite_rings/homset.py +357 -0
- sage/rings/finite_rings/integer_mod.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/integer_mod.pxd +56 -0
- sage/rings/finite_rings/integer_mod.pyx +4586 -0
- sage/rings/finite_rings/integer_mod_limits.h +11 -0
- sage/rings/finite_rings/integer_mod_ring.py +2044 -0
- sage/rings/finite_rings/residue_field.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/residue_field.pxd +30 -0
- sage/rings/finite_rings/residue_field.pyx +1811 -0
- sage/rings/finite_rings/stdint.pxd +19 -0
- sage/rings/fraction_field.py +1452 -0
- sage/rings/fraction_field_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/fraction_field_element.pyx +1357 -0
- sage/rings/function_field/all.py +7 -0
- sage/rings/function_field/all__sagemath_categories.py +2 -0
- sage/rings/function_field/constructor.py +218 -0
- sage/rings/function_field/element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/function_field/element.pxd +11 -0
- sage/rings/function_field/element.pyx +1008 -0
- sage/rings/function_field/element_rational.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/function_field/element_rational.pyx +513 -0
- sage/rings/function_field/extensions.py +230 -0
- sage/rings/function_field/function_field.py +1468 -0
- sage/rings/function_field/function_field_rational.py +1005 -0
- sage/rings/function_field/ideal.py +1155 -0
- sage/rings/function_field/ideal_rational.py +629 -0
- sage/rings/function_field/jacobian_base.py +826 -0
- sage/rings/function_field/jacobian_hess.py +1053 -0
- sage/rings/function_field/jacobian_khuri_makdisi.py +1027 -0
- sage/rings/function_field/maps.py +1039 -0
- sage/rings/function_field/order.py +281 -0
- sage/rings/function_field/order_basis.py +586 -0
- sage/rings/function_field/order_rational.py +576 -0
- sage/rings/function_field/place.py +426 -0
- sage/rings/function_field/place_rational.py +181 -0
- sage/rings/generic.py +320 -0
- sage/rings/homset.py +332 -0
- sage/rings/ideal.py +1885 -0
- sage/rings/ideal_monoid.py +215 -0
- sage/rings/infinity.py +1890 -0
- sage/rings/integer.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/integer.pxd +45 -0
- sage/rings/integer.pyx +7874 -0
- sage/rings/integer_ring.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/integer_ring.pxd +8 -0
- sage/rings/integer_ring.pyx +1693 -0
- sage/rings/laurent_series_ring.py +931 -0
- sage/rings/laurent_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/laurent_series_ring_element.pxd +11 -0
- sage/rings/laurent_series_ring_element.pyx +1927 -0
- sage/rings/lazy_series.py +7815 -0
- sage/rings/lazy_series_ring.py +4356 -0
- sage/rings/localization.py +1043 -0
- sage/rings/morphism.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/morphism.pxd +39 -0
- sage/rings/morphism.pyx +3299 -0
- sage/rings/multi_power_series_ring.py +1145 -0
- sage/rings/multi_power_series_ring_element.py +2184 -0
- sage/rings/noncommutative_ideals.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/noncommutative_ideals.pyx +423 -0
- sage/rings/number_field/all__sagemath_categories.py +1 -0
- sage/rings/number_field/number_field_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/number_field/number_field_base.pxd +8 -0
- sage/rings/number_field/number_field_base.pyx +507 -0
- sage/rings/number_field/number_field_element_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/number_field/number_field_element_base.pxd +6 -0
- sage/rings/number_field/number_field_element_base.pyx +36 -0
- sage/rings/number_field/number_field_ideal.py +3550 -0
- sage/rings/padics/all__sagemath_categories.py +4 -0
- sage/rings/padics/local_generic.py +1670 -0
- sage/rings/padics/local_generic_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/padics/local_generic_element.pxd +5 -0
- sage/rings/padics/local_generic_element.pyx +1017 -0
- sage/rings/padics/misc.py +256 -0
- sage/rings/padics/padic_generic.py +1911 -0
- sage/rings/padics/pow_computer.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/padics/pow_computer.pxd +38 -0
- sage/rings/padics/pow_computer.pyx +671 -0
- sage/rings/padics/precision_error.py +24 -0
- sage/rings/polynomial/all__sagemath_categories.py +25 -0
- sage/rings/polynomial/commutative_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/commutative_polynomial.pxd +6 -0
- sage/rings/polynomial/commutative_polynomial.pyx +24 -0
- sage/rings/polynomial/cyclotomic.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/cyclotomic.pyx +404 -0
- sage/rings/polynomial/flatten.py +711 -0
- sage/rings/polynomial/ideal.py +102 -0
- sage/rings/polynomial/infinite_polynomial_element.py +1768 -0
- sage/rings/polynomial/infinite_polynomial_ring.py +1653 -0
- sage/rings/polynomial/laurent_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/laurent_polynomial.pxd +18 -0
- sage/rings/polynomial/laurent_polynomial.pyx +2190 -0
- sage/rings/polynomial/laurent_polynomial_ideal.py +590 -0
- sage/rings/polynomial/laurent_polynomial_ring.py +832 -0
- sage/rings/polynomial/laurent_polynomial_ring_base.py +708 -0
- sage/rings/polynomial/multi_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/multi_polynomial.pxd +12 -0
- sage/rings/polynomial/multi_polynomial.pyx +3082 -0
- sage/rings/polynomial/multi_polynomial_element.py +2570 -0
- sage/rings/polynomial/multi_polynomial_ideal.py +5771 -0
- sage/rings/polynomial/multi_polynomial_ring.py +947 -0
- sage/rings/polynomial/multi_polynomial_ring_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/multi_polynomial_ring_base.pxd +15 -0
- sage/rings/polynomial/multi_polynomial_ring_base.pyx +1855 -0
- sage/rings/polynomial/multi_polynomial_sequence.py +2204 -0
- sage/rings/polynomial/polydict.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/polydict.pxd +45 -0
- sage/rings/polynomial/polydict.pyx +2701 -0
- sage/rings/polynomial/polynomial_compiled.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/polynomial_compiled.pxd +59 -0
- sage/rings/polynomial/polynomial_compiled.pyx +509 -0
- sage/rings/polynomial/polynomial_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/polynomial_element.pxd +64 -0
- sage/rings/polynomial/polynomial_element.pyx +13255 -0
- sage/rings/polynomial/polynomial_element_generic.py +1637 -0
- sage/rings/polynomial/polynomial_fateman.py +97 -0
- sage/rings/polynomial/polynomial_quotient_ring.py +2465 -0
- sage/rings/polynomial/polynomial_quotient_ring_element.py +779 -0
- sage/rings/polynomial/polynomial_ring.py +3784 -0
- sage/rings/polynomial/polynomial_ring_constructor.py +1051 -0
- sage/rings/polynomial/polynomial_ring_homomorphism.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/polynomial_ring_homomorphism.pxd +5 -0
- sage/rings/polynomial/polynomial_ring_homomorphism.pyx +121 -0
- sage/rings/polynomial/polynomial_singular_interface.py +549 -0
- sage/rings/polynomial/symmetric_ideal.py +989 -0
- sage/rings/polynomial/symmetric_reduction.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/symmetric_reduction.pxd +8 -0
- sage/rings/polynomial/symmetric_reduction.pyx +669 -0
- sage/rings/polynomial/term_order.py +2279 -0
- sage/rings/polynomial/toy_buchberger.py +449 -0
- sage/rings/polynomial/toy_d_basis.py +387 -0
- sage/rings/polynomial/toy_variety.py +362 -0
- sage/rings/power_series_mpoly.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/power_series_mpoly.pxd +9 -0
- sage/rings/power_series_mpoly.pyx +161 -0
- sage/rings/power_series_poly.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/power_series_poly.pxd +10 -0
- sage/rings/power_series_poly.pyx +1317 -0
- sage/rings/power_series_ring.py +1441 -0
- sage/rings/power_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/power_series_ring_element.pxd +12 -0
- sage/rings/power_series_ring_element.pyx +3028 -0
- sage/rings/puiseux_series_ring.py +487 -0
- sage/rings/puiseux_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/puiseux_series_ring_element.pxd +7 -0
- sage/rings/puiseux_series_ring_element.pyx +1055 -0
- sage/rings/qqbar_decorators.py +167 -0
- sage/rings/quotient_ring.py +1598 -0
- sage/rings/quotient_ring_element.py +979 -0
- sage/rings/rational.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/rational.pxd +20 -0
- sage/rings/rational.pyx +4284 -0
- sage/rings/rational_field.py +1730 -0
- sage/rings/real_double.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/real_double.pxd +16 -0
- sage/rings/real_double.pyx +2218 -0
- sage/rings/real_lazy.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/real_lazy.pxd +30 -0
- sage/rings/real_lazy.pyx +1773 -0
- sage/rings/ring.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/ring.pxd +30 -0
- sage/rings/ring.pyx +850 -0
- sage/rings/semirings/all.py +3 -0
- sage/rings/semirings/non_negative_integer_semiring.py +107 -0
- sage/rings/semirings/tropical_mpolynomial.py +972 -0
- sage/rings/semirings/tropical_polynomial.py +997 -0
- sage/rings/semirings/tropical_semiring.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/semirings/tropical_semiring.pyx +676 -0
- sage/rings/semirings/tropical_variety.py +1701 -0
- sage/rings/sum_of_squares.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/sum_of_squares.pxd +3 -0
- sage/rings/sum_of_squares.pyx +336 -0
- sage/rings/tests.py +504 -0
- sage/schemes/affine/affine_homset.py +508 -0
- sage/schemes/affine/affine_morphism.py +1574 -0
- sage/schemes/affine/affine_point.py +460 -0
- sage/schemes/affine/affine_rational_point.py +308 -0
- sage/schemes/affine/affine_space.py +1264 -0
- sage/schemes/affine/affine_subscheme.py +592 -0
- sage/schemes/affine/all.py +25 -0
- sage/schemes/all__sagemath_categories.py +5 -0
- sage/schemes/generic/algebraic_scheme.py +2092 -0
- sage/schemes/generic/all.py +5 -0
- sage/schemes/generic/ambient_space.py +400 -0
- sage/schemes/generic/divisor.py +465 -0
- sage/schemes/generic/divisor_group.py +313 -0
- sage/schemes/generic/glue.py +84 -0
- sage/schemes/generic/homset.py +820 -0
- sage/schemes/generic/hypersurface.py +234 -0
- sage/schemes/generic/morphism.py +2107 -0
- sage/schemes/generic/point.py +237 -0
- sage/schemes/generic/scheme.py +1190 -0
- sage/schemes/generic/spec.py +199 -0
- sage/schemes/product_projective/all.py +6 -0
- sage/schemes/product_projective/homset.py +236 -0
- sage/schemes/product_projective/morphism.py +517 -0
- sage/schemes/product_projective/point.py +568 -0
- sage/schemes/product_projective/rational_point.py +550 -0
- sage/schemes/product_projective/space.py +1301 -0
- sage/schemes/product_projective/subscheme.py +466 -0
- sage/schemes/projective/all.py +24 -0
- sage/schemes/projective/proj_bdd_height.py +453 -0
- sage/schemes/projective/projective_homset.py +718 -0
- sage/schemes/projective/projective_morphism.py +2792 -0
- sage/schemes/projective/projective_point.py +1484 -0
- sage/schemes/projective/projective_rational_point.py +569 -0
- sage/schemes/projective/projective_space.py +2571 -0
- sage/schemes/projective/projective_subscheme.py +1574 -0
- sage/sets/all.py +17 -0
- sage/sets/cartesian_product.py +376 -0
- sage/sets/condition_set.py +525 -0
- sage/sets/disjoint_set.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/sets/disjoint_set.pxd +36 -0
- sage/sets/disjoint_set.pyx +998 -0
- sage/sets/disjoint_union_enumerated_sets.py +625 -0
- sage/sets/family.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/sets/family.pxd +12 -0
- sage/sets/family.pyx +1556 -0
- sage/sets/finite_enumerated_set.py +406 -0
- sage/sets/finite_set_map_cy.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/sets/finite_set_map_cy.pxd +34 -0
- sage/sets/finite_set_map_cy.pyx +708 -0
- sage/sets/finite_set_maps.py +591 -0
- sage/sets/image_set.py +448 -0
- sage/sets/integer_range.py +829 -0
- sage/sets/non_negative_integers.py +241 -0
- sage/sets/positive_integers.py +93 -0
- sage/sets/primes.py +188 -0
- sage/sets/real_set.py +2760 -0
- sage/sets/recursively_enumerated_set.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/sets/recursively_enumerated_set.pxd +31 -0
- sage/sets/recursively_enumerated_set.pyx +2082 -0
- sage/sets/set.py +2083 -0
- sage/sets/set_from_iterator.py +1021 -0
- sage/sets/totally_ordered_finite_set.py +329 -0
- sage/symbolic/all__sagemath_categories.py +1 -0
- sage/symbolic/function.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/symbolic/function.pxd +29 -0
- sage/symbolic/function.pyx +1488 -0
- sage/symbolic/symbols.py +56 -0
- sage/tests/all__sagemath_categories.py +1 -0
- sage/tests/cython.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/tests/cython.pyx +37 -0
- sage/tests/stl_vector.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/tests/stl_vector.pyx +171 -0
- sage/typeset/all.py +6 -0
- sage/typeset/ascii_art.py +295 -0
- sage/typeset/character_art.py +789 -0
- sage/typeset/character_art_factory.py +572 -0
- sage/typeset/symbols.py +334 -0
- sage/typeset/unicode_art.py +183 -0
- sage/typeset/unicode_characters.py +101 -0
|
@@ -0,0 +1,1159 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-categories
|
|
2
|
+
# distutils: language = c++
|
|
3
|
+
"""
|
|
4
|
+
Dancing Links internal pyx code
|
|
5
|
+
|
|
6
|
+
EXAMPLES::
|
|
7
|
+
|
|
8
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
9
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
10
|
+
sage: x = dlx_solver(rows)
|
|
11
|
+
sage: x
|
|
12
|
+
Dancing links solver for 6 columns and 6 rows
|
|
13
|
+
|
|
14
|
+
The number of solutions::
|
|
15
|
+
|
|
16
|
+
sage: x.number_of_solutions()
|
|
17
|
+
3
|
|
18
|
+
|
|
19
|
+
Iterate over the solutions::
|
|
20
|
+
|
|
21
|
+
sage: sorted(map(sorted, x.solutions_iterator()))
|
|
22
|
+
[[0, 1], [2, 3], [4, 5]]
|
|
23
|
+
|
|
24
|
+
All solutions (computed in parallel)::
|
|
25
|
+
|
|
26
|
+
sage: sorted(map(sorted, x.all_solutions()))
|
|
27
|
+
[[0, 1], [2, 3], [4, 5]]
|
|
28
|
+
|
|
29
|
+
Return the first solution found when the computation is done in parallel::
|
|
30
|
+
|
|
31
|
+
sage: sorted(x.one_solution(ncpus=2)) # random
|
|
32
|
+
[0, 1]
|
|
33
|
+
|
|
34
|
+
Find all solutions using some specific rows::
|
|
35
|
+
|
|
36
|
+
sage: x_using_row_2 = x.restrict([2])
|
|
37
|
+
sage: x_using_row_2
|
|
38
|
+
Dancing links solver for 7 columns and 6 rows
|
|
39
|
+
sage: sorted(map(sorted, x_using_row_2.solutions_iterator()))
|
|
40
|
+
[[2, 3]]
|
|
41
|
+
|
|
42
|
+
The two basic methods that are wrapped in this class are ``search`` which
|
|
43
|
+
returns ``1`` if a solution is found or ``0`` otherwise and ``get_solution``
|
|
44
|
+
which return the current solution::
|
|
45
|
+
|
|
46
|
+
sage: x = dlx_solver(rows)
|
|
47
|
+
sage: x.search()
|
|
48
|
+
1
|
|
49
|
+
sage: x.get_solution()
|
|
50
|
+
[0, 1]
|
|
51
|
+
sage: x.search()
|
|
52
|
+
1
|
|
53
|
+
sage: x.get_solution()
|
|
54
|
+
[2, 3]
|
|
55
|
+
sage: x.search()
|
|
56
|
+
1
|
|
57
|
+
sage: x.get_solution()
|
|
58
|
+
[4, 5]
|
|
59
|
+
sage: x.search()
|
|
60
|
+
0
|
|
61
|
+
|
|
62
|
+
There is also a method ``reinitialize`` to reinitialize the algorithm::
|
|
63
|
+
|
|
64
|
+
sage: x.reinitialize()
|
|
65
|
+
sage: x.search()
|
|
66
|
+
1
|
|
67
|
+
sage: x.get_solution()
|
|
68
|
+
[0, 1]
|
|
69
|
+
"""
|
|
70
|
+
# ****************************************************************************
|
|
71
|
+
# Copyright (C) 2008 Carlo Hamalainen <carlo.hamalainen@gmail.com>
|
|
72
|
+
# Copyright (C) 2015-2018 Sébastien Labbé <slabqc@gmail.com>
|
|
73
|
+
#
|
|
74
|
+
# This program is free software: you can redistribute it and/or modify
|
|
75
|
+
# it under the terms of the GNU General Public License as published by
|
|
76
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
77
|
+
# (at your option) any later version.
|
|
78
|
+
# https://www.gnu.org/licenses/
|
|
79
|
+
# ****************************************************************************
|
|
80
|
+
|
|
81
|
+
from cpython.object cimport PyObject_RichCompare
|
|
82
|
+
from libcpp.vector cimport vector
|
|
83
|
+
from cysignals.signals cimport sig_on, sig_off
|
|
84
|
+
|
|
85
|
+
cdef extern from "dancing_links_c.h":
|
|
86
|
+
cdef cppclass dancing_links:
|
|
87
|
+
dancing_links()
|
|
88
|
+
vector[int] solution
|
|
89
|
+
int number_of_columns()
|
|
90
|
+
void add_rows(vector[vector[int]] rows)
|
|
91
|
+
int search_is_started()
|
|
92
|
+
int search()
|
|
93
|
+
|
|
94
|
+
from sage.misc.cachefunc import cached_method
|
|
95
|
+
|
|
96
|
+
cdef class dancing_linksWrapper:
|
|
97
|
+
r"""
|
|
98
|
+
A simple class that implements dancing links.
|
|
99
|
+
|
|
100
|
+
The main methods to list the solutions are :meth:`search` and
|
|
101
|
+
:meth:`get_solution`. You can also use :meth:`number_of_solutions` to count
|
|
102
|
+
them.
|
|
103
|
+
|
|
104
|
+
This class simply wraps a C++ implementation of Carlo Hamalainen.
|
|
105
|
+
"""
|
|
106
|
+
cdef dancing_links _x
|
|
107
|
+
cdef list _rows
|
|
108
|
+
|
|
109
|
+
def __init__(self, rows):
|
|
110
|
+
"""
|
|
111
|
+
Initialize our wrapper (self._x) as an actual C++ object.
|
|
112
|
+
|
|
113
|
+
We must pass a list of rows at start up.
|
|
114
|
+
|
|
115
|
+
EXAMPLES::
|
|
116
|
+
|
|
117
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
118
|
+
sage: rows = [[0,1,2]]
|
|
119
|
+
sage: rows+= [[0,2]]
|
|
120
|
+
sage: rows+= [[1]]
|
|
121
|
+
sage: rows+= [[3]]
|
|
122
|
+
sage: x = dlx_solver(rows)
|
|
123
|
+
sage: x
|
|
124
|
+
Dancing links solver for 4 columns and 4 rows
|
|
125
|
+
sage: x.search()
|
|
126
|
+
1
|
|
127
|
+
sage: x.get_solution()
|
|
128
|
+
[3, 0]
|
|
129
|
+
|
|
130
|
+
::
|
|
131
|
+
|
|
132
|
+
sage: rows = [[0,1,2], [1, 2]]
|
|
133
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
134
|
+
sage: x = dlx_solver(rows)
|
|
135
|
+
sage: x
|
|
136
|
+
Dancing links solver for 3 columns and 2 rows
|
|
137
|
+
sage: type(x)
|
|
138
|
+
<class 'sage.combinat.matrices.dancing_links.dancing_linksWrapper'>
|
|
139
|
+
|
|
140
|
+
TESTS:
|
|
141
|
+
|
|
142
|
+
The following example would crash in Sage's debug version
|
|
143
|
+
from :issue:`13864` prior to the fix from :issue:`13882`::
|
|
144
|
+
|
|
145
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
146
|
+
sage: x = dlx_solver([])
|
|
147
|
+
sage: x.get_solution()
|
|
148
|
+
[]
|
|
149
|
+
"""
|
|
150
|
+
self._rows = [row for row in rows]
|
|
151
|
+
self._initialize()
|
|
152
|
+
|
|
153
|
+
def _initialize(self):
|
|
154
|
+
r"""
|
|
155
|
+
Initialization of the search algorithm.
|
|
156
|
+
|
|
157
|
+
This adds the rows to the instance of dancing_links. This method is
|
|
158
|
+
used by ``__init__`` and ``reinitialize`` methods and should not be
|
|
159
|
+
used directly.
|
|
160
|
+
|
|
161
|
+
EXAMPLES::
|
|
162
|
+
|
|
163
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
164
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
165
|
+
sage: x = dlx_solver(rows) # indirect doctest
|
|
166
|
+
sage: x.get_solution() if x.search() else None
|
|
167
|
+
[0, 1]
|
|
168
|
+
sage: x.get_solution() if x.search() else None
|
|
169
|
+
[2, 3]
|
|
170
|
+
|
|
171
|
+
Reinitialization of the algorithm::
|
|
172
|
+
|
|
173
|
+
sage: x.reinitialize() # indirect doctest
|
|
174
|
+
sage: x.get_solution() if x.search() else None
|
|
175
|
+
[0, 1]
|
|
176
|
+
"""
|
|
177
|
+
cdef vector[int] v
|
|
178
|
+
cdef vector[vector[int]] vv
|
|
179
|
+
|
|
180
|
+
for row in self._rows:
|
|
181
|
+
v.clear()
|
|
182
|
+
for x in row:
|
|
183
|
+
v.push_back(x)
|
|
184
|
+
vv.push_back(v)
|
|
185
|
+
|
|
186
|
+
sig_on()
|
|
187
|
+
self._x.add_rows(vv)
|
|
188
|
+
sig_off()
|
|
189
|
+
|
|
190
|
+
def reinitialize(self):
|
|
191
|
+
r"""
|
|
192
|
+
Reinitialization of the search algorithm.
|
|
193
|
+
|
|
194
|
+
This recreates an empty ``dancing_links`` object and adds the rows to
|
|
195
|
+
the instance of ``dancing_links.``
|
|
196
|
+
|
|
197
|
+
EXAMPLES::
|
|
198
|
+
|
|
199
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
200
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
201
|
+
sage: x = dlx_solver(rows)
|
|
202
|
+
sage: x.get_solution() if x.search() else None
|
|
203
|
+
[0, 1]
|
|
204
|
+
sage: x.get_solution() if x.search() else None
|
|
205
|
+
[2, 3]
|
|
206
|
+
|
|
207
|
+
Reinitialization of the algorithm::
|
|
208
|
+
|
|
209
|
+
sage: x.reinitialize()
|
|
210
|
+
sage: x.get_solution() if x.search() else None
|
|
211
|
+
[0, 1]
|
|
212
|
+
sage: x.get_solution() if x.search() else None
|
|
213
|
+
[2, 3]
|
|
214
|
+
sage: x.get_solution() if x.search() else None
|
|
215
|
+
[4, 5]
|
|
216
|
+
sage: x.get_solution() if x.search() else None
|
|
217
|
+
|
|
218
|
+
Reinitialization works after solutions are exhausted::
|
|
219
|
+
|
|
220
|
+
sage: x.reinitialize()
|
|
221
|
+
sage: x.get_solution() if x.search() else None
|
|
222
|
+
[0, 1]
|
|
223
|
+
sage: x.get_solution() if x.search() else None
|
|
224
|
+
[2, 3]
|
|
225
|
+
sage: x.get_solution() if x.search() else None
|
|
226
|
+
[4, 5]
|
|
227
|
+
sage: x.get_solution() if x.search() else None
|
|
228
|
+
"""
|
|
229
|
+
sig_on()
|
|
230
|
+
self._x = dancing_links()
|
|
231
|
+
sig_off()
|
|
232
|
+
|
|
233
|
+
self._initialize()
|
|
234
|
+
|
|
235
|
+
def __repr__(self):
|
|
236
|
+
"""
|
|
237
|
+
The string representation of this wrapper is just the list of
|
|
238
|
+
rows as supplied at startup.
|
|
239
|
+
|
|
240
|
+
TESTS::
|
|
241
|
+
|
|
242
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
243
|
+
sage: rows = [[0,1,2], [1,2], [0]]
|
|
244
|
+
sage: dlx_solver(rows)
|
|
245
|
+
Dancing links solver for 3 columns and 3 rows
|
|
246
|
+
"""
|
|
247
|
+
return "Dancing links solver for {} columns and {} rows".format(
|
|
248
|
+
self.ncols(), self.nrows())
|
|
249
|
+
|
|
250
|
+
def rows(self):
|
|
251
|
+
r"""
|
|
252
|
+
Return the list of rows.
|
|
253
|
+
|
|
254
|
+
EXAMPLES::
|
|
255
|
+
|
|
256
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
257
|
+
sage: rows = [[0,1,2], [1,2], [0]]
|
|
258
|
+
sage: x = dlx_solver(rows)
|
|
259
|
+
sage: x.rows()
|
|
260
|
+
[[0, 1, 2], [1, 2], [0]]
|
|
261
|
+
"""
|
|
262
|
+
return self._rows
|
|
263
|
+
|
|
264
|
+
def ncols(self):
|
|
265
|
+
"""
|
|
266
|
+
Return the number of columns.
|
|
267
|
+
|
|
268
|
+
EXAMPLES::
|
|
269
|
+
|
|
270
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
271
|
+
sage: rows = [[0,1,2], [1,2], [0], [3,4,5]]
|
|
272
|
+
sage: dlx = dlx_solver(rows)
|
|
273
|
+
sage: dlx.ncols()
|
|
274
|
+
6
|
|
275
|
+
"""
|
|
276
|
+
return self._x.number_of_columns()
|
|
277
|
+
|
|
278
|
+
def nrows(self):
|
|
279
|
+
"""
|
|
280
|
+
Return the number of rows.
|
|
281
|
+
|
|
282
|
+
EXAMPLES::
|
|
283
|
+
|
|
284
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
285
|
+
sage: rows = [[0,1,2], [1,2], [0], [3,4,5]]
|
|
286
|
+
sage: dlx = dlx_solver(rows)
|
|
287
|
+
sage: dlx.nrows()
|
|
288
|
+
4
|
|
289
|
+
"""
|
|
290
|
+
return len(self._rows)
|
|
291
|
+
|
|
292
|
+
def __reduce__(self):
|
|
293
|
+
"""
|
|
294
|
+
This is used when pickling.
|
|
295
|
+
|
|
296
|
+
TESTS::
|
|
297
|
+
|
|
298
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
299
|
+
sage: rows = [[0,1,2]]
|
|
300
|
+
sage: X = dlx_solver(rows)
|
|
301
|
+
sage: X == loads(dumps(X))
|
|
302
|
+
1
|
|
303
|
+
sage: rows += [[2]]
|
|
304
|
+
sage: Y = dlx_solver(rows)
|
|
305
|
+
sage: Y == loads(dumps(X))
|
|
306
|
+
0
|
|
307
|
+
"""
|
|
308
|
+
return type(self), (self._rows,)
|
|
309
|
+
|
|
310
|
+
def __richcmp__(dancing_linksWrapper left, dancing_linksWrapper right, int op):
|
|
311
|
+
"""
|
|
312
|
+
Two dancing_linksWrapper objects are equal if they were
|
|
313
|
+
initialised using the same row list.
|
|
314
|
+
|
|
315
|
+
TESTS::
|
|
316
|
+
|
|
317
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
318
|
+
sage: rows = [[0,1,2]]
|
|
319
|
+
sage: X = dlx_solver(rows)
|
|
320
|
+
sage: Z = dlx_solver(rows)
|
|
321
|
+
sage: rows += [[2]]
|
|
322
|
+
sage: Y = dlx_solver(rows)
|
|
323
|
+
sage: X == Z
|
|
324
|
+
1
|
|
325
|
+
sage: X == Y
|
|
326
|
+
0
|
|
327
|
+
"""
|
|
328
|
+
return PyObject_RichCompare(left._rows, right._rows, op)
|
|
329
|
+
|
|
330
|
+
def get_solution(self):
|
|
331
|
+
"""
|
|
332
|
+
Return the current solution.
|
|
333
|
+
|
|
334
|
+
After a new solution is found using the method :meth:`search` this
|
|
335
|
+
method return the rows that make up the current solution.
|
|
336
|
+
|
|
337
|
+
TESTS::
|
|
338
|
+
|
|
339
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
340
|
+
sage: rows = [[0,1,2]]
|
|
341
|
+
sage: rows+= [[0,2]]
|
|
342
|
+
sage: rows+= [[1]]
|
|
343
|
+
sage: rows+= [[3]]
|
|
344
|
+
sage: x = dlx_solver(rows)
|
|
345
|
+
sage: print(x.search())
|
|
346
|
+
1
|
|
347
|
+
sage: print(x.get_solution())
|
|
348
|
+
[3, 0]
|
|
349
|
+
"""
|
|
350
|
+
cdef size_t i
|
|
351
|
+
cdef list s = []
|
|
352
|
+
for i in range(self._x.solution.size()):
|
|
353
|
+
s.append(self._x.solution.at(i))
|
|
354
|
+
|
|
355
|
+
return s
|
|
356
|
+
|
|
357
|
+
def search(self):
|
|
358
|
+
"""
|
|
359
|
+
Search for a new solution.
|
|
360
|
+
|
|
361
|
+
Return ``1`` if a new solution is found and ``0`` otherwise. To recover
|
|
362
|
+
the solution, use the method :meth:`get_solution`.
|
|
363
|
+
|
|
364
|
+
EXAMPLES::
|
|
365
|
+
|
|
366
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
367
|
+
sage: rows = [[0,1,2]]
|
|
368
|
+
sage: rows+= [[0,2]]
|
|
369
|
+
sage: rows+= [[1]]
|
|
370
|
+
sage: rows+= [[3]]
|
|
371
|
+
sage: x = dlx_solver(rows)
|
|
372
|
+
sage: print(x.search())
|
|
373
|
+
1
|
|
374
|
+
sage: print(x.get_solution())
|
|
375
|
+
[3, 0]
|
|
376
|
+
|
|
377
|
+
TESTS:
|
|
378
|
+
|
|
379
|
+
Test that :issue:`11814` is fixed::
|
|
380
|
+
|
|
381
|
+
sage: dlx_solver([]).search()
|
|
382
|
+
0
|
|
383
|
+
sage: dlx_solver([[]]).search()
|
|
384
|
+
0
|
|
385
|
+
|
|
386
|
+
If search is called once too often, it keeps returning 0::
|
|
387
|
+
|
|
388
|
+
sage: x = dlx_solver([[0]])
|
|
389
|
+
sage: x.search()
|
|
390
|
+
1
|
|
391
|
+
sage: x.search()
|
|
392
|
+
0
|
|
393
|
+
sage: x.search()
|
|
394
|
+
0
|
|
395
|
+
"""
|
|
396
|
+
sig_on()
|
|
397
|
+
x = self._x.search()
|
|
398
|
+
sig_off()
|
|
399
|
+
return x
|
|
400
|
+
|
|
401
|
+
def restrict(self, indices):
|
|
402
|
+
r"""
|
|
403
|
+
Return a dancing links solver solving the subcase which uses some
|
|
404
|
+
given rows.
|
|
405
|
+
|
|
406
|
+
For every row that is wanted in the solution, we add a new column
|
|
407
|
+
to the row to make sure it is in the solution.
|
|
408
|
+
|
|
409
|
+
INPUT:
|
|
410
|
+
|
|
411
|
+
- ``indices`` -- list; row indices to be found in the solution
|
|
412
|
+
|
|
413
|
+
OUTPUT: dancing links solver
|
|
414
|
+
|
|
415
|
+
EXAMPLES::
|
|
416
|
+
|
|
417
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
418
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
419
|
+
sage: d = dlx_solver(rows)
|
|
420
|
+
sage: d
|
|
421
|
+
Dancing links solver for 6 columns and 6 rows
|
|
422
|
+
sage: sorted(map(sorted, d.solutions_iterator()))
|
|
423
|
+
[[0, 1], [2, 3], [4, 5]]
|
|
424
|
+
|
|
425
|
+
To impose that the `0`-th row is part of the solution, the rows of the new
|
|
426
|
+
problem are::
|
|
427
|
+
|
|
428
|
+
sage: d_using_0 = d.restrict([0])
|
|
429
|
+
sage: d_using_0
|
|
430
|
+
Dancing links solver for 7 columns and 6 rows
|
|
431
|
+
sage: d_using_0.rows()
|
|
432
|
+
[[0, 1, 2, 6], [3, 4, 5], [0, 1], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]]
|
|
433
|
+
|
|
434
|
+
After restriction the subproblem has one more columns and the same
|
|
435
|
+
number of rows as the original one::
|
|
436
|
+
|
|
437
|
+
sage: d.restrict([1]).rows()
|
|
438
|
+
[[0, 1, 2], [3, 4, 5, 6], [0, 1], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]]
|
|
439
|
+
sage: d.restrict([2]).rows()
|
|
440
|
+
[[0, 1, 2], [3, 4, 5], [0, 1, 6], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]]
|
|
441
|
+
|
|
442
|
+
This method allows to find solutions where the `0`-th row is part of a
|
|
443
|
+
solution::
|
|
444
|
+
|
|
445
|
+
sage: sorted(map(sorted, d.restrict([0]).solutions_iterator()))
|
|
446
|
+
[[0, 1]]
|
|
447
|
+
|
|
448
|
+
Some other examples::
|
|
449
|
+
|
|
450
|
+
sage: sorted(map(sorted, d.restrict([1]).solutions_iterator()))
|
|
451
|
+
[[0, 1]]
|
|
452
|
+
sage: sorted(map(sorted, d.restrict([2]).solutions_iterator()))
|
|
453
|
+
[[2, 3]]
|
|
454
|
+
sage: sorted(map(sorted, d.restrict([3]).solutions_iterator()))
|
|
455
|
+
[[2, 3]]
|
|
456
|
+
|
|
457
|
+
Here there are no solutions using both 0th and 3rd row::
|
|
458
|
+
|
|
459
|
+
sage: list(d.restrict([0,3]).solutions_iterator())
|
|
460
|
+
[]
|
|
461
|
+
|
|
462
|
+
TESTS::
|
|
463
|
+
|
|
464
|
+
sage: d.restrict([]).rows()
|
|
465
|
+
[[0, 1, 2], [3, 4, 5], [0, 1], [2, 3, 4, 5], [0], [1, 2, 3, 4, 5]]
|
|
466
|
+
"""
|
|
467
|
+
from copy import copy
|
|
468
|
+
rows = copy(self._rows)
|
|
469
|
+
ncols = self.ncols()
|
|
470
|
+
for i, row_index in enumerate(indices):
|
|
471
|
+
# in the line below we want the creation of a new list
|
|
472
|
+
rows[row_index] = rows[row_index] + [ncols+i]
|
|
473
|
+
return dlx_solver(rows)
|
|
474
|
+
|
|
475
|
+
def split(self, column):
|
|
476
|
+
r"""
|
|
477
|
+
Return a dict of independent solvers.
|
|
478
|
+
|
|
479
|
+
For each ``i``-th row containing a ``1`` in the ``column``, the
|
|
480
|
+
dict associates the solver giving all solution using the ``i``-th
|
|
481
|
+
row.
|
|
482
|
+
|
|
483
|
+
This is used for parallel computations.
|
|
484
|
+
|
|
485
|
+
INPUT:
|
|
486
|
+
|
|
487
|
+
- ``column`` -- integer; the column used to split the problem into
|
|
488
|
+
independent subproblems
|
|
489
|
+
|
|
490
|
+
OUTPUT: dict where keys are row numbers and values are dlx solvers
|
|
491
|
+
|
|
492
|
+
EXAMPLES::
|
|
493
|
+
|
|
494
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
495
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
496
|
+
sage: d = dlx_solver(rows)
|
|
497
|
+
sage: d
|
|
498
|
+
Dancing links solver for 6 columns and 6 rows
|
|
499
|
+
sage: sorted(map(sorted, d.solutions_iterator()))
|
|
500
|
+
[[0, 1], [2, 3], [4, 5]]
|
|
501
|
+
|
|
502
|
+
After the split each subproblem has one more column and the same
|
|
503
|
+
number of rows as the original problem::
|
|
504
|
+
|
|
505
|
+
sage: D = d.split(0)
|
|
506
|
+
sage: D
|
|
507
|
+
{0: Dancing links solver for 7 columns and 6 rows,
|
|
508
|
+
2: Dancing links solver for 7 columns and 6 rows,
|
|
509
|
+
4: Dancing links solver for 7 columns and 6 rows}
|
|
510
|
+
|
|
511
|
+
The (disjoint) union of the solutions of the subproblems is equal to the
|
|
512
|
+
set of solutions shown above::
|
|
513
|
+
|
|
514
|
+
sage: for x in D.values(): sorted(map(sorted, x.solutions_iterator()))
|
|
515
|
+
[[0, 1]]
|
|
516
|
+
[[2, 3]]
|
|
517
|
+
[[4, 5]]
|
|
518
|
+
|
|
519
|
+
TESTS::
|
|
520
|
+
|
|
521
|
+
sage: d.split(6)
|
|
522
|
+
Traceback (most recent call last):
|
|
523
|
+
...
|
|
524
|
+
ValueError: column(=6) must be in range(ncols) where ncols=6
|
|
525
|
+
|
|
526
|
+
This use to take a lot of time and memory. Not anymore since
|
|
527
|
+
:issue:`24315`::
|
|
528
|
+
|
|
529
|
+
sage: S = Subsets(range(11))
|
|
530
|
+
sage: rows = map(list, S)
|
|
531
|
+
sage: dlx = dlx_solver(rows)
|
|
532
|
+
sage: dlx
|
|
533
|
+
Dancing links solver for 11 columns and 2048 rows
|
|
534
|
+
sage: d = dlx.split(0)
|
|
535
|
+
sage: d[1]
|
|
536
|
+
Dancing links solver for 12 columns and 2048 rows
|
|
537
|
+
"""
|
|
538
|
+
if not 0 <= column < self.ncols():
|
|
539
|
+
raise ValueError("column(={}) must be in range(ncols) "
|
|
540
|
+
"where ncols={}".format(column, self.ncols()))
|
|
541
|
+
indices = (i for i, row in enumerate(self._rows) if column in row)
|
|
542
|
+
return {i: self.restrict([i]) for i in indices}
|
|
543
|
+
|
|
544
|
+
def solutions_iterator(self):
|
|
545
|
+
r"""
|
|
546
|
+
Return an iterator of the solutions.
|
|
547
|
+
|
|
548
|
+
EXAMPLES::
|
|
549
|
+
|
|
550
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
551
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
552
|
+
sage: d = dlx_solver(rows)
|
|
553
|
+
sage: sorted(map(sorted, d.solutions_iterator()))
|
|
554
|
+
[[0, 1], [2, 3], [4, 5]]
|
|
555
|
+
|
|
556
|
+
TESTS:
|
|
557
|
+
|
|
558
|
+
The algorithm is automatically reinitialized if needed, for example
|
|
559
|
+
when iterating the solutions a second time (:issue:`25125`)::
|
|
560
|
+
|
|
561
|
+
sage: sorted(map(sorted, d.solutions_iterator()))
|
|
562
|
+
[[0, 1], [2, 3], [4, 5]]
|
|
563
|
+
"""
|
|
564
|
+
if self._x.search_is_started():
|
|
565
|
+
self.reinitialize()
|
|
566
|
+
while self.search():
|
|
567
|
+
yield self.get_solution()
|
|
568
|
+
|
|
569
|
+
def one_solution(self, ncpus=None, column=None):
|
|
570
|
+
r"""
|
|
571
|
+
Return the first solution found.
|
|
572
|
+
|
|
573
|
+
This method allows parallel computations which might be useful for
|
|
574
|
+
some kind of problems when it is very hard just to find one
|
|
575
|
+
solution.
|
|
576
|
+
|
|
577
|
+
INPUT:
|
|
578
|
+
|
|
579
|
+
- ``ncpus`` -- integer (default: ``None``); maximal number of
|
|
580
|
+
subprocesses to use at the same time. If ``None``, it detects the
|
|
581
|
+
number of effective CPUs in the system using
|
|
582
|
+
:func:`sage.parallel.ncpus.ncpus()`.
|
|
583
|
+
If ``ncpus=1``, the first solution is searched serially.
|
|
584
|
+
- ``column`` -- integer (default: ``None``); the column used to split
|
|
585
|
+
the problem (see :meth:`restrict`). If ``None``, a random column
|
|
586
|
+
is chosen. This argument is ignored if ``ncpus=1``.
|
|
587
|
+
|
|
588
|
+
OUTPUT: list of rows or ``None`` if no solution is found
|
|
589
|
+
|
|
590
|
+
.. NOTE::
|
|
591
|
+
|
|
592
|
+
For some case, increasing the number of cpus makes it
|
|
593
|
+
faster. For other instances, ``ncpus=1`` is faster. It all
|
|
594
|
+
depends on problem which is considered.
|
|
595
|
+
|
|
596
|
+
EXAMPLES::
|
|
597
|
+
|
|
598
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
599
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
600
|
+
sage: d = dlx_solver(rows)
|
|
601
|
+
sage: solutions = [[0,1], [2,3], [4,5]]
|
|
602
|
+
sage: sorted(d.one_solution()) in solutions
|
|
603
|
+
True
|
|
604
|
+
|
|
605
|
+
The number of CPUs can be specified as input::
|
|
606
|
+
|
|
607
|
+
sage: sorted(d.one_solution(ncpus=2)) in solutions
|
|
608
|
+
True
|
|
609
|
+
|
|
610
|
+
The column used to split the problem for parallel computations can
|
|
611
|
+
be given::
|
|
612
|
+
|
|
613
|
+
sage: sorted(d.one_solution(ncpus=2, column=4)) in solutions
|
|
614
|
+
True
|
|
615
|
+
|
|
616
|
+
When no solution is found::
|
|
617
|
+
|
|
618
|
+
sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]]
|
|
619
|
+
sage: d = dlx_solver(rows)
|
|
620
|
+
sage: d.one_solution() is None
|
|
621
|
+
True
|
|
622
|
+
|
|
623
|
+
TESTS::
|
|
624
|
+
|
|
625
|
+
sage: [d.one_solution(column=i) for i in range(6)]
|
|
626
|
+
[None, None, None, None, None, None]
|
|
627
|
+
|
|
628
|
+
The preprocess needed to start the parallel computation is not so
|
|
629
|
+
big (less than 50ms in the example below)::
|
|
630
|
+
|
|
631
|
+
sage: S = Subsets(range(11))
|
|
632
|
+
sage: rows = list(map(list, S))
|
|
633
|
+
sage: dlx = dlx_solver(rows)
|
|
634
|
+
sage: dlx
|
|
635
|
+
Dancing links solver for 11 columns and 2048 rows
|
|
636
|
+
sage: solution = dlx.one_solution()
|
|
637
|
+
sage: subsets = [set(rows[i]) for i in solution]
|
|
638
|
+
|
|
639
|
+
We make sure the solution is an exact cover::
|
|
640
|
+
|
|
641
|
+
sage: set.union(*subsets)
|
|
642
|
+
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
|
643
|
+
sage: from itertools import combinations
|
|
644
|
+
sage: any(p.intersection(q) for p,q in combinations(subsets, 2))
|
|
645
|
+
False
|
|
646
|
+
"""
|
|
647
|
+
if ncpus == 1:
|
|
648
|
+
return self.get_solution() if self.search() else None
|
|
649
|
+
|
|
650
|
+
if column is None:
|
|
651
|
+
from random import randrange
|
|
652
|
+
column = randrange(self.ncols())
|
|
653
|
+
|
|
654
|
+
if not 0 <= column < self.ncols():
|
|
655
|
+
raise ValueError("column(={}) must be in range(ncols) "
|
|
656
|
+
"where ncols={}".format(column, self.ncols()))
|
|
657
|
+
|
|
658
|
+
from sage.parallel.decorate import parallel
|
|
659
|
+
|
|
660
|
+
@parallel(ncpus=ncpus)
|
|
661
|
+
def first_solution(i):
|
|
662
|
+
dlx = self.restrict([i])
|
|
663
|
+
if dlx.search():
|
|
664
|
+
return dlx.get_solution()
|
|
665
|
+
else:
|
|
666
|
+
return None
|
|
667
|
+
|
|
668
|
+
indices = [i for (i, row) in enumerate(self._rows) if column in row]
|
|
669
|
+
for (args_kwds, val) in first_solution(indices):
|
|
670
|
+
if val is not None:
|
|
671
|
+
return val
|
|
672
|
+
|
|
673
|
+
def all_solutions(self, ncpus=None, column=None):
|
|
674
|
+
r"""
|
|
675
|
+
Return all solutions found after splitting the problem to allow
|
|
676
|
+
parallel computation.
|
|
677
|
+
|
|
678
|
+
INPUT:
|
|
679
|
+
|
|
680
|
+
- ``ncpus`` -- integer (default: ``None``); maximal number of
|
|
681
|
+
subprocesses to use at the same time. If ``None``, it detects the
|
|
682
|
+
number of effective CPUs in the system using
|
|
683
|
+
:func:`sage.parallel.ncpus.ncpus()`.
|
|
684
|
+
- ``column`` -- integer (default: ``None``); the column used to split
|
|
685
|
+
the problem, if ``None`` a random column is chosen
|
|
686
|
+
|
|
687
|
+
OUTPUT: list of solutions
|
|
688
|
+
|
|
689
|
+
EXAMPLES::
|
|
690
|
+
|
|
691
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
692
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
693
|
+
sage: d = dlx_solver(rows)
|
|
694
|
+
sage: S = d.all_solutions()
|
|
695
|
+
sage: sorted(sorted(s) for s in S)
|
|
696
|
+
[[0, 1], [2, 3], [4, 5]]
|
|
697
|
+
|
|
698
|
+
The number of CPUs can be specified as input::
|
|
699
|
+
|
|
700
|
+
sage: S = Subsets(range(4))
|
|
701
|
+
sage: rows = map(list, S)
|
|
702
|
+
sage: dlx = dlx_solver(rows)
|
|
703
|
+
sage: dlx
|
|
704
|
+
Dancing links solver for 4 columns and 16 rows
|
|
705
|
+
sage: dlx.number_of_solutions()
|
|
706
|
+
15
|
|
707
|
+
sage: sorted(sorted(s) for s in dlx.all_solutions(ncpus=2))
|
|
708
|
+
[[1, 2, 3, 4],
|
|
709
|
+
[1, 2, 10],
|
|
710
|
+
[1, 3, 9],
|
|
711
|
+
[1, 4, 8],
|
|
712
|
+
[1, 14],
|
|
713
|
+
[2, 3, 7],
|
|
714
|
+
[2, 4, 6],
|
|
715
|
+
[2, 13],
|
|
716
|
+
[3, 4, 5],
|
|
717
|
+
[3, 12],
|
|
718
|
+
[4, 11],
|
|
719
|
+
[5, 10],
|
|
720
|
+
[6, 9],
|
|
721
|
+
[7, 8],
|
|
722
|
+
[15]]
|
|
723
|
+
|
|
724
|
+
If ``ncpus=1``, the computation is not done in parallel::
|
|
725
|
+
|
|
726
|
+
sage: sorted(sorted(s) for s in dlx.all_solutions(ncpus=1))
|
|
727
|
+
[[1, 2, 3, 4],
|
|
728
|
+
[1, 2, 10],
|
|
729
|
+
[1, 3, 9],
|
|
730
|
+
[1, 4, 8],
|
|
731
|
+
[1, 14],
|
|
732
|
+
[2, 3, 7],
|
|
733
|
+
[2, 4, 6],
|
|
734
|
+
[2, 13],
|
|
735
|
+
[3, 4, 5],
|
|
736
|
+
[3, 12],
|
|
737
|
+
[4, 11],
|
|
738
|
+
[5, 10],
|
|
739
|
+
[6, 9],
|
|
740
|
+
[7, 8],
|
|
741
|
+
[15]]
|
|
742
|
+
|
|
743
|
+
TESTS:
|
|
744
|
+
|
|
745
|
+
When no solution is found::
|
|
746
|
+
|
|
747
|
+
sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]]
|
|
748
|
+
sage: d = dlx_solver(rows)
|
|
749
|
+
sage: d.all_solutions()
|
|
750
|
+
[]
|
|
751
|
+
|
|
752
|
+
::
|
|
753
|
+
|
|
754
|
+
sage: [d.all_solutions(column=i) for i in range(6)]
|
|
755
|
+
[[], [], [], [], [], []]
|
|
756
|
+
"""
|
|
757
|
+
if ncpus == 1:
|
|
758
|
+
if self._x.search_is_started():
|
|
759
|
+
self.reinitialize()
|
|
760
|
+
L = []
|
|
761
|
+
while self.search():
|
|
762
|
+
L.append(self.get_solution())
|
|
763
|
+
return L
|
|
764
|
+
|
|
765
|
+
if column is None:
|
|
766
|
+
from random import randrange
|
|
767
|
+
column = randrange(self.ncols())
|
|
768
|
+
|
|
769
|
+
if not 0 <= column < self.ncols():
|
|
770
|
+
raise ValueError("column(={}) must be in range(ncols) "
|
|
771
|
+
"where ncols={}".format(column, self.ncols()))
|
|
772
|
+
|
|
773
|
+
from sage.parallel.decorate import parallel
|
|
774
|
+
|
|
775
|
+
@parallel(ncpus=ncpus)
|
|
776
|
+
def all_solutions(i):
|
|
777
|
+
dlx = self.restrict([i])
|
|
778
|
+
L = []
|
|
779
|
+
while dlx.search():
|
|
780
|
+
L.append(dlx.get_solution())
|
|
781
|
+
return L
|
|
782
|
+
|
|
783
|
+
indices = [i for i, row in enumerate(self._rows) if column in row]
|
|
784
|
+
L = []
|
|
785
|
+
for (args_kwds, val) in all_solutions(indices):
|
|
786
|
+
L.extend(val)
|
|
787
|
+
return L
|
|
788
|
+
|
|
789
|
+
def number_of_solutions(self, ncpus=None, column=None):
|
|
790
|
+
r"""
|
|
791
|
+
Return the number of distinct solutions.
|
|
792
|
+
|
|
793
|
+
INPUT:
|
|
794
|
+
|
|
795
|
+
- ``ncpus`` -- integer (default: ``None``); maximal number of
|
|
796
|
+
subprocesses to use at the same time. If ``ncpus>1`` the dancing
|
|
797
|
+
links problem is split into independent subproblems to allow
|
|
798
|
+
parallel computation. If ``None``, it detects the number of
|
|
799
|
+
effective CPUs in the system using
|
|
800
|
+
:func:`sage.parallel.ncpus.ncpus()`.
|
|
801
|
+
- ``column`` -- integer (default: ``None``); the column used to split
|
|
802
|
+
the problem, if ``None`` a random column is chosen (this argument
|
|
803
|
+
is ignored if ``ncpus`` is ``1``)
|
|
804
|
+
|
|
805
|
+
OUTPUT: integer
|
|
806
|
+
|
|
807
|
+
EXAMPLES::
|
|
808
|
+
|
|
809
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
810
|
+
sage: rows = [[0,1,2]]
|
|
811
|
+
sage: rows += [[0,2]]
|
|
812
|
+
sage: rows += [[1]]
|
|
813
|
+
sage: rows += [[3]]
|
|
814
|
+
sage: x = dlx_solver(rows)
|
|
815
|
+
sage: x.number_of_solutions()
|
|
816
|
+
2
|
|
817
|
+
|
|
818
|
+
The number of CPUs can be specified as input::
|
|
819
|
+
|
|
820
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
821
|
+
sage: x = dlx_solver(rows)
|
|
822
|
+
sage: x.number_of_solutions(ncpus=2, column=3)
|
|
823
|
+
3
|
|
824
|
+
|
|
825
|
+
::
|
|
826
|
+
|
|
827
|
+
sage: S = Subsets(range(5))
|
|
828
|
+
sage: rows = map(list, S)
|
|
829
|
+
sage: d = dlx_solver(rows)
|
|
830
|
+
sage: d.number_of_solutions()
|
|
831
|
+
52
|
|
832
|
+
|
|
833
|
+
TESTS:
|
|
834
|
+
|
|
835
|
+
The algorithm is automatically reinitialized if needed, for example
|
|
836
|
+
when counting the number of solutions a second time (:issue:`25125`)::
|
|
837
|
+
|
|
838
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
839
|
+
sage: x = dlx_solver(rows)
|
|
840
|
+
sage: x.number_of_solutions(ncpus=1)
|
|
841
|
+
3
|
|
842
|
+
sage: x.number_of_solutions(ncpus=1)
|
|
843
|
+
3
|
|
844
|
+
|
|
845
|
+
Works with empty rows::
|
|
846
|
+
|
|
847
|
+
sage: dlx_solver([]).number_of_solutions(ncpus=None)
|
|
848
|
+
0
|
|
849
|
+
sage: dlx_solver([]).number_of_solutions(ncpus=1)
|
|
850
|
+
0
|
|
851
|
+
"""
|
|
852
|
+
cdef int N = 0
|
|
853
|
+
if ncpus == 1:
|
|
854
|
+
if self._x.search_is_started():
|
|
855
|
+
self.reinitialize()
|
|
856
|
+
while self.search():
|
|
857
|
+
N += 1
|
|
858
|
+
return N
|
|
859
|
+
|
|
860
|
+
if self.ncols() == 0:
|
|
861
|
+
return 0
|
|
862
|
+
|
|
863
|
+
if column is None:
|
|
864
|
+
from random import randrange
|
|
865
|
+
column = randrange(self.ncols())
|
|
866
|
+
|
|
867
|
+
if not 0 <= column < self.ncols():
|
|
868
|
+
raise ValueError("column(={}) must be in range(ncols) "
|
|
869
|
+
"where ncols={}".format(column, self.ncols()))
|
|
870
|
+
|
|
871
|
+
from sage.parallel.decorate import parallel
|
|
872
|
+
|
|
873
|
+
@parallel(ncpus=ncpus)
|
|
874
|
+
def nb_sol(i):
|
|
875
|
+
dlx = self.restrict([i])
|
|
876
|
+
N = 0
|
|
877
|
+
while dlx.search():
|
|
878
|
+
N += 1
|
|
879
|
+
return N
|
|
880
|
+
|
|
881
|
+
indices = [i for i, row in enumerate(self._rows) if column in row]
|
|
882
|
+
return sum(val for (args_kwds, val) in nb_sol(indices))
|
|
883
|
+
|
|
884
|
+
@cached_method
|
|
885
|
+
def to_sat_solver(self, solver=None):
|
|
886
|
+
r"""
|
|
887
|
+
Return the SAT solver solving an equivalent problem.
|
|
888
|
+
|
|
889
|
+
Note that row index `i` in the dancing links solver corresponds to
|
|
890
|
+
the boolean variable index `ì+1` for the SAT solver to avoid
|
|
891
|
+
the variable index `0`.
|
|
892
|
+
|
|
893
|
+
See also :mod:`sage.sat.solvers.satsolver`.
|
|
894
|
+
|
|
895
|
+
INPUT:
|
|
896
|
+
|
|
897
|
+
- ``solver`` -- string or ``None`` (default: ``None``),
|
|
898
|
+
possible values include ``'picosat'``, ``'cryptominisat'``,
|
|
899
|
+
``'LP'``, ``'glucose'``, ``'glucose-syrup'``.
|
|
900
|
+
|
|
901
|
+
OUTPUT: SAT solver instance
|
|
902
|
+
|
|
903
|
+
EXAMPLES::
|
|
904
|
+
|
|
905
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
906
|
+
sage: rows = [[0,1,2], [0,2], [1], [3]]
|
|
907
|
+
sage: x = dlx_solver(rows)
|
|
908
|
+
sage: s = x.to_sat_solver() # needs sage.numerical.mip sage.sat
|
|
909
|
+
|
|
910
|
+
Using some optional SAT solvers::
|
|
911
|
+
|
|
912
|
+
sage: x.to_sat_solver('cryptominisat') # optional - pycryptosat # needs sage.sat
|
|
913
|
+
CryptoMiniSat solver: 4 variables, 7 clauses.
|
|
914
|
+
"""
|
|
915
|
+
from sage.sat.solvers.satsolver import SAT
|
|
916
|
+
s = SAT(solver)
|
|
917
|
+
|
|
918
|
+
# Note that row number i is associated to SAT variable i+1 to
|
|
919
|
+
# avoid a variable zero
|
|
920
|
+
columns = [[] for _ in range(self.ncols())]
|
|
921
|
+
for i, row in enumerate(self.rows(), start=1):
|
|
922
|
+
for a in row:
|
|
923
|
+
columns[a].append(i)
|
|
924
|
+
|
|
925
|
+
# At least one 1 in each column
|
|
926
|
+
for clause in columns:
|
|
927
|
+
s.add_clause(clause)
|
|
928
|
+
|
|
929
|
+
# At most one 1 in each column
|
|
930
|
+
import itertools
|
|
931
|
+
for clause in columns:
|
|
932
|
+
for p, q in itertools.combinations(clause, 2):
|
|
933
|
+
sub_clause = [-p, -q]
|
|
934
|
+
s.add_clause(sub_clause)
|
|
935
|
+
|
|
936
|
+
return s
|
|
937
|
+
|
|
938
|
+
def one_solution_using_sat_solver(self, solver=None):
|
|
939
|
+
r"""
|
|
940
|
+
Return a solution found using a SAT solver.
|
|
941
|
+
|
|
942
|
+
INPUT:
|
|
943
|
+
|
|
944
|
+
- ``solver`` -- string or ``None`` (default: ``None``),
|
|
945
|
+
possible values include ``'picosat'``, ``'cryptominisat'``,
|
|
946
|
+
``'LP'``, ``'glucose'``, ``'glucose-syrup'``.
|
|
947
|
+
|
|
948
|
+
OUTPUT: list of rows or ``None`` if no solution is found
|
|
949
|
+
|
|
950
|
+
.. NOTE::
|
|
951
|
+
|
|
952
|
+
When comparing the time taken by method ``one_solution``,
|
|
953
|
+
have in mind that ``one_solution_using_sat_solver`` first
|
|
954
|
+
creates the SAT solver instance from the dancing links
|
|
955
|
+
solver. This copy of data may take many seconds depending on
|
|
956
|
+
the size of the problem.
|
|
957
|
+
|
|
958
|
+
EXAMPLES::
|
|
959
|
+
|
|
960
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
961
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
962
|
+
sage: d = dlx_solver(rows)
|
|
963
|
+
sage: solutions = [[0,1], [2,3], [4,5]]
|
|
964
|
+
sage: d.one_solution_using_sat_solver() in solutions # needs sage.numerical.mip sage.sat
|
|
965
|
+
True
|
|
966
|
+
|
|
967
|
+
Using optional solvers::
|
|
968
|
+
|
|
969
|
+
sage: s = d.one_solution_using_sat_solver('glucose') # optional - glucose, needs sage.sat
|
|
970
|
+
sage: s in solutions # optional - glucose, needs sage.sat
|
|
971
|
+
True
|
|
972
|
+
|
|
973
|
+
When no solution is found::
|
|
974
|
+
|
|
975
|
+
sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]]
|
|
976
|
+
sage: d = dlx_solver(rows)
|
|
977
|
+
sage: d.one_solution_using_sat_solver() is None # needs sage.sat sage.numerical.mip
|
|
978
|
+
True
|
|
979
|
+
"""
|
|
980
|
+
sat_solver = self.to_sat_solver(solver)
|
|
981
|
+
solution = sat_solver()
|
|
982
|
+
if not solution:
|
|
983
|
+
return None
|
|
984
|
+
return [key for key, val in enumerate(solution, start=-1) if val]
|
|
985
|
+
|
|
986
|
+
@cached_method
|
|
987
|
+
def to_milp(self, solver=None):
|
|
988
|
+
r"""
|
|
989
|
+
Return the mixed integer linear program (MILP) representing an
|
|
990
|
+
equivalent problem.
|
|
991
|
+
|
|
992
|
+
See also :mod:`sage.numerical.mip.MixedIntegerLinearProgram`.
|
|
993
|
+
|
|
994
|
+
INPUT:
|
|
995
|
+
|
|
996
|
+
- ``solver`` -- string or ``None`` (default: ``None``); possible
|
|
997
|
+
values include ``'GLPK'``, ``'GLPK/exact'``, ``'Coin'``,
|
|
998
|
+
``'CPLEX'``, ``'Gurobi'``, ``'CVXOPT'``, ``'PPL'``,
|
|
999
|
+
``'InteractiveLP'``
|
|
1000
|
+
|
|
1001
|
+
OUTPUT:
|
|
1002
|
+
|
|
1003
|
+
- MixedIntegerLinearProgram instance
|
|
1004
|
+
- MIPVariable with binary components
|
|
1005
|
+
|
|
1006
|
+
EXAMPLES::
|
|
1007
|
+
|
|
1008
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
1009
|
+
sage: rows = [[0,1,2], [0,2], [1], [3]]
|
|
1010
|
+
sage: d = dlx_solver(rows)
|
|
1011
|
+
sage: p,x = d.to_milp() # needs sage.numerical.mip
|
|
1012
|
+
sage: p # needs sage.numerical.mip
|
|
1013
|
+
Boolean Program (no objective, 4 variables, ... constraints)
|
|
1014
|
+
sage: x # needs sage.numerical.mip
|
|
1015
|
+
MIPVariable with 4 binary components
|
|
1016
|
+
|
|
1017
|
+
In the reduction, the boolean variable `x_i` is ``True`` if and only if
|
|
1018
|
+
the `i`-th row is in the solution::
|
|
1019
|
+
|
|
1020
|
+
sage: p.show() # needs sage.numerical.mip
|
|
1021
|
+
Maximization:
|
|
1022
|
+
<BLANKLINE>
|
|
1023
|
+
<BLANKLINE>
|
|
1024
|
+
Constraints:...
|
|
1025
|
+
one 1 in 0-th column: 1.0 <= x_0 + x_1 <= 1.0
|
|
1026
|
+
one 1 in 1-th column: 1.0 <= x_0 + x_2 <= 1.0
|
|
1027
|
+
one 1 in 2-th column: 1.0 <= x_0 + x_1 <= 1.0
|
|
1028
|
+
one 1 in 3-th column: 1.0 <= x_3 <= 1.0
|
|
1029
|
+
Variables:
|
|
1030
|
+
x_0 is a boolean variable (min=0.0, max=1.0)
|
|
1031
|
+
x_1 is a boolean variable (min=0.0, max=1.0)
|
|
1032
|
+
x_2 is a boolean variable (min=0.0, max=1.0)
|
|
1033
|
+
x_3 is a boolean variable (min=0.0, max=1.0)
|
|
1034
|
+
|
|
1035
|
+
Using some optional MILP solvers::
|
|
1036
|
+
|
|
1037
|
+
sage: d.to_milp('gurobi') # optional - gurobi sage_numerical_backends_gurobi, needs sage.numerical.mip
|
|
1038
|
+
(Boolean Program (no objective, 4 variables, 4 constraints),
|
|
1039
|
+
MIPVariable with 4 binary components)
|
|
1040
|
+
"""
|
|
1041
|
+
from sage.numerical.mip import MixedIntegerLinearProgram
|
|
1042
|
+
p = MixedIntegerLinearProgram(solver=solver)
|
|
1043
|
+
|
|
1044
|
+
# x[i] == True iff i-th dlx row is in the solution
|
|
1045
|
+
x = p.new_variable(binary=True, indices=range(self.nrows()))
|
|
1046
|
+
|
|
1047
|
+
# Construction of the columns (transpose of the rows)
|
|
1048
|
+
columns = [[] for _ in range(self.ncols())]
|
|
1049
|
+
for i, row in enumerate(self.rows()):
|
|
1050
|
+
for a in row:
|
|
1051
|
+
columns[a].append(i)
|
|
1052
|
+
|
|
1053
|
+
# Constraints: exactly one 1 in each column
|
|
1054
|
+
for j, column in enumerate(columns):
|
|
1055
|
+
S = p.sum(x[a] for a in column)
|
|
1056
|
+
name = "one 1 in {}-th column".format(j)
|
|
1057
|
+
p.add_constraint(S == 1, name=name)
|
|
1058
|
+
|
|
1059
|
+
return p, x
|
|
1060
|
+
|
|
1061
|
+
def one_solution_using_milp_solver(self, solver=None, integrality_tolerance=1e-3):
|
|
1062
|
+
r"""
|
|
1063
|
+
Return a solution found using a MILP solver.
|
|
1064
|
+
|
|
1065
|
+
INPUT:
|
|
1066
|
+
|
|
1067
|
+
- ``solver`` -- string or ``None`` (default: ``None``); possible
|
|
1068
|
+
values include ``'GLPK'``, ``'GLPK/exact'``, ``'Coin'``,
|
|
1069
|
+
``'CPLEX'``, ``'Gurobi'``, ``'CVXOPT'``, ``'PPL'``,
|
|
1070
|
+
``'InteractiveLP'``
|
|
1071
|
+
|
|
1072
|
+
OUTPUT: list of rows or ``None`` if no solution is found
|
|
1073
|
+
|
|
1074
|
+
.. NOTE::
|
|
1075
|
+
|
|
1076
|
+
When comparing the time taken by method ``one_solution``, have in
|
|
1077
|
+
mind that ``one_solution_using_milp_solver`` first creates (and
|
|
1078
|
+
caches) the MILP solver instance from the dancing links solver.
|
|
1079
|
+
This copy of data may take many seconds depending on the size
|
|
1080
|
+
of the problem.
|
|
1081
|
+
|
|
1082
|
+
EXAMPLES::
|
|
1083
|
+
|
|
1084
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
1085
|
+
sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]]
|
|
1086
|
+
sage: d = dlx_solver(rows)
|
|
1087
|
+
sage: solutions = [[0,1], [2,3], [4,5]]
|
|
1088
|
+
sage: d.one_solution_using_milp_solver() in solutions # needs sage.numerical.mip
|
|
1089
|
+
True
|
|
1090
|
+
|
|
1091
|
+
Using optional solvers::
|
|
1092
|
+
|
|
1093
|
+
sage: # optional - gurobi sage_numerical_backends_gurobi, needs sage.numerical.mip
|
|
1094
|
+
sage: s = d.one_solution_using_milp_solver('gurobi')
|
|
1095
|
+
sage: s in solutions
|
|
1096
|
+
True
|
|
1097
|
+
|
|
1098
|
+
When no solution is found::
|
|
1099
|
+
|
|
1100
|
+
sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]]
|
|
1101
|
+
sage: d = dlx_solver(rows)
|
|
1102
|
+
sage: d.one_solution_using_milp_solver() is None # needs sage.numerical.mip
|
|
1103
|
+
True
|
|
1104
|
+
"""
|
|
1105
|
+
from sage.numerical.mip import MIPSolverException
|
|
1106
|
+
p, x = self.to_milp(solver)
|
|
1107
|
+
try:
|
|
1108
|
+
p.solve()
|
|
1109
|
+
except MIPSolverException:
|
|
1110
|
+
return None
|
|
1111
|
+
|
|
1112
|
+
soln = p.get_values(x, convert=bool, tolerance=integrality_tolerance)
|
|
1113
|
+
return sorted(key for key in soln if soln[key])
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
def dlx_solver(rows):
|
|
1117
|
+
"""
|
|
1118
|
+
Internal-use wrapper for the dancing links C++ code.
|
|
1119
|
+
|
|
1120
|
+
EXAMPLES::
|
|
1121
|
+
|
|
1122
|
+
sage: from sage.combinat.matrices.dancing_links import dlx_solver
|
|
1123
|
+
sage: rows = [[0,1,2]]
|
|
1124
|
+
sage: rows+= [[0,2]]
|
|
1125
|
+
sage: rows+= [[1]]
|
|
1126
|
+
sage: rows+= [[3]]
|
|
1127
|
+
sage: x = dlx_solver(rows)
|
|
1128
|
+
sage: print(x.search())
|
|
1129
|
+
1
|
|
1130
|
+
sage: print(x.get_solution())
|
|
1131
|
+
[3, 0]
|
|
1132
|
+
sage: print(x.search())
|
|
1133
|
+
1
|
|
1134
|
+
sage: print(x.get_solution())
|
|
1135
|
+
[3, 1, 2]
|
|
1136
|
+
sage: print(x.search())
|
|
1137
|
+
0
|
|
1138
|
+
"""
|
|
1139
|
+
return dancing_linksWrapper(rows)
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
def make_dlxwrapper(s):
|
|
1143
|
+
"""
|
|
1144
|
+
Create a dlx wrapper from a Python *string* s.
|
|
1145
|
+
|
|
1146
|
+
This was historically used in unpickling and is kept for backwards
|
|
1147
|
+
compatibility. We expect s to be ``dumps(rows)`` where rows is the
|
|
1148
|
+
list of rows used to instantiate the object.
|
|
1149
|
+
|
|
1150
|
+
TESTS::
|
|
1151
|
+
|
|
1152
|
+
sage: from sage.combinat.matrices.dancing_links import make_dlxwrapper
|
|
1153
|
+
sage: rows = [[0,1,2]]
|
|
1154
|
+
sage: x = make_dlxwrapper(dumps(rows))
|
|
1155
|
+
sage: print(x.__str__())
|
|
1156
|
+
Dancing links solver for 3 columns and 1 rows
|
|
1157
|
+
"""
|
|
1158
|
+
from sage.misc.persist import loads
|
|
1159
|
+
return dancing_linksWrapper(loads(s))
|