From d1a3d23a9b58deaff0592b22a9a2b3cfd40ebef7 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 20 Feb 2021 10:00:13 +0100 Subject: [PATCH 001/336] 31069: flint declarations --- src/sage/libs/flint/flint_wrap.h | 1 + src/sage/libs/flint/fmpz_mod.pxd | 46 +++ src/sage/libs/flint/fmpz_mod_poly.pxd | 505 +++++++++++++++++++++++++- src/sage/libs/flint/thread_pool.pxd | 57 +++ src/sage/libs/flint/types.pxd | 40 +- 5 files changed, 643 insertions(+), 6 deletions(-) create mode 100644 src/sage/libs/flint/fmpz_mod.pxd create mode 100644 src/sage/libs/flint/thread_pool.pxd diff --git a/src/sage/libs/flint/flint_wrap.h b/src/sage/libs/flint/flint_wrap.h index 9c0ca4e88b1..ae02e473a95 100644 --- a/src/sage/libs/flint/flint_wrap.h +++ b/src/sage/libs/flint/flint_wrap.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/src/sage/libs/flint/fmpz_mod.pxd b/src/sage/libs/flint/fmpz_mod.pxd new file mode 100644 index 00000000000..555b3123398 --- /dev/null +++ b/src/sage/libs/flint/fmpz_mod.pxd @@ -0,0 +1,46 @@ +# distutils: libraries = flint +# distutils: depends = flint/fmpz_mod.h + +# flint/fmpz_mod.h +cdef extern from "flint_wrap.h": + void fmpz_mod_ctx_init(fmpz_mod_ctx_t ctx, const fmpz_t n) + void fmpz_mod_ctx_init_ui(fmpz_mod_ctx_t ctx, ulong n) + void fmpz_mod_ctx_clear(fmpz_mod_ctx_t ctx) + + const fmpz * fmpz_mod_ctx_modulus(const fmpz_mod_ctx_t ctx) + + void fmpz_mod_ctx_set_modulus(fmpz_mod_ctx_t ctx, const fmpz_t n) + void fmpz_mod_ctx_set_modulus_ui(fmpz_mod_ctx_t ctx, ulong n) + + int fmpz_mod_is_canonical(const fmpz_t a, const fmpz_mod_ctx_t ctx) + void fmpz_mod_assert_canonical(const fmpz_t a, const fmpz_mod_ctx_t ctx) + + int fmpz_mod_is_one(const fmpz_t a, const fmpz_mod_ctx_t ctx) + void fmpz_mod_add(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_sub(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_neg(fmpz_t a, const fmpz_t b, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_mul(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_inv(fmpz_t a, const fmpz_t b, const fmpz_mod_ctx_t ctx) + + int fmpz_mod_divides(fmpz_t a, const fmpz_t b, const fmpz_t c, const fmpz_mod_ctx_t ctx) + void fmpz_mod_pow_ui(fmpz_t a, const fmpz_t b, ulong pow, const fmpz_mod_ctx_t ctx) + int fmpz_mod_pow_fmpz(fmpz_t a, const fmpz_t b, const fmpz_t pow, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_discrete_log_pohlig_hellman_init(fmpz_mod_discrete_log_pohlig_hellman_t L) + + void fmpz_mod_discrete_log_pohlig_hellman_clear(fmpz_mod_discrete_log_pohlig_hellman_t L) + + double fmpz_mod_discrete_log_pohlig_hellman_precompute_prime( + fmpz_mod_discrete_log_pohlig_hellman_t L, + const fmpz_t p) + + void fmpz_mod_discrete_log_pohlig_hellman_run( + fmpz_t x, + const fmpz_mod_discrete_log_pohlig_hellman_t L, + const fmpz_t y) + + const fmpz * fmpz_mod_discrete_log_pohlig_hellman_primitive_root( + fmpz_mod_discrete_log_pohlig_hellman_t L) + + int fmpz_next_smooth_prime(fmpz_t a, const fmpz_t b) diff --git a/src/sage/libs/flint/fmpz_mod_poly.pxd b/src/sage/libs/flint/fmpz_mod_poly.pxd index 159033c4861..24b653eb3b7 100644 --- a/src/sage/libs/flint/fmpz_mod_poly.pxd +++ b/src/sage/libs/flint/fmpz_mod_poly.pxd @@ -1,12 +1,507 @@ # distutils: libraries = flint # distutils: depends = flint/fmpz_mod_poly.h -from sage.libs.flint.types cimport fmpz_mod_poly_t, fmpz_t, slong +from sage.libs.flint.types cimport * +from sage.libs.flint.thread_pool cimport * # flint/fmpz_mod_poly.h cdef extern from "flint_wrap.h": - void fmpz_mod_poly_init(fmpz_mod_poly_t poly, const fmpz_t p) - void fmpz_mod_poly_clear(fmpz_mod_poly_t poly) + void fmpz_mod_poly_init(fmpz_mod_poly_t poly, const fmpz_t p, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_clear(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) - void fmpz_mod_poly_set_coeff_fmpz(fmpz_mod_poly_t poly, slong n, const fmpz_t x) - void fmpz_mod_poly_get_coeff_fmpz(fmpz_t x, const fmpz_mod_poly_t poly, slong n) + void fmpz_mod_poly_realloc(fmpz_mod_poly_t poly, slong alloc, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_fit_length(fmpz_mod_poly_t poly, slong len, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_truncate(fmpz_mod_poly_t poly, slong len, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_set_trunc(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + + slong fmpz_mod_poly_degree(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + slong fmpz_mod_poly_length(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + fmpz * fmpz_mod_poly_lead(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_one(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + int fmpz_mod_poly_is_gen(const fmpz_mod_poly_t op, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_set(fmpz_mod_poly_t poly1, + const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_swap(fmpz_mod_poly_t poly1, + fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_reverse(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_zero(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_one(fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_zero_coeffs(fmpz_mod_poly_t poly, + slong i, slong j, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_set_ui(fmpz_mod_poly_t f, ulong x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_set_fmpz(fmpz_mod_poly_t poly, const fmpz_t c, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_set_fmpz_poly(fmpz_mod_poly_t f, + const fmpz_poly_t g, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_get_fmpz_poly(fmpz_poly_t f, + const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + + int fmpz_mod_poly_equal(const fmpz_mod_poly_t poly1, + const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + + int fmpz_mod_poly_equal_trunc(const fmpz_mod_poly_t poly1, + const fmpz_mod_poly_t poly2, slong n, const fmpz_mod_ctx_t ctx) + + int fmpz_mod_poly_is_zero(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_set_coeff_fmpz(fmpz_mod_poly_t poly, slong n, + const fmpz_t x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_set_coeff_ui(fmpz_mod_poly_t poly, slong n, + ulong x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_set_coeff_si(fmpz_mod_poly_t poly, slong n, + slong x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_get_coeff_fmpz(fmpz_t x, const fmpz_mod_poly_t poly, + slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_set_coeff_mpz(fmpz_mod_poly_t poly, + slong n, const mpz_t x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_get_coeff_mpz(mpz_t x, + const fmpz_mod_poly_t poly, slong n, const fmpz_mod_ctx_t ctx) + + + void _fmpz_mod_poly_shift_left(fmpz * res, const fmpz * poly, + slong len, slong n) + + void fmpz_mod_poly_shift_left(fmpz_mod_poly_t f, + const fmpz_mod_poly_t g, slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_shift_right(fmpz_mod_poly_t f, + const fmpz_mod_poly_t g, slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_add(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, + const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_add_series(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_sub(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, + const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_sub_series(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_neg(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_scalar_mul_fmpz(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_t x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_scalar_mul_ui(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, ulong x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_scalar_div_fmpz(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_t x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_mul(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, + const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_mullow(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_sqr(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_mulmod(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_mulmod_preinv(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_pow(fmpz_mod_poly_t rop, const fmpz_mod_poly_t op, + ulong e, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_pow_trunc(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, ulong e, slong trunc, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_pow_trunc_binexp(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, ulong e, slong trunc, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_powmod_ui_binexp(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, ulong e, + const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_powmod_ui_binexp_preinv(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, ulong e, + const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_powmod_fmpz_binexp(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_t e, + const fmpz_mod_poly_t f, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_powmod_fmpz_binexp_preinv(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_t e, + const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_powmod_x_fmpz_preinv(fmpz_mod_poly_t res, + const fmpz_t e, const fmpz_mod_poly_t f, const fmpz_mod_poly_t finv, + const fmpz_mod_ctx_t ctx) + + + void fmpz_mod_poly_powers_mod_naive(fmpz_mod_poly_struct * res, + const fmpz_mod_poly_t f, slong n, const fmpz_mod_poly_t g, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_powers_mod_bsgs(fmpz_mod_poly_struct * res, + const fmpz_mod_poly_t f, slong n, const fmpz_mod_poly_t g, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_frobenius_powers_2exp_precomp( + fmpz_mod_poly_frobenius_powers_2exp_t pow, const fmpz_mod_poly_t f, + const fmpz_mod_poly_t finv, ulong m, const fmpz_mod_ctx_t ctx) + + + void fmpz_mod_poly_frobenius_powers_2exp_clear( + fmpz_mod_poly_frobenius_powers_2exp_t pow, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_frobenius_power(fmpz_mod_poly_t res, + fmpz_mod_poly_frobenius_powers_2exp_t pow, + const fmpz_mod_poly_t f, ulong m, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_frobenius_powers_precomp( + fmpz_mod_poly_frobenius_powers_t pow, const fmpz_mod_poly_t f, + const fmpz_mod_poly_t finv, ulong m, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_frobenius_powers_clear( + fmpz_mod_poly_frobenius_powers_t pow, const fmpz_mod_ctx_t ctx) + + + void fmpz_mod_poly_divrem_basecase(fmpz_mod_poly_t Q, fmpz_mod_poly_t R, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_div_basecase(fmpz_mod_poly_t Q, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_div_newton_n_preinv(fmpz_mod_poly_t Q, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_poly_t Binv, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_divrem_newton_n_preinv(fmpz_mod_poly_t Q, + fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_poly_t Binv, const fmpz_mod_ctx_t ctx) + + ulong fmpz_mod_poly_remove(fmpz_mod_poly_t f, + const fmpz_mod_poly_t p, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_rem_basecase(fmpz_mod_poly_t R, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_divrem_divconquer(fmpz_mod_poly_t Q, + fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_divrem(fmpz_mod_poly_t Q, fmpz_mod_poly_t R, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_divrem_f(fmpz_t f, fmpz_mod_poly_t Q, + fmpz_mod_poly_t R, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_rem(fmpz_mod_poly_t R, const fmpz_mod_poly_t A, + const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + + + void fmpz_mod_poly_rem_f(fmpz_t f, fmpz_mod_poly_t R, const fmpz_mod_poly_t A, + const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + + + void fmpz_mod_poly_inv_series_newton(fmpz_mod_poly_t Qinv, + const fmpz_mod_poly_t Q, slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_inv_series_newton_f(fmpz_t f, fmpz_mod_poly_t Qinv, + const fmpz_mod_poly_t Q, slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_inv_series(fmpz_mod_poly_t Qinv, const fmpz_mod_poly_t Q, + slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_inv_series_f(fmpz_t f, fmpz_mod_poly_t Qinv, + const fmpz_mod_poly_t Q, slong n, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_div_series(fmpz_mod_poly_t Q, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, slong n, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_make_monic(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_make_monic_f(fmpz_t f, fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_gcd_euclidean(fmpz_mod_poly_t G, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_gcd_euclidean_f(fmpz_t f, fmpz_mod_poly_t G, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_gcd_f(fmpz_t f, fmpz_mod_poly_t G, const fmpz_mod_poly_t A, + const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_gcd_hgcd(fmpz_mod_poly_t G, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + + void fmpz_mod_poly_gcd(fmpz_mod_poly_t G, const fmpz_mod_poly_t A, + const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + + + void fmpz_mod_poly_xgcd_euclidean(fmpz_mod_poly_t G, + fmpz_mod_poly_t S, fmpz_mod_poly_t T, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_xgcd_euclidean_f(fmpz_t f, fmpz_mod_poly_t G, + fmpz_mod_poly_t S, fmpz_mod_poly_t T, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_xgcd_hgcd(fmpz_mod_poly_t G, fmpz_mod_poly_t S, + fmpz_mod_poly_t T, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_xgcd(fmpz_mod_poly_t G, fmpz_mod_poly_t S, fmpz_mod_poly_t T, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_xgcd_f(fmpz_t f, fmpz_mod_poly_t G, fmpz_mod_poly_t S, + fmpz_mod_poly_t T, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_gcdinv_euclidean_f(fmpz_t f, fmpz_mod_poly_t G, + fmpz_mod_poly_t S, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_gcdinv_euclidean(fmpz_mod_poly_t G, + fmpz_mod_poly_t S, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_gcdinv(fmpz_mod_poly_t G, fmpz_mod_poly_t S, + const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_gcdinv_f(fmpz_t f, fmpz_mod_poly_t G, + fmpz_mod_poly_t S, const fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_ctx_t ctx) + + int fmpz_mod_poly_invmod(fmpz_mod_poly_t A, const fmpz_mod_poly_t B, + const fmpz_mod_poly_t P, const fmpz_mod_ctx_t ctx) + + int fmpz_mod_poly_invmod_f(fmpz_t f, fmpz_mod_poly_t A, + const fmpz_mod_poly_t B, const fmpz_mod_poly_t P, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_minpoly_bm(fmpz_mod_poly_t poly, const fmpz* seq, slong len, + const fmpz_mod_ctx_t ctx) + void fmpz_mod_poly_minpoly_hgcd(fmpz_mod_poly_t poly, const fmpz* seq, slong len, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_minpoly(fmpz_mod_poly_t poly, const fmpz* seq, slong len, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_resultant_euclidean(fmpz_t r, + const fmpz_mod_poly_t f, const fmpz_mod_poly_t g, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_resultant_hgcd(fmpz_t res, const fmpz_mod_poly_t A, + const fmpz_mod_poly_t B, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_resultant(fmpz_t res, const fmpz_mod_poly_t f, + const fmpz_mod_poly_t g, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_discriminant(fmpz_t d, const fmpz_mod_poly_t f, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_derivative(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_evaluate_fmpz(fmpz_t res, + const fmpz_mod_poly_t poly, const fmpz_t a, + const fmpz_mod_ctx_t ctx) + + fmpz_poly_struct ** _fmpz_mod_poly_tree_alloc(slong len) + + void fmpz_mod_poly_evaluate_fmpz_vec_iter(fmpz * ys, + const fmpz_mod_poly_t poly, const fmpz * xs, slong n, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_evaluate_fmpz_vec_fast(fmpz * ys, + const fmpz_mod_poly_t poly, const fmpz * xs, slong n, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_evaluate_fmpz_vec(fmpz * ys, + const fmpz_mod_poly_t poly, const fmpz * xs, slong n, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_horner(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_divconquer(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose(fmpz_mod_poly_t res, const fmpz_mod_poly_t poly1, + const fmpz_mod_poly_t poly2, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_mod(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_poly_t poly3, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_mod_brent_kung(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_poly_t poly3, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_precompute_matrix(fmpz_mat_t A, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_poly_t poly2inv, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_mod_brent_kung_precomp_preinv(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mat_t A, + const fmpz_mod_poly_t poly3, const fmpz_mod_poly_t poly3inv, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_mod_brent_kung_preinv(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_poly_t poly3, const fmpz_mod_poly_t poly3inv, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_mod_horner(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly1, const fmpz_mod_poly_t poly2, + const fmpz_mod_poly_t poly3, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_mod_brent_kung_vec_preinv( + fmpz_mod_poly_struct * res, const fmpz_mod_poly_struct * polys, + slong len1,slong n, const fmpz_mod_poly_t g, const fmpz_mod_poly_t poly, + const fmpz_mod_poly_t polyinv, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_compose_mod_brent_kung_vec_preinv_threaded_pool(fmpz_mod_poly_struct * res, + const fmpz_mod_poly_struct * polys, slong len1, slong n, + const fmpz_mod_poly_t g, const fmpz_mod_poly_t poly, + const fmpz_mod_poly_t polyinv, const fmpz_mod_ctx_t ctx, + thread_pool_handle * threads, slong num_threads) + + void fmpz_mod_poly_compose_mod_brent_kung_vec_preinv_threaded(fmpz_mod_poly_struct * res, + const fmpz_mod_poly_struct * polys, slong len1, slong n, + const fmpz_mod_poly_t g, const fmpz_mod_poly_t poly, + const fmpz_mod_poly_t polyinv, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_radix_init(fmpz_mod_poly_radix_t D, + const fmpz_mod_poly_t R, slong degF, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_radix_clear(fmpz_mod_poly_radix_t D) + + void fmpz_mod_poly_radix(fmpz_mod_poly_struct **B, const fmpz_mod_poly_t F, + const fmpz_mod_poly_radix_t D, const fmpz_mod_ctx_t ctx) + + int fmpz_mod_poly_fprint(FILE * file, const fmpz_mod_poly_t poly, + const fmpz_mod_ctx_t ctx) + + int fmpz_mod_poly_fread(FILE * file, fmpz_mod_poly_t poly, + fmpz_mod_ctx_t ctx) + + + int fmpz_mod_poly_fprint_pretty(FILE * file, const fmpz_mod_poly_t poly, + const char * x, const fmpz_mod_ctx_t ctx) + + + + int fmpz_mod_poly_print(const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + + int fmpz_mod_poly_print_pretty(const fmpz_mod_poly_t poly, const char * x, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_product_roots_fmpz_vec(fmpz_poly_t poly, const fmpz * xs, + slong n, const fmpz_t mod) + + int fmpz_mod_poly_find_distinct_nonzero_roots(fmpz * roots, + const fmpz_mod_poly_t P, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_berlekamp_massey_init( + fmpz_mod_berlekamp_massey_t B, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_berlekamp_massey_start_over( + fmpz_mod_berlekamp_massey_t B, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_berlekamp_massey_clear(fmpz_mod_berlekamp_massey_t B, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_berlekamp_massey_print( + const fmpz_mod_berlekamp_massey_t B, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_berlekamp_massey_add_points( + fmpz_mod_berlekamp_massey_t B, const fmpz * a, slong count, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_berlekamp_massey_add_zeros( + fmpz_mod_berlekamp_massey_t B, slong count, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_berlekamp_massey_add_point( + fmpz_mod_berlekamp_massey_t B, const fmpz_t a, + const fmpz_mod_ctx_t ctx) + + void fmpz_mod_berlekamp_massey_add_point_ui( + fmpz_mod_berlekamp_massey_t B, ulong a, + const fmpz_mod_ctx_t ctx) + + int fmpz_mod_berlekamp_massey_reduce( + fmpz_mod_berlekamp_massey_t B, const fmpz_mod_ctx_t ctx) + + const fmpz * fmpz_mod_berlekamp_massey_points( + const fmpz_mod_berlekamp_massey_t B) + + slong fmpz_mod_berlekamp_massey_point_count( + const fmpz_mod_berlekamp_massey_t B) + + const fmpz_mod_poly_struct * fmpz_mod_berlekamp_massey_V_poly( + const fmpz_mod_berlekamp_massey_t B) + + const fmpz_mod_poly_struct * fmpz_mod_berlekamp_massey_R_poly( + const fmpz_mod_berlekamp_massey_t B) + + void fmpz_mod_poly_add_si(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, slong c, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_sub_si(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, slong c, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_si_sub(fmpz_mod_poly_t res, slong c, + const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_add_fmpz(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, fmpz_t c, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_sub_fmpz(fmpz_mod_poly_t res, + const fmpz_mod_poly_t poly, fmpz_t c, const fmpz_mod_ctx_t ctx) + + void fmpz_mod_poly_fmpz_sub(fmpz_mod_poly_t res, fmpz_t c, + const fmpz_mod_poly_t poly, const fmpz_mod_ctx_t ctx) diff --git a/src/sage/libs/flint/thread_pool.pxd b/src/sage/libs/flint/thread_pool.pxd new file mode 100644 index 00000000000..b773695d2de --- /dev/null +++ b/src/sage/libs/flint/thread_pool.pxd @@ -0,0 +1,57 @@ +# distutils: libraries = flint +# distutils: depends = flint/thread_pool.h + +#***************************************************************************** +# Copyright (C) 2021 Vincent Delecroix +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.libs.flint.types cimport slong + +# flint/thread_pool.h +cdef extern from "flint/thread_pool.h": + ctypedef struct thread_pool_entry_struct: + pass + + ctypedef thread_pool_entry_struct thread_pool_entry_t[1]; + + ctypedef struct thread_pool_struct: + pass + + ctypedef thread_pool_struct thread_pool_t[1] + ctypedef int thread_pool_handle + + + extern thread_pool_t global_thread_pool + extern int global_thread_pool_initialized + + void * thread_pool_idle_loop(void * varg) + + void thread_pool_init(thread_pool_t T, slong l) + + int thread_pool_set_affinity(thread_pool_t T, + int * cpus, slong length) + + int thread_pool_restore_affinity(thread_pool_t T) + + slong thread_pool_get_size(thread_pool_t T) + + int thread_pool_set_size(thread_pool_t T, slong new_size) + + slong thread_pool_request(thread_pool_t T, + thread_pool_handle * out, slong requested) + + void thread_pool_wake(thread_pool_t T, thread_pool_handle i, + int max_workers, void (*f)(void*), void * a) + + void thread_pool_wait(thread_pool_t T, thread_pool_handle i) + + void thread_pool_give_back(thread_pool_t T, thread_pool_handle i) + + void thread_pool_clear(thread_pool_t T) + diff --git a/src/sage/libs/flint/types.pxd b/src/sage/libs/flint/types.pxd index 3e0d595ee9f..7208f89d9aa 100644 --- a/src/sage/libs/flint/types.pxd +++ b/src/sage/libs/flint/types.pxd @@ -42,6 +42,19 @@ cdef extern from "flint_wrap.h": ctypedef fmpz_preinvn_struct[1] fmpz_preinvn_t +# flint/fmpz_mod.h: +cdef extern from "flint_wrap.h": + ctypedef struct fmpz_mod_ctx_struct: + pass + + ctypedef fmpz_mod_ctx_struct fmpz_mod_ctx_t[1] + + ctypedef struct fmpz_mod_discrete_log_pohlig_hellman_entry_struct: + pass + ctypedef struct fmpz_mod_discrete_log_pohlig_hellman_struct: + pass + ctypedef fmpz_mod_discrete_log_pohlig_hellman_struct fmpz_mod_discrete_log_pohlig_hellman_t[1] + # flint/fmpz_poly.h: cdef extern from "flint_wrap.h": ctypedef struct fmpz_poly_struct: @@ -90,9 +103,34 @@ cdef extern from "flint_wrap.h": cdef extern from "flint_wrap.h": ctypedef struct fmpz_mod_poly_struct: pass - ctypedef fmpz_mod_poly_struct fmpz_mod_poly_t[1] + ctypedef struct fmpz_mod_poly_res_struct: + pass + ctypedef fmpz_mod_poly_res_struct fmpz_mod_poly_res_t[1] + + ctypedef struct fmpz_mod_poly_frobenius_powers_2exp_struct: + pass + ctypedef fmpz_mod_poly_frobenius_powers_2exp_struct fmpz_mod_poly_frobenius_powers_2exp_t[1] + + ctypedef struct fmpz_mod_poly_frobenius_powers_struct: + pass + ctypedef fmpz_mod_poly_frobenius_powers_struct fmpz_mod_poly_frobenius_powers_t[1] + + ctypedef struct fmpz_mod_poly_matrix_precompute_arg_t: + pass + + ctypedef struct fmpz_mod_poly_compose_mod_precomp_preinv_arg_t: + pass + + ctypedef struct fmpz_mod_poly_radix_struct: + pass + ctypedef fmpz_mod_poly_radix_struct fmpz_mod_poly_radix_t[1] + + ctypedef struct fmpz_mod_berlekamp_massey_struct: + pass + ctypedef fmpz_mod_berlekamp_massey_struct fmpz_mod_berlekamp_massey_t[1] + # flint/nmod_poly.h: cdef extern from "flint_wrap.h": ctypedef struct nmod_t: From 7b1dee7116068c14c86b930f4a52dc132173a296 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 19 Mar 2021 16:18:38 -0700 Subject: [PATCH 002/336] build/pkgs/flint/spkg-configure.m4: Reject FLINT < 2.6.0 --- build/pkgs/flint/spkg-configure.m4 | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/build/pkgs/flint/spkg-configure.m4 b/build/pkgs/flint/spkg-configure.m4 index b697e478d74..d7e068049f3 100644 --- a/build/pkgs/flint/spkg-configure.m4 +++ b/build/pkgs/flint/spkg-configure.m4 @@ -1,14 +1,8 @@ SAGE_SPKG_CONFIGURE([flint], [ - AC_REQUIRE([SAGE_SPKG_CONFIGURE_MPFR]) - AC_REQUIRE([SAGE_SPKG_CONFIGURE_NTL]) - AC_MSG_CHECKING([installing mpfr or ntl? ]) - if test x$sage_spkg_install_mpfr = xyes -o x$sage_spkg_install_ntl = xyes; then - AC_MSG_RESULT([yes; install flint as well]) - sage_spkg_install_flint=yes - else + SAGE_SPKG_DEPCHECK([mpfr ntl], [ AC_CHECK_HEADER(flint/flint.h, [ - dnl fmpz_mat_is_hadamard appears in Flint 2.5.0 - AC_SEARCH_LIBS([fmpz_mat_is_hadamard], [flint], [ + dnl fmpz_mod_ctx_init appears in Flint 2.6.0 + AC_SEARCH_LIBS([fmpz_mod_ctx_init], [flint], [ dnl check that NTL is linked in AC_SEARCH_LIBS([fmpz_poly_get_ZZX], [flint], [ @@ -26,13 +20,11 @@ SAGE_SPKG_CONFIGURE([flint], [ ], [sage_spkg_install_flint=yes]) ], [sage_spkg_install_flint=yes]) ], [sage_spkg_install_flint=yes]) - fi + ]) ], [], [], [ if test x$sage_spkg_install_flint = xyes; then AC_SUBST(SAGE_FLINT_PREFIX, ['$SAGE_LOCAL']) - AC_MSG_RESULT([using Sage's flint SPKG]) else AC_SUBST(SAGE_FLINT_PREFIX, ['']) - AC_MSG_RESULT([using flint library from the system]) fi ]) From c3dcc343cce025fb0d1706863f8d5dc201a10391 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sat, 27 Mar 2021 08:54:36 +0100 Subject: [PATCH 003/336] Trac 31582: use existing attributes and methods of Ideal_fractional, and use _richcmp_() for comparison --- .../algebras/quatalg/quaternion_algebra.py | 78 ++++++------------- 1 file changed, 22 insertions(+), 56 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 324392f7165..a61fb26b280 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1820,6 +1820,16 @@ def __init__(self, basis, left_order=None, right_order=None, check=True): Fractional ideal (1/2 + 1/2*i, i, 1/2*j + 1/2*k, k) sage: R.right_ideal(tuple(R.basis()), check=False) Fractional ideal (1/2 + 1/2*i, 1/2*j - 1/2*k, i, -k) + + TESTS:: + + sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().gens() + (1/2 + 1/2*i, 1/2*j - 1/2*k, i, -k) + + Check that :trac:`31582` is fixed:: + + sage: BrandtModule(23).right_ideals()[0].parent() + Monoid of ideals of Quaternion Algebra (-1, -23) with base ring Rational Field """ if check: if left_order is not None and not isinstance(left_order, QuaternionOrder): @@ -1839,7 +1849,7 @@ def __init__(self, basis, left_order=None, right_order=None, check=True): Q = basis[0].parent() basis = tuple([Q(v) for v in (QQ**4).span([Q(v).coefficient_tuple() for v in basis], ZZ).basis()]) - self.__basis = basis + Ideal_fractional.__init__(self, basis[0].parent(), basis) def scale(self, alpha, left=False): r""" @@ -1862,11 +1872,11 @@ def scale(self, alpha, left=False): sage: B = BrandtModule(5,37); I = B.right_ideals()[0]; i,j,k = B.quaternion_algebra().gens(); I Fractional ideal (2 + 2*j + 106*k, i + 2*j + 105*k, 4*j + 64*k, 148*k) sage: I.scale(i) - Fractional ideal [2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j] + Fractional ideal (2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j) sage: I.scale(i, left=True) - Fractional ideal [2*i - 212*j + 2*k, -2 - 210*j + 2*k, -128*j + 4*k, -296*j] + Fractional ideal (2*i - 212*j + 2*k, -2 - 210*j + 2*k, -128*j + 4*k, -296*j) sage: I.scale(i, left=False) - Fractional ideal [2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j] + Fractional ideal (2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j) sage: i * I.gens()[0] 2*i - 212*j + 2*k sage: I.gens()[0] * i @@ -1894,13 +1904,7 @@ def quaternion_algebra(self): sage: I.quaternion_algebra() Quaternion Algebra (-1, -3) with base ring Rational Field """ - try: - return self.__quaternion_algebra - except AttributeError: - pass - A = self.__basis[0].parent() - self.__quaternion_algebra = A - return A + return Ideal_fractional.ring(self) def _compute_order(self, side='left'): r""" @@ -2095,34 +2099,12 @@ def basis(self): sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().basis() (1/2 + 1/2*i, 1/2*j - 1/2*k, i, -k) """ - return self.__basis - - def gens(self): - r""" - Return the generators for this ideal, which are the same as - the `\ZZ`-basis for this ideal. - - EXAMPLES:: - - sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().gens() - (1/2 + 1/2*i, 1/2*j - 1/2*k, i, -k) - """ - return self.__basis + return self.gens() - def __eq__(self, right): + def _richcmp_(self, right, op): """ Compare this fractional quaternion ideal to ``right``. - If ``right`` is not a fractional quaternion ideal, return ``False``. - - If the fractional ideals are in different ambient - quaternion algebras, then the quaternion algebras themselves - are compared. - - INPUT: - - - ``right`` - another fractional quaternion ideal - EXAMPLES:: sage: I = QuaternionAlgebra(-11,-1).maximal_order().unit_ideal() @@ -2130,26 +2112,10 @@ def __eq__(self, right): True sage: I == 5 False - """ - if not isinstance(right, QuaternionFractionalIdeal_rational): - return False - return self.__basis == right.__basis - - def __ne__(self, other): - """ - Compare this fractional quaternion ideal to ``right``. - - INPUT: - - - ``right`` - another fractional quaternion ideal - - EXAMPLES:: - - sage: I = QuaternionAlgebra(-11,-1).maximal_order().unit_ideal() sage: I != I # indirect doctest False """ - return not self.__eq__(other) + return self.basis_matrix()._richcmp_(right.basis_matrix(), op) def __hash__(self): """ @@ -2166,7 +2132,7 @@ def __hash__(self): sage: R = QuaternionAlgebra(-11,-1).maximal_order() sage: H = hash(R.right_ideal(R.basis())) """ - return hash(self.__basis) + return hash(self.gens()) def basis_matrix(self): r""" @@ -2192,7 +2158,7 @@ def basis_matrix(self): return self.__hermite_basis_matrix except AttributeError: pass - B = quaternion_algebra_cython.rational_matrix_from_rational_quaternions(self.__basis) + B = quaternion_algebra_cython.rational_matrix_from_rational_quaternions(self.gens()) self.__hermite_basis_matrix = B return B @@ -2328,8 +2294,8 @@ def gram_matrix(self): [ 2112 13440 13056 15360] [ 1920 16320 15360 19200] """ - A = self.__basis - B = [z.conjugate() for z in self.__basis] + A = self.gens() + B = [z.conjugate() for z in A] two = QQ(2) m = [two * (a * b).reduced_trace() for b in B for a in A] M44 = MatrixSpace(QQ, 4, 4) From 5c453e90a8fac30546d20ea0ae69398a0bda67f8 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sat, 27 Mar 2021 10:54:21 +0100 Subject: [PATCH 004/336] Trac 31582: add argument Q to QuaternionFractionalIdeal_rational.__init__() --- .../algebras/quatalg/quaternion_algebra.py | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index a61fb26b280..d0856393344 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1101,7 +1101,7 @@ def ideal(self, gens, left_order=None, right_order=None, check=True, **kwds): Fractional ideal (2, 2*i, 2*j, 2*k) """ if self.base_ring() == QQ: - return QuaternionFractionalIdeal_rational(gens, left_order=left_order, right_order=right_order, check=check) + return QuaternionFractionalIdeal_rational(self, gens, left_order=left_order, right_order=right_order, check=check) else: raise NotImplementedError("ideal only implemented for quaternion algebras over QQ") @@ -1661,7 +1661,7 @@ def left_ideal(self, gens, check=True): Fractional ideal (1 + i, 2*i, j + k, 2*k) """ if self.base_ring() == ZZ: - return QuaternionFractionalIdeal_rational(gens, left_order=self, check=check) + return QuaternionFractionalIdeal_rational(self.quaternion_algebra(), gens, left_order=self, check=check) else: raise NotImplementedError("ideal only implemented for quaternion algebras over QQ") @@ -1683,7 +1683,7 @@ def right_ideal(self, gens, check=True): Fractional ideal (1 + i, 2*i, j + k, 2*k) """ if self.base_ring() == ZZ: - return QuaternionFractionalIdeal_rational(gens, right_order=self, check=check) + return QuaternionFractionalIdeal_rational(self.quaternion_algebra(), gens, right_order=self, check=check) else: raise NotImplementedError("ideal only implemented for quaternion algebras over QQ") @@ -1698,7 +1698,7 @@ def unit_ideal(self): Fractional ideal (1/2 + 1/2*i, 1/2*j - 1/2*k, i, -k) """ if self.base_ring() == ZZ: - return QuaternionFractionalIdeal_rational(self.basis(), left_order=self, right_order=self, check=False) + return QuaternionFractionalIdeal_rational(self.quaternion_algebra(), self.basis(), left_order=self, right_order=self, check=False) else: raise NotImplementedError("ideal only implemented for quaternion algebras over QQ") @@ -1811,7 +1811,7 @@ class QuaternionFractionalIdeal_rational(QuaternionFractionalIdeal): - ``check`` -- bool (default: ``True``); if ``False``, do no type checking, and the input basis *must* be in Hermite form. """ - def __init__(self, basis, left_order=None, right_order=None, check=True): + def __init__(self, Q, basis, left_order=None, right_order=None, check=True): """ EXAMPLES:: @@ -1838,18 +1838,11 @@ def __init__(self, basis, left_order=None, right_order=None, check=True): raise TypeError("right_order must be a quaternion order or None") if not isinstance(basis, (list, tuple)): raise TypeError("basis must be a list or tuple") - - self.__left_order = left_order - self.__right_order = right_order - - if check: - try: - Q = self.quaternion_order().quaternion_algebra() - except RuntimeError: - Q = basis[0].parent() basis = tuple([Q(v) for v in (QQ**4).span([Q(v).coefficient_tuple() for v in basis], ZZ).basis()]) - Ideal_fractional.__init__(self, basis[0].parent(), basis) + self.__left_order = left_order + self.__right_order = right_order + Ideal_fractional.__init__(self, Q, basis) def scale(self, alpha, left=False): r""" From c8ee32284cb00b0c1a417d050536c7f1f2712f1e Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sat, 27 Mar 2021 17:07:06 +0100 Subject: [PATCH 005/336] Trac 31583: deprecate quaternion_order() and ring() methods of quaternion ideals --- .../algebras/quatalg/quaternion_algebra.py | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index d0856393344..cd14ecc9e55 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1897,6 +1897,8 @@ def quaternion_algebra(self): sage: I.quaternion_algebra() Quaternion Algebra (-1, -3) with base ring Rational Field """ + # TODO: when the ring() method is removed from this class, the + # following line can be changed to self.ring(). return Ideal_fractional.ring(self) def _compute_order(self, side='left'): @@ -2052,8 +2054,12 @@ def quaternion_order(self): sage: R = QuaternionAlgebra(-11,-1).maximal_order() sage: R.unit_ideal().quaternion_order() is R + doctest:...: DeprecationWarning: quaternion_order() is deprecated, please use left_order() or right_order() + See https://trac.sagemath.org/31583 for details. True """ + from sage.misc.superseded import deprecation + deprecation(31583, 'quaternion_order() is deprecated, please use left_order() or right_order()') try: return self.__quaternion_order except AttributeError: @@ -2071,13 +2077,27 @@ def ring(self): """ Return ring that this is a fractional ideal for. + The :meth:`ring` method will be removed from this class in the + future. Calling :meth:`ring` will then return the ambient + quaternion algebra. This is consistent with the behaviour for + number fields. + EXAMPLES:: sage: R = QuaternionAlgebra(-11,-1).maximal_order() sage: R.unit_ideal().ring() is R + doctest:...: DeprecationWarning: ring() will return the quaternion algebra in the future, please use left_order() or right_order() + See https://trac.sagemath.org/31583 for details. True """ - return self.quaternion_order() + from sage.misc.superseded import deprecation + deprecation(31583, 'ring() will return the quaternion algebra in the future, please use left_order() or right_order()') + if self.__left_order is not None: + return self.__left_order + elif self.__right_order is not None: + return self.__right_order + else: + raise RuntimeError("unable to determine quaternion order of ideal without known order") def basis(self): """ @@ -2327,11 +2347,7 @@ def norm(self): assert r.is_square(), "first is bad!" r = r.sqrt() # If we know either the left- or the right order, use that one to compute the norm. - # Otherwise quaternion_order() will raise a RuntimeError and we compute the left order - try: - R = self.quaternion_order() - except RuntimeError: - R = self.left_order() + R = self.__left_order or self.__right_order or self.left_order() r/= R.discriminant() assert r.is_square(), "second is bad!" return r.sqrt() @@ -2509,7 +2525,7 @@ def is_equivalent(I, J, B=10): False sage: R[0].is_equivalent(R[0]) True - sage: OO = R[0].quaternion_order() + sage: OO = R[0].left_order() sage: S = OO.right_ideal([3*a for a in R[0].basis()]) sage: R[0].is_equivalent(S) True From 13b8f3fa915439e5edbc2df077c22fc679b6593e Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Fri, 2 Dec 2016 15:14:52 +0100 Subject: [PATCH 006/336] #21991 regression test for issue fixed in #24371 --- src/sage/rings/real_lazy.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index 6f64732bad4..24b679f8935 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -9,6 +9,15 @@ precision. The main purpose of these classes is to provide a place for exact rings (e.g. number fields) to embed for the coercion model (as only one embedding can be specified in the forward direction). + +TESTS: + +Bug :trac:`21991`:: + + sage: a = QuadraticField(5).gen() + sage: u = -573147844013817084101/2*a + 1281597540372340914251/2 + sage: RealIntervalField(128)(RLF(u)).is_exact() + False """ # **************************************************************************** From 0ac27ff147e7e5f1c41d83142894dc49869a171e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Apr 2021 09:22:33 -0700 Subject: [PATCH 007/336] build/pkgs/numpy/lapack_conf.py: Change #! line to use python3. Decorative change only - spkg-install.in already explicitly uses python3 to call this script --- build/pkgs/numpy/lapack_conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/numpy/lapack_conf.py b/build/pkgs/numpy/lapack_conf.py index f69a12fa504..eeb9ffaf72c 100644 --- a/build/pkgs/numpy/lapack_conf.py +++ b/build/pkgs/numpy/lapack_conf.py @@ -1,4 +1,4 @@ -#!/usr/bin/env sage-bootstrap-python +#!/usr/bin/env python3 import pkgconfig, os From 45e85eda56880a27116595627dfa52717d06bb0b Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 13 Apr 2021 00:37:43 +0200 Subject: [PATCH 008/336] Trac 30272: refactoring homogeneous components + introduce set_comp --- .../manifolds/differentiable/mixed_form.py | 73 +++++++++++-------- .../differentiable/mixed_form_algebra.py | 18 ++--- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index e9fdcdb9916..0fb8130d769 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -100,16 +100,10 @@ class MixedForm(AlgebraElement): A = [x] + [x*y dx] + [x*y^2 dx/\dy] sage: A[0] Scalar field f on the 2-dimensional differentiable manifold M - sage: A[0] is f - True sage: A[1] 1-form omega on the 2-dimensional differentiable manifold M - sage: A[1] is omega - True sage: A[2] 2-form eta on the 2-dimensional differentiable manifold M - sage: A[2] is eta - True Alternatively, the components can be determined from scratch:: @@ -243,8 +237,7 @@ def __init__(self, parent, name=None, latex_name=None): self._max_deg = vmodule._ambient_domain.dim() self._is_zero = False # a priori, may be changed below or via # method __bool__() - # Set components: - self._comp = [self._domain.diff_form(j) for j in self.irange()] + self._comp = None # initialized on demand; see _init_comp # Set names: self._name = name if latex_name is None: @@ -252,6 +245,25 @@ def __init__(self, parent, name=None, latex_name=None): else: self._latex_name = latex_name + def _init_comp(self): + r""" + Initialize homogeneous components of ``self``. + + TESTS:: + + sage: M = Manifold(2, 'M') + sage: A = M.mixed_form(name='A') + sage: A._comp is None + True + sage: A._init_comp() + sage: A._comp + [Scalar field on the 2-dimensional differentiable manifold M, + 1-form on the 2-dimensional differentiable manifold M, + 2-form on the 2-dimensional differentiable manifold M] + + """ + self._comp = [self._domain.diff_form(j) for j in self.irange()] + def _repr_(self): r""" String representation of ``self``. @@ -534,7 +546,7 @@ def __bool__(self): """ if self._is_zero: return False - if any(bool(form) for form in self._comp): + if any(bool(form) for form in self): self._is_zero = False return True self._is_zero = True @@ -665,7 +677,7 @@ def _add_(self, other): return self # Generic case: resu = self._new_instance() - resu[:] = [self[j] + other[j] for j in self.irange()] + resu._comp = [self[j] + other[j] for j in self.irange()] # Compose name: if self._name is not None and other._name is not None: resu._name = self._name + '+' + other._name @@ -739,7 +751,7 @@ def _sub_(self, other): return self # Generic case: resu = self._new_instance() - resu[:] = [self[j] - other[j] for j in self.irange()] + resu._comp = [self[j] - other[j] for j in self.irange()] # Compose name: from sage.tensor.modules.format_utilities import is_atomic @@ -860,8 +872,8 @@ def wedge(self, other): return self # Generic case: resu = self._new_instance() - for j in self.irange(): - resu[j] = sum(self[k].wedge(other[j - k]) for k in range(j + 1)) + resu._comp = [sum(self[k].wedge(other[j-k]) for k in range(j+1)) + for j in self.irange()] # Compose name: from sage.tensor.modules.format_utilities import (format_mul_txt, format_mul_latex) @@ -913,7 +925,7 @@ def _lmul_(self, other): if other == 1: return self resu = self._new_instance() - resu[:] = [other * form for form in self._comp] + resu._comp = [other * form for form in self] # Compose name: from sage.misc.latex import latex from sage.tensor.modules.format_utilities import (format_mul_txt, @@ -1046,20 +1058,9 @@ def copy(self, name=None, latex_name=None): sage: A is B False - Notice, that changes in the differential forms usually cause changes in - the original instance. But for the copy of a mixed form, the components - are copied as well:: - - sage: omega[e_xy,0] = y; omega.display() - omega = y dx - sage: A.display_expansion(e_xy) - A = [x] + [y dx] + [0] - sage: B.display_expansion(e_xy) - [x] + [x dx] + [0] - """ resu = self._new_instance() - resu[:] = [form.copy() for form in self] + resu._comp = [form.copy() for form in self] resu.set_name(name=name, latex_name=latex_name) resu._is_zero = self._is_zero # a priori @@ -1098,7 +1099,7 @@ def __setitem__(self, index, values): stop = index + 1 step = 1 elif isinstance(index, slice): - start, stop, step = index.indices(len(self._comp)) + start, stop, step = index.indices(self._max_deg + 1) else: raise TypeError("index must be int, Integer or slice") if isinstance(values, list): @@ -1108,8 +1109,11 @@ def __setitem__(self, index, values): if len(form_list) != len(range(start, stop, step)): raise IndexError("either input or index out of range") for deg, j in zip(range(start, stop, step), range(len(form_list))): - self._comp[deg] = self._domain.diff_form_module(deg, - self._dest_map)(form_list[j]) + dmodule = self._domain.diff_form_module(deg, self._dest_map) + form = dmodule(form_list[j]) + self[deg].copy_from(form) + self[deg].set_name(name=form._name, + latex_name=form._latex_name) self._is_zero = False # a priori def __getitem__(self, deg): @@ -1141,6 +1145,8 @@ def __getitem__(self, deg): 2-form b on the 2-dimensional differentiable manifold M] """ + if self._comp is None: + self._init_comp() return self._comp[deg] def set_restriction(self, rst): @@ -1199,7 +1205,7 @@ def set_restriction(self, rst): if not isinstance(rst, MixedForm): raise TypeError("the argument must be a mixed form") if not rst._domain.is_subset(self._domain): - raise ValueError("the specified domain is not a subset of " + + raise ValueError("the specified domain is not a subset of " "the domain of definition of the mixed form") for j in self.irange(): self[j].set_restriction(rst[j]) @@ -1366,3 +1372,10 @@ def irange(self, start=None): """ return self.parent().irange(start=start) + + def set_comp(self, i): + r""" + Return the `i`-th homogeneous component for assignment. + + """ + return self[i] diff --git a/src/sage/manifolds/differentiable/mixed_form_algebra.py b/src/sage/manifolds/differentiable/mixed_form_algebra.py index 5edb7b22955..a5727d3812d 100644 --- a/src/sage/manifolds/differentiable/mixed_form_algebra.py +++ b/src/sage/manifolds/differentiable/mixed_form_algebra.py @@ -249,8 +249,8 @@ def _an_element_(self): """ res = self.element_class(self) dom = self._domain - res[:] = [dom.diff_form_module(j, self._dest_map)._an_element_() - for j in self.irange()] + res._comp = [dom.diff_form_module(j, self._dest_map)._an_element_() + for j in self.irange()] return res def _coerce_map_from_(self, S): @@ -297,8 +297,7 @@ def _coerce_map_from_(self, S): return True # Let us check for each degree consecutively: dom = self._domain - return any(dom.diff_form_module(deg, - self._dest_map).has_coerce_map_from(S) + return any(dom.diff_form_module(deg, self._dest_map).has_coerce_map_from(S) for deg in self.irange()) @cached_method @@ -316,8 +315,8 @@ def zero(self): """ res = self.element_class(self, name='zero', latex_name='0') - res._comp[:] = [self._domain.diff_form_module(j, - dest_map=self._dest_map).zero() for j in self.irange()] + res._comp = [self._domain.diff_form_module(j, dest_map=self._dest_map).zero() + for j in self.irange()] res._is_zero = True # This element is certainly zero return res @@ -336,10 +335,9 @@ def one(self): """ res = self.element_class(self, name='one', latex_name='1') - res._comp[0] = self._domain.one_scalar_field() - res._comp[1:] = [self._domain.diff_form_module(j, - dest_map=self._dest_map).zero() - for j in self.irange(1)] + res._comp = [self._domain.one_scalar_field(), + *(self._domain.diff_form_module(j, dest_map=self._dest_map).zero() + for j in self.irange(1))] return res def vector_field_module(self): From 69f7bb51b0bd5d0cfe3db416e4e15b3bedc9997a Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 13 Apr 2021 01:31:25 +0200 Subject: [PATCH 009/336] Trac #30272: new standard name for homogeneous components (without tests) --- .../manifolds/differentiable/mixed_form.py | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index 0fb8130d769..ba3272b47df 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -29,6 +29,7 @@ from sage.rings.integer import Integer class MixedForm(AlgebraElement): + # TODO: refactor documentation according in favor of `set_comp` r""" An instance of this class is a mixed form along some differentiable map `\varphi: M \to N` between two differentiable manifolds `M` and `N`. More @@ -257,12 +258,21 @@ def _init_comp(self): True sage: A._init_comp() sage: A._comp - [Scalar field on the 2-dimensional differentiable manifold M, - 1-form on the 2-dimensional differentiable manifold M, - 2-form on the 2-dimensional differentiable manifold M] + [Scalar field A_0 on the 2-dimensional differentiable manifold M, + 1-form A_1 on the 2-dimensional differentiable manifold M, + 2-form A_2 on the 2-dimensional differentiable manifold M] """ - self._comp = [self._domain.diff_form(j) for j in self.irange()] + self._comp = [] + for i in self.irange(): + comp_name, comp_latex_name = None, None + if self._name is not None: + comp_name = f"{self._name}_{i}" + if self._latex_name is not None: + comp_latex_name = '{' + self._latex_name + '}_{' + str(i) + '}' + diff_form = self._domain.diff_form + self._comp.append(diff_form(i, name=comp_name, + latex_name=comp_latex_name)) def _repr_(self): r""" @@ -477,7 +487,7 @@ def display(self): disp = display - def set_name(self, name=None, latex_name=None): + def set_name(self, name=None, latex_name=None, set_all=True): r""" Redefine the string and LaTeX representation of the object. @@ -486,6 +496,9 @@ def set_name(self, name=None, latex_name=None): - ``name`` -- (default: ``None``) name given to the mixed form - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the mixed form; if none is provided, the LaTeX symbol is set to ``name`` + - ``set_all`` -- (default: ``True``) if ``True`` all homogeneous + components will be renamed accordingly; if ``False`` only the mixed + form will be renamed EXAMPLES:: @@ -501,6 +514,8 @@ def set_name(self, name=None, latex_name=None): sage: latex(F) \eta + # TODO: add more examples with `set_all` + """ if name is not None: self._name = name @@ -508,6 +523,33 @@ def set_name(self, name=None, latex_name=None): self._latex_name = self._name if latex_name is not None: self._latex_name = latex_name + if set_all: + for i in self.irange(): + comp_name, comp_latex_name = None, None + if self._name is not None: + comp_name = f"{self._name}_{i}" + if self._latex_name is not None: + comp_latex_name = '{' + self._latex_name + '}_{' + str(i) + '}' + self.set_name_comp(i, name=comp_name, + latex_name=comp_latex_name) + + def set_name_comp(self, i, name=None, latex_name=None): + r""" + Redefine the string and LaTeX representation of the `i`-th + homogeneous component of ``self``. + + INPUT: + + - ``name`` -- (default: ``None``) name given to the mixed form + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + mixed form; if none is provided, the LaTeX symbol is set to ``name`` + + EXAMPLES:: + + # TODO: add examples + + """ + self[i].set_name(name=name, latex_name=latex_name) def __bool__(self): r""" @@ -552,8 +594,6 @@ def __bool__(self): self._is_zero = True return False - __nonzero__ = __bool__ # For Python2 compatibility - def _richcmp_(self, other, op): r""" Comparison method for sage objects. @@ -1093,11 +1133,9 @@ def __setitem__(self, index, values): """ if self is self.parent().one() or self is self.parent().zero(): raise ValueError("the components of the element " - "{} cannot be changed".format(self._name)) + f"{self._name} cannot be changed") if isinstance(index, (int, Integer)): - start = index - stop = index + 1 - step = 1 + start, stop, step = index, index + 1, 1 elif isinstance(index, slice): start, stop, step = index.indices(self._max_deg + 1) else: @@ -1377,5 +1415,7 @@ def set_comp(self, i): r""" Return the `i`-th homogeneous component for assignment. + # TODO: add examples + """ return self[i] From 27aa360aebdcdd632fa62d6d3161d475f3954221 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 13 Apr 2021 12:23:36 +0200 Subject: [PATCH 010/336] Trac #30272: doctests added --- .../manifolds/differentiable/mixed_form.py | 109 +++++++++++++----- 1 file changed, 81 insertions(+), 28 deletions(-) diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index ba3272b47df..5b1bcf3d7fe 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -29,7 +29,6 @@ from sage.rings.integer import Integer class MixedForm(AlgebraElement): - # TODO: refactor documentation according in favor of `set_comp` r""" An instance of this class is a mixed form along some differentiable map `\varphi: M \to N` between two differentiable manifolds `M` and `N`. More @@ -79,8 +78,20 @@ class MixedForm(AlgebraElement): Graded algebra Omega^*(M) of mixed differential forms on the 2-dimensional differentiable manifold M - One convenient way to define the homogeneous components of a mixed form is - to define some differential forms first:: + The most straightforward way to define the `i`-th homogeneous components + of a mixed form is via :meth:`set_comp` or ``A[i]``:: + + sage: A = M.mixed_form(name='A') + sage: A[0].set_expr(x) # scalar field + sage: A.set_comp(1)[0] = y*x + sage: A.set_comp(2)[0,1] = y^2*x + sage: A.display() # display names + A = A_0 + A_1 + A_2 + sage: A.display_expansion() # display expansion in basis + A = [x] + [x*y dx] + [x*y^2 dx/\dy] + + Another way to define the homogeneous components is using predefined + differential forms:: sage: f = M.scalar_field(x, name='f'); f Scalar field f on the 2-dimensional differentiable manifold M @@ -93,26 +104,34 @@ class MixedForm(AlgebraElement): sage: eta[e_xy,0,1] = y^2*x; eta.display() eta = x*y^2 dx/\dy - The components of the mixed form ``F`` can be set very easily:: + The components of a mixed form ``B`` can then be set as follows:: - sage: A[:] = [f, omega, eta]; A.display() # display names - A = f + omega + eta - sage: A.display_expansion() # display in coordinates - A = [x] + [x*y dx] + [x*y^2 dx/\dy] - sage: A[0] + sage: B = M.mixed_form(name='B') + sage: B[:] = [f, omega, eta] + sage: B.display() + B = f + omega + eta + sage: B.display_expansion() + B = [x] + [x*y dx] + [x*y^2 dx/\dy] + sage: B[0] Scalar field f on the 2-dimensional differentiable manifold M - sage: A[1] + sage: B[1] 1-form omega on the 2-dimensional differentiable manifold M - sage: A[2] + sage: B[2] 2-form eta on the 2-dimensional differentiable manifold M + As we can see, the names are applied. However, the differential + forms are different instances:: + + sage: f is B[0] + False + Alternatively, the components can be determined from scratch:: sage: B = M.mixed_form([f, omega, eta], name='B') - sage: A == B - True + sage: B.display() + B = f + omega + eta - Mixed forms are elements of an algebra, so they can be added, and multiplied + Mixed forms are elements of an algebra so they can be added, and multiplied via the wedge product:: sage: C = x*A; C @@ -144,7 +163,7 @@ class MixedForm(AlgebraElement): mixed form:: sage: dA = A.exterior_derivative(); dA.display() - dA = zero + df + domega + dA = zero + dA_0 + dA_1 sage: dA.display_expansion() dA = [0] + [dx] + [-x dx/\dy] @@ -162,18 +181,15 @@ class MixedForm(AlgebraElement): sage: W = U.intersection(V) sage: e_xy = c_xy.frame(); e_uv = c_uv.frame() # define frames sage: A = M.mixed_form(name='A') - sage: A[0].set_name('f') sage: A[0].set_expr(x, c_xy) sage: A[0].display() - f: M --> R + A_0: M --> R on U: (x, y) |--> x on W: (u, v) |--> 1/2*u + 1/2*v - sage: A[1].set_name('omega') sage: A[1][0] = y*x; A[1].display(e_xy) - omega = x*y dx - sage: A[2].set_name('eta') + A_1 = x*y dx sage: A[2][e_uv,0,1] = u*v^2; A[2].display(e_uv) - eta = u*v^2 du/\dv + A_2 = u*v^2 du/\dv sage: A.add_comp_by_continuation(e_uv, W, c_uv) sage: A.display_expansion(e_uv) A = [1/2*u + 1/2*v] + [(1/8*u^2 - 1/8*v^2) du + (1/8*u^2 - 1/8*v^2) dv] @@ -500,7 +516,9 @@ def set_name(self, name=None, latex_name=None, set_all=True): components will be renamed accordingly; if ``False`` only the mixed form will be renamed - EXAMPLES:: + EXAMPLES: + + Rename a mixed form:: sage: M = Manifold(4, 'M') sage: F = M.mixed_form(name='dummy', latex_name=r'\ugly'); F @@ -508,13 +526,29 @@ def set_name(self, name=None, latex_name=None, set_all=True): manifold M sage: latex(F) \ugly - sage: F.set_name(name='fancy', latex_name=r'\eta'); F - Mixed differential form fancy on the 4-dimensional differentiable + sage: F.set_name(name='F', latex_name=r'\mathcal{F}'); F + Mixed differential form F on the 4-dimensional differentiable manifold M sage: latex(F) - \eta + \mathcal{F} + + If not stated otherwise, all homogeneous components are renamed + accordingly: - # TODO: add more examples with `set_all` + sage: F.display() + F = F_0 + F_1 + F_2 + F_3 + F_4 + + Setting the argument ``set_all`` to ``False`` prevents the renaming + in the homogeneous components:: + + sage: F.set_name(name='eta', latex_name=r'\eta', set_all=False) + sage: F.display() + eta = F_0 + F_1 + F_2 + F_3 + F_4 + + .. SEEALSO:: + + To rename a homogeneous component individually, use + :meth:`set_name_comp`. """ if name is not None: @@ -546,7 +580,15 @@ def set_name_comp(self, i, name=None, latex_name=None): EXAMPLES:: - # TODO: add examples + sage: M = Manifold(3, 'M') + sage: F = M.mixed_form(name='F', latex_name=r'\mathcal{F}'); F + Mixed differential form F on the 3-dimensional differentiable + manifold M + sage: F.display() + F = F_0 + F_1 + F_2 + F_3 + sage: F.set_name_comp(0, name='g') + sage: F.display() + F = g + F_1 + F_2 + F_3 """ self[i].set_name(name=name, latex_name=latex_name) @@ -1415,7 +1457,18 @@ def set_comp(self, i): r""" Return the `i`-th homogeneous component for assignment. - # TODO: add examples + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: A = M.mixed_form(name='A') + sage: A.set_comp(0).set_expr(x^2) # scalar field + sage: A.set_comp(1)[:] = [-y, x] + sage: A.set_comp(2)[0,1] = x-y + sage: A.display() + A = A_0 + A_1 + A_2 + sage: A.display_expansion() + A = [x^2] + [-y dx + x dy] + [(x - y) dx/\dy] """ return self[i] From 55b4364c2008d1ac2dc511ea59188d4b45fd3305 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Tue, 9 Feb 2021 09:07:14 -0800 Subject: [PATCH 011/336] trac 31368: fix preparsing of "time" --- src/sage/repl/preparse.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/repl/preparse.py b/src/sage/repl/preparse.py index 77a9aa88bda..9dc5308db80 100644 --- a/src/sage/repl/preparse.py +++ b/src/sage/repl/preparse.py @@ -1745,7 +1745,7 @@ def preparse(line, reset=True, do_time=False, ignore_prompts=False, 'a * BackslashOperator() * b \\' sage: preparse("time R. = ZZ[]", do_time=True) - '__time__=misc.cputime(); __wall__=misc.walltime(); R = ZZ[\'x\']; print("Time: CPU %.2f s, Wall: %.2f s"%(misc.cputime(__time__), misc.walltime(__wall__))); (x,) = R._first_ngens(1)' + '__time__=sage.misc.misc.cputime(); __wall__=sage.misc.misc.walltime(); R = ZZ[\'x\']; print("Time: CPU %.2f s, Wall: %.2f s"%(sage.misc.misc.cputime(__time__), sage.misc.misc.walltime(__wall__))); (x,) = R._first_ngens(1)' TESTS: @@ -1838,8 +1838,8 @@ def preparse(line, reset=True, do_time=False, ignore_prompts=False, if do_time: # Time keyword L = re.sub(r';time;(\s*)(\S[^;]*)', - r';\1__time__=misc.cputime(); __wall__=misc.walltime(); \2; print(' + - '"Time: CPU %%.2f s, Wall: %%.2f s"%%(misc.cputime(__time__), misc.walltime(__wall__)))', + r';\1__time__=sage.misc.misc.cputime(); __wall__=sage.misc.misc.walltime(); \2; print(' + + '"Time: CPU %%.2f s, Wall: %%.2f s"%%(sage.misc.misc.cputime(__time__), sage.misc.misc.walltime(__wall__)))', L) # Remove extra ;'s From ca407e4f74f3fa30c9f64e796ff6496400082adc Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Tue, 13 Apr 2021 10:03:34 -0700 Subject: [PATCH 012/336] trac 31368: change imports from 'sage.misc.misc.cputime' to 'cputime', etc. --- src/sage/repl/preparse.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/repl/preparse.py b/src/sage/repl/preparse.py index 9dc5308db80..e5ac7f7ab73 100644 --- a/src/sage/repl/preparse.py +++ b/src/sage/repl/preparse.py @@ -1745,7 +1745,7 @@ def preparse(line, reset=True, do_time=False, ignore_prompts=False, 'a * BackslashOperator() * b \\' sage: preparse("time R. = ZZ[]", do_time=True) - '__time__=sage.misc.misc.cputime(); __wall__=sage.misc.misc.walltime(); R = ZZ[\'x\']; print("Time: CPU %.2f s, Wall: %.2f s"%(sage.misc.misc.cputime(__time__), sage.misc.misc.walltime(__wall__))); (x,) = R._first_ngens(1)' + '__time__=cputime(); __wall__=walltime(); R = ZZ[\'x\']; print("Time: CPU %.2f s, Wall: %.2f s"%(cputime(__time__), walltime(__wall__))); (x,) = R._first_ngens(1)' TESTS: @@ -1838,8 +1838,8 @@ def preparse(line, reset=True, do_time=False, ignore_prompts=False, if do_time: # Time keyword L = re.sub(r';time;(\s*)(\S[^;]*)', - r';\1__time__=sage.misc.misc.cputime(); __wall__=sage.misc.misc.walltime(); \2; print(' + - '"Time: CPU %%.2f s, Wall: %%.2f s"%%(sage.misc.misc.cputime(__time__), sage.misc.misc.walltime(__wall__)))', + r';\1__time__=cputime(); __wall__=walltime(); \2; print(' + + '"Time: CPU %%.2f s, Wall: %%.2f s"%%(cputime(__time__), walltime(__wall__)))', L) # Remove extra ;'s From 26d4a1b961c630e909f6e04e502c1029042089d0 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 20 Apr 2021 20:32:27 +0200 Subject: [PATCH 013/336] Trac #31706: immutability mixed forms --- .../manifolds/differentiable/mixed_form.py | 41 +++++++++++++++---- .../differentiable/mixed_form_algebra.py | 2 + 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index 5b1bcf3d7fe..d92648f57fb 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -25,10 +25,11 @@ #****************************************************************************** from sage.misc.cachefunc import cached_method -from sage.structure.element import AlgebraElement +from sage.structure.mutability import Mutability +from sage.structure.element import AlgebraElement, ModuleElementWithMutability from sage.rings.integer import Integer -class MixedForm(AlgebraElement): +class MixedForm(AlgebraElement, ModuleElementWithMutability): r""" An instance of this class is a mixed form along some differentiable map `\varphi: M \to N` between two differentiable manifolds `M` and `N`. More @@ -205,12 +206,12 @@ class MixedForm(AlgebraElement): sage: z[0] = 1 Traceback (most recent call last): ... - ValueError: the components of the element zero cannot be changed + ValueError: the components of an immutable element cannot be changed sage: one = M.mixed_form_algebra().one() sage: one[0] = 0 Traceback (most recent call last): ... - ValueError: the components of the element one cannot be changed + ValueError: the components of an immutable element cannot be changed """ def __init__(self, parent, name=None, latex_name=None): @@ -551,6 +552,9 @@ def set_name(self, name=None, latex_name=None, set_all=True): :meth:`set_name_comp`. """ + if self.is_immutable(): + raise ValueError("the name of an immutable element " + "cannot be changed") if name is not None: self._name = name if latex_name is None: @@ -1173,9 +1177,9 @@ def __setitem__(self, index, values): A = [x] + [y dx] + [x*y dx/\dy] """ - if self is self.parent().one() or self is self.parent().zero(): - raise ValueError("the components of the element " - f"{self._name} cannot be changed") + if self.is_immutable(): + raise ValueError("the components of an immutable element " + "cannot be changed") if isinstance(index, (int, Integer)): start, stop, step = index, index + 1, 1 elif isinstance(index, slice): @@ -1472,3 +1476,26 @@ def set_comp(self, i): """ return self[i] + + def set_immutable(self): + r""" + Set ``self`` and homogeneous components of ``self`` immutable. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: f = M.scalar_field(x^2, name='f') + sage: A = M.mixed_form([f, 0, 0], name='A') + sage: A.set_immutable() + sage: A.is_immutable() + True + sage: A[0].is_immutable() + True + sage: f.is_immutable() + False + + """ + for form in self: + form.set_immutable() + super().set_immutable() diff --git a/src/sage/manifolds/differentiable/mixed_form_algebra.py b/src/sage/manifolds/differentiable/mixed_form_algebra.py index a5727d3812d..30c74cee389 100644 --- a/src/sage/manifolds/differentiable/mixed_form_algebra.py +++ b/src/sage/manifolds/differentiable/mixed_form_algebra.py @@ -318,6 +318,7 @@ def zero(self): res._comp = [self._domain.diff_form_module(j, dest_map=self._dest_map).zero() for j in self.irange()] res._is_zero = True # This element is certainly zero + res.set_immutable() return res @cached_method @@ -338,6 +339,7 @@ def one(self): res._comp = [self._domain.one_scalar_field(), *(self._domain.diff_form_module(j, dest_map=self._dest_map).zero() for j in self.irange(1))] + res.set_immutable() return res def vector_field_module(self): From d91bb152b249332c4009d9734b966c7931957bab Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 20 Apr 2021 20:33:16 +0200 Subject: [PATCH 014/336] Trac #31706: remove unnecessary import --- src/sage/manifolds/differentiable/mixed_form.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index d92648f57fb..750383d15cb 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -25,7 +25,6 @@ #****************************************************************************** from sage.misc.cachefunc import cached_method -from sage.structure.mutability import Mutability from sage.structure.element import AlgebraElement, ModuleElementWithMutability from sage.rings.integer import Integer From f76287b8ec12aa28e413a9629b36db20b1e92f2f Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Wed, 28 Apr 2021 10:00:39 -0400 Subject: [PATCH 015/336] initial implementation of polyhedral_complex.py --- src/sage/homology/all.py | 2 + src/sage/homology/polyhedral_complex.py | 1435 +++++++++++++++++++++++ 2 files changed, 1437 insertions(+) create mode 100644 src/sage/homology/polyhedral_complex.py diff --git a/src/sage/homology/all.py b/src/sage/homology/all.py index 7708a0cc57c..28a5ceb558f 100644 --- a/src/sage/homology/all.py +++ b/src/sage/homology/all.py @@ -11,6 +11,8 @@ from .cubical_complex import CubicalComplex, cubical_complexes +from .polyhedral_complex import PolyhedralComplex + from sage.misc.lazy_import import lazy_import lazy_import('sage.homology.koszul_complex', 'KoszulComplex') lazy_import('sage.homology', 'simplicial_complexes_catalog', 'simplicial_complexes') diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py new file mode 100644 index 00000000000..0011f690cd0 --- /dev/null +++ b/src/sage/homology/polyhedral_complex.py @@ -0,0 +1,1435 @@ +# -*- coding: utf-8 -*- +r""" +Finite polyhedral complexes + +This module implements the basic structure of finite polyhedral complexes. + +Description to be completed. + +AUTHORS: + +- Yuan Zhou (2021-04): initial implementation +""" + +# **************************************************************************** +# Copyright (C) 2021 Yuan Zhou +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from copy import copy +from sage.homology.cell_complex import GenericCellComplex +from sage.geometry.polyhedron.constructor import Polyhedron +from sage.geometry.polyhedron.base import is_Polyhedron +from sage.modules.free_module_element import vector +from sage.structure.sage_object import SageObject +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ +from sage.graphs.graph import Graph + +class PolyhedralComplex(GenericCellComplex): + r""" + Define a PolyhedralComplex. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1/7, 2/7)]), + ....: Polyhedron(vertices=[(1/7, 2/7), (0, 0), (0, 1/4)])]) + sage: [p.Vrepresentation() for p in pc.cells_list()] + [(A vertex at (0, 0), A vertex at (0, 1/4), A vertex at (1/7, 2/7)), + (A vertex at (0, 0), A vertex at (1/3, 1/3), A vertex at (1/7, 2/7)), + (A vertex at (0, 0), A vertex at (0, 1/4)), + (A vertex at (0, 0), A vertex at (1/7, 2/7)), + (A vertex at (0, 0), A vertex at (1/3, 1/3)), + (A vertex at (0, 1/4), A vertex at (1/7, 2/7)), + (A vertex at (1/3, 1/3), A vertex at (1/7, 2/7)), + (A vertex at (0, 0),), + (A vertex at (0, 1/4),), + (A vertex at (1/7, 2/7),), + (A vertex at (1/3, 1/3),)] + sage: pc.plot() # not tested + sage: pc.is_pure() + True + sage: pc.is_full_dimensional() + True + sage: pc.is_compact() + True + sage: pc.is_convex() + True + sage: pc.union_as_polyhedron().Hrepresentation() + (An inequality (1, -4) x + 1 >= 0, + An inequality (-1, 1) x + 0 >= 0, + An inequality (1, 0) x + 0 >= 0) + sage: pc.face_poset() + Finite poset containing 11 elements + sage: pc.is_connected() + True + sage: pc.connected_component() == pc + True + """ + def __init__(self, maximal_cells=None, maximality_check=True, + face_to_face_check=False): + r""" + INPUT: + + - ``maximal_cells`` -- a list, a tuple, or a dictionary (indexed by + dimension) of cells of the Complex. Each cell is of class + :class:`Polyhedron` of the same ambient dimension. To set up a + :class:PolyhedralComplex, it is sufficient to provide the maximal + faces. Use keyword argument partial=``True`` to set up a partial + polyhedral complex, which is a subset of the faces (viewed as + relatively open) of a polyhedral complex that is not necessarily + closed under taking intersection. + + - ``maximality_check`` -- boolean; default ``True``; + if it is ``True``, then the constructor checks that each given + maximal cell is indeed maximal, ane ignores those that are not. + + + - ``face_to_face_check`` -- boolean; default ``False``; + if it is ``True``, then the constructor checks whether the cells + are face-to-face, and it raises a ValueError if they are not. + + TESTS: + + Check that non-maximal cells are ignored if ``maximality_check=True``:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0)]) ]) + sage: pc.maximal_cells() + {2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}} + + Check that non face-to-face can be detected:: + + sage: PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(2, 2), (0, 0)]) ], + ....: face_to_face_check=True) + Traceback (most recent call last): + ... + ValueError: The given cells are not face-to-face + + Check that all the cells must have the same ambient dimension:: + + sage: PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[[2], [0]]) ]) + Traceback (most recent call last): + ... + ValueError: The given cells are not polyhedrain the same ambient space. + """ + def cells_list_to_cells_dict(cells_list): + cells_dict = {} + for cell in cells_list: + d = cell.dimension() + if d in cells_dict: + cells_dict[d].add(cell) + else: + cells_dict[d] = set([cell]) + return cells_dict + + if maximal_cells is None: + cells_dict = {} + elif isinstance(maximal_cells, (list, tuple)): + cells_dict = cells_list_to_cells_dict(maximal_cells) + elif isinstance(maximal_cells, dict): + cells_dict = {k: set(l) for (k, l) in maximal_cells.items()} + else: + raise ValueError + if not cells_dict: + self._dim = -1 + self._ambient_dim = -1 + else: + self._dim = max(cells_dict.keys()) + self._ambient_dim = next(iter(cells_dict[self._dim])).ambient_dim() + self._maximal_cells = cells_dict + if not all( (is_Polyhedron(cell) and + cell.ambient_dim() == self._ambient_dim) + for cell in self.maximal_cell_iterator()): + raise ValueError("The given cells are not polyhedra" + + "in the same ambient space.") + # initialize the attributes + self._is_convex = None + self._polyhedron = None + self._maximal_cells_list = None # needed for hash + self._cells = None + self._face_poset = None + + if maximality_check: + cells = self.cells() # compute self._cells and self._face_poset + self._maximal_cells = cells_list_to_cells_dict( + self._face_poset.maximal_elements()) + if face_to_face_check: + poset = self.face_poset() + maximal_cells = poset.maximal_elements() # a list + for i in range(len(maximal_cells)): + p = maximal_cells[i] + for j in range(i, len(maximal_cells)): + q = maximal_cells[j] + r = p.intersection(q) + if not (r.is_empty() or (r in poset) and + poset.is_gequal(p,r) and poset.is_gequal(q,r)): + raise ValueError("The given cells are not face-to-face") + # For now, a polyhedral complex is immutable + #TODO: is_mutable and is_immutable paramters and set_immutable method. + self._is_immutable = True + + def cells(self, subcomplex=None): + """ + The cells of this polyhedral complex, in the form of a dictionary: + the keys are integers, representing dimension, and the value + associated to an integer `d` is the set of `d`-cells. If the + optional argument ``subcomplex`` is present, then return only + the cells which are *not* in the subcomplex. + + :param subcomplex: a subcomplex of this polyhedral complex. Return + the cells which are not in this subcomplex. + :type subcomplex: optional, default None + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: list(pc.cells().keys()) + [2, 1, 0] + """ + if subcomplex is not None: + raise NotImplementedError + if self._cells is not None: + return self._cells + maximal_cells = self.maximal_cells() + cells = {} + covers = {} + for k in range(self._dim, -1, -1): + if k in maximal_cells: + if not k in cells: + cells[k] = set([]) + cells[k].update(maximal_cells[k]) + if k in cells: + for cell in cells[k]: + if not cell in covers: + covers[cell] = [] + for facet in cell.facets(): + p = facet.as_polyhedron() + if not p in covers: + covers[p] = [] + covers[p].append(cell) + if not (k-1) in cells: + cells[k-1] = set([]) + cells[k-1].add(p) + from sage.combinat.posets.posets import Poset + self._face_poset = Poset(covers) + self._cells = cells + return self._cells + + def cell_iterator(self, increasing=True): + """ + An iterator for the cells in this polyhedral complex. + + INPUT: + + - ``increasing`` -- (optional, default ``True``) if ``True``, return + cells in increasing order of dimension, thus starting with the + zero-dimensional cells. Otherwise it returns cells in decreasing + order of dimension. + + .. NOTE:: + + Among the cells of a fixed dimension, there is no sorting. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: len(list(pc.cell_iterator())) + 11 + """ + cells = self.cells() + dim_index = range(0, self.dimension() + 1) + if not increasing: + dim_index = reversed(dim_index) + for d in dim_index: + if d in cells: + for c in cells[d]: + yield c + + def _n_cells_sorted(self, n, subcomplex=None): + """ + Sorted list of cells of dimension ``n`` of this polyhedral complex. + If the optional argument ``subcomplex`` is present, then + return the ``n``-dimensional cells which are *not* in the + subcomplex. + + :param n: the dimension + :type n: non-negative integer + :param subcomplex: a subcomplex of this cell complex. Return + the cells which are not in this subcomplex. + :type subcomplex: optional, default ``None`` + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: [p.Vrepresentation() for p in pc._n_cells_sorted(1)] + [(A vertex at (0, 0), A vertex at (0, 2)), + (A vertex at (0, 0), A vertex at (1, 1)), + (A vertex at (0, 0), A vertex at (1, 2)), + (A vertex at (0, 2), A vertex at (1, 2)), + (A vertex at (1, 1), A vertex at (1, 2))] + sage: pc._n_cells_sorted(3) + [] + """ + n_cells = self.n_cells(n, subcomplex) + return sorted(n_cells, + key=lambda p: (p.vertices(), p.rays(), p.lines())) + + def cells_list(self, subcomplex=None): + """ + The sorted list of the cells of this polyhedral complex + in non-increasing dimensions. + If the optional argument ``subcomplex`` is present, then return only + the cells which are *not* in the subcomplex. + + :param subcomplex: a subcomplex of this polyhedral complex. Return + the cells which are not in this subcomplex. + :type subcomplex: optional, default None + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: len(pc.cells_list()) + 11 + sage: pc.cells_list()[0].Vrepresentation() + (A vertex at (0, 0), A vertex at (0, 2), A vertex at (1, 2)) + """ + cells = [] + for n in range(self._dim, -1, -1): + cells += self._n_cells_sorted(n, subcomplex) + return cells + + def maximal_cells(self): + """ + The maximal cells of this polyhedral complex, in the form of a + dictionary: the keys are integers, representing dimension, and the + value associated to an integer `d` is the set of `d`-maximal cells. + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2, p3]) + sage: len(pc.maximal_cells()[2]) + 2 + sage: 1 in pc.maximal_cells() + False + + Wrong answer due to ``maximality_check=False``:: + + sage: pc_invalid = PolyhedralComplex([p1, p2, p3], + ....: maximality_check=False) + sage: len(pc_invalid.maximal_cells()[1]) + 1 + """ + return self._maximal_cells + + def maximal_cell_iterator(self, increasing=False): + """ + An iterator for the maximal cells in this polyhedral complex. + + INPUT: + + - ``increasing`` -- (optional, default ``False``) if ``True``, return + maximal cells in increasing order of dimension. + Otherwise it returns cells in decreasing order of dimension. + + .. NOTE:: + + Among the cells of a fixed dimension, there is no sorting. + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2, p3]) + sage: len(list(pc.maximal_cell_iterator())) + 2 + + Wrong answer due to ``maximality_check=False``:: + + sage: pc_invalid = PolyhedralComplex([p1, p2, p3], + ....: maximality_check=False) + sage: len(list(pc_invalid.maximal_cell_iterator())) + 3 + """ + maximal_cells = self.maximal_cells() + dim_index = range(-1, self.dimension() + 1) + if not increasing: + dim_index = reversed(dim_index) + for d in dim_index: + if d in maximal_cells: + for c in maximal_cells[d]: + yield c + + def n_maximal_cells(self, n): + """ + List of maximal cells of dimension ``n`` of this polyhedral complex. + + :param n: the dimension + :type n: non-negative integer + + .. NOTE:: + + The resulting list need not be sorted. If you want a sorted + list of `n`-cells, use :meth:`_n_maximal_cells_sorted`. + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2, p3]) + sage: len(pc.n_maximal_cells(2)) + 2 + sage: len(pc.n_maximal_cells(1)) + 0 + + Wrong answer due to ``maximality_check=False``:: + + sage: pc_invalid = PolyhedralComplex([p1, p2, p3], + ....: maximality_check=False) + sage: len(pc_invalid.n_maximal_cells(1)) + 1 + """ + if n in self.maximal_cells(): + return list(self.maximal_cells()[n]) + else: + return [] + + def _n_maximal_cells_sorted(self, n): + """ + Sorted list of maximal cells of dimension ``n`` of this polyhedral + complex. + + :param n: the dimension + :type n: non-negative integer + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: pc._n_maximal_cells_sorted(2)[0].vertices_list() + [[0, 0], [0, 2], [1, 2]] + """ + n_maximal_cells = self.n_maximal_cells(n) + return sorted(n_maximal_cells, + key=lambda p: (p.vertices(), p.rays(), p.lines())) + + def maximal_cells_list(self): + """ + The sorted list of the maximal cells of this polyhedral complex + in non-increasing dimensions. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: [p.vertices_list() for p in pc.maximal_cells_list()] + [[[0, 0], [0, 2], [1, 2]], [[0, 0], [1, 1], [1, 2]]] + """ + if self._maximal_cells_list is None: + maximal_cells = [] + for n in range(self._dim, -1, -1): + maximal_cells += self._n_maximal_cells_sorted(n) + self._maximal_cells_list = maximal_cells + return self._maximal_cells_list + + def has_maximal_cell(self, c): + """ + Return whether the given cell ``c`` is a maximal cell of ``self``. + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2, p3]) + sage: pc.has_maximal_cell(p1) + True + sage: pc.has_maximal_cell(p3) + False + + Wrong answer due to ``maximality_check=False``:: + + sage: pc_invalid = PolyhedralComplex([p1, p2, p3], + ....: maximality_check=False) + sage: pc_invalid.has_maximal_cell(p3) + True + """ + d = c.dimension() + return (c in self.n_maximal_cells(d)) + + def has_cell(self, c): + """ + Return whether the given cell ``c`` is a cell of ``self``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2]) + sage: pc.has_cell(p3) + True + sage: pc.has_cell(Polyhedron(vertices=[(0, 0)])) + True + """ + d = c.dimension() + return (c in self.n_cells(d)) + + def dimension(self): + """ + The dimension of this cell complex: the maximum + dimension of its cells. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 2)]) ]) + sage: pc.dimension() + 2 + sage: empty_pc = PolyhedralComplex([]) + sage: empty_pc.dimension() + -1 + """ + return self._dim + + def ambient_dimension(self): + """ + The ambient dimension of this cell complex: the ambient + dimension of each of its cells. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([Polyhedron(vertices=[(1, 2, 3)])]) + sage: pc.ambient_dimension() + 3 + sage: empty_pc = PolyhedralComplex([]) + sage: empty_pc.ambient_dimension() + -1 + """ + return self._ambient_dim + + def plot(self, **kwds): + """ + Return a plot of the polyhedral complex, if it is of dim at most 2. + """ + if self.dimension() > 2: + raise ValueError("Cannot plot in high dimension") + return sum(cell.plot(**kwds) for cell in self.maximal_cell_iterator()) + + def is_pure(self): + """ + Test if this polyhedral complex is pure. + + A polyhedral complex is pure if and only if all of its maximal cells + have the same dimension. + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2, p3]) + sage: pc.is_pure() + True + + Wrong answer due to ``maximality_check=False``:: + + sage: pc_invalid = PolyhedralComplex([p1, p2, p3], + ....: maximality_check=False) + sage: pc_invalid.is_pure() + False + """ + return len(self._maximal_cells) == 1 + + def is_full_dimensional(self): + """ + Return whether this polyhedral complex is full-dimensional: + its dimension is equal to its ambient dimension. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2, p3]) + sage: pc.is_full_dimensional() + True + sage: PolyhedralComplex([p3]).is_full_dimensional() + False + """ + return self._dim == self._ambient_dim + + def __hash__(self): + """ + Compute the hash value of ``self`` using its ``maximal_cells_list``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]) + sage: pc1 = PolyhedralComplex([p1, p2]) + sage: hash(pc1) == hash(pc1) + True + sage: pc2 = PolyhedralComplex([p2, p1]) + sage: hash(pc1) == hash(pc2) + True + """ + if not self._is_immutable: + raise ValueError("This polyhedral complex must be immutable" + + "Call set_immutable().") + return hash(tuple(self.maximal_cells_list())) + + def __eq__(self, right): + """ + Two polyhedral complexes are equal iff their maximal cells are equal. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]) + sage: pc1 = PolyhedralComplex([p1, p2]) + sage: pc1 == pc1 + True + sage: pc2 = PolyhedralComplex([p2, p1]) + sage: pc1 == pc2 + True + """ + return isinstance(right, PolyhedralComplex) and self.maximal_cells_list() == right.maximal_cells_list() + + def __ne__(self, right): + """ + Return ``True`` if ``self`` and ``right`` are not equal. + + EXAMPLES:: + + sage: pc1 = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)])]) + sage: pc2 = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) + sage: pc1 != pc2 + True + """ + return not self.__eq__(right) + + def __copy__(self): + """ + Return a copy of ``self``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]) + sage: p2 = copy(p1) + sage: p1 == p2 + True + """ + return PolyhedralComplex(self._maximal_cells, maximality_check=False) + + def _an_element_(self): + """ + Return a (maximal) cell of this complex. + + EXAMPLES:: + + sage: PolyhedralComplex()._an_element_() + () + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) + sage: pc._an_element_().vertices_list() + [[0, 0], [0, 1/2], [1, 2]] + """ + try: + return next(self.maximal_cell_iterator(increasing=False)) + except StopIteration: + return () + + def __contains__(self, x): + """ + True if ``x`` is a polyhedron which is contained in this complex. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]) + sage: pc = PolyhedralComplex([p1, p2]) + sage: (p1 in pc) and (p2 in pc) + True + sage: Polyhedron(vertices=[(1, 2), (0, 0)]) in pc + True + sage: Polyhedron(vertices=[(1, 1), (0, 0)]) in pc + False + sage: Polyhedron(vertices=[(0, 0)]) in pc + True + sage: (0, 0) in pc # not a polyhedron + False + """ + if not is_Polyhedron(x): + return False + dim = x.dimension() + return dim in self.cells() and x in self.cells()[dim] + + def __call__(self, x): + """ + If ``x`` is a polyhedron in this complex, return it. + Otherwise, raise a ``ValueError``. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) + sage: pc(Polyhedron(vertices=[(1, 2), (0, 0)])) + A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: pc(Polyhedron(vertices=[(1, 1)])) + Traceback (most recent call last): + ... + ValueError: the polyhedron is not in this complex + """ + if x not in self: + raise ValueError('the polyhedron is not in this complex') + return x + + def face_poset(self): + r""" + The face poset of this polyhedral complex, the poset of + nonempty cells, ordered by inclusion. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) + sage: poset = pc.face_poset() + sage: poset + Finite poset containing 11 elements + sage: d = {i:i.vertices_matrix() for i in poset} + sage: poset.plot(element_labels=d) # not tested + + TESTS on nonbounded polyhedral complex:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]), + ....: Polyhedron(vertices=[(-1/2, -1/2)], lines=[(1, -1)]), + ....: Polyhedron(rays=[(1, 0)])]) + sage: poset = pc.face_poset() + sage: poset + Finite poset containing 13 elements + sage: d = {i:''.join([str(v)+'\n' + ....: for v in i.Vrepresentation()]) for i in poset} + sage: poset.show(element_labels=d, figsize=15) # not tested + sage: pc = PolyhedralComplex([ + ....: Polyhedron(rays=[(1,0),(0,1)]), + ....: Polyhedron(rays=[(-1,0),(0,1)]), + ....: Polyhedron(rays=[(-1,0),(0,-1)]), + ....: Polyhedron(rays=[(1,0),(0,-1)])]) + sage: pc.face_poset() + Finite poset containing 9 elements + """ + if self._face_poset is None: + cells = self.cells() # poset is obtained and cached in that method + return self._face_poset + + def is_subcomplex(self, other): + r""" + Return True if ``self`` is a subcomplex of ``other``. + + :param other: a polyhedral complex + + Each maximal cell of ``self`` must be a cell of ``other`` + for this to be True. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]) + sage: p3 = Polyhedron(vertices=[(0, 0), (1, 0)]) + sage: pc = PolyhedralComplex([p1, Polyhedron(vertices=[(1, 0)])]) + sage: pc.is_subcomplex(PolyhedralComplex([p1, p2, p3])) + True + sage: pc.is_subcomplex(PolyhedralComplex([p1, p2])) + False + """ + other_cells = other.cells() + for (d, stratum) in self.maximal_cells().items(): + if not stratum.issubset(other_cells.get(d, set([]))): + return False + return True + + def is_compact(self): + """ + Test for boundedness of the polyhedral complex + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]) + sage: p2 = Polyhedron(rays=[(1, 0)]) + sage: PolyhedralComplex([p1]).is_compact() + True + sage: PolyhedralComplex([p1, p2]).is_compact() + False + """ + return all(p.is_compact() for p in self.maximal_cell_iterator()) + + def graph(self): + """ + The 1-skeleton of this polyhedral complex, as a graph. + The vertices of the graph are of type ``vector``. + Raise NotImplementedError if the polyhedral complex is unbounded. + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: g = pc.graph(); g + Graph on 4 vertices + sage: g.vertices() + [(0, 0), (0, 2), (1, 1), (1, 2)] + sage: g.edges(labels=False) + [((0, 0), (0, 2)), ((0, 0), (1, 1)), ((0, 0), (1, 2)), ((0, 2), (1, 2)), ((1, 1), (1, 2))] + sage: PolyhedralComplex([Polyhedron(rays=[(1,1)])]).graph() + Traceback (most recent call last): + ... + NotImplementedError: The polyhedral complex is unbounded. + + Wrong answer due to ``maximality_check=False``:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: PolyhedralComplex([p1, p2]).is_pure() + True + sage: PolyhedralComplex([p2, p3], maximality_check=True).is_pure() + True + sage: PolyhedralComplex([p2, p3], maximality_check=False).is_pure() + False + """ + if not self.is_compact(): + raise NotImplementedError("The polyhedral complex is unbounded.") + edges = self.n_cells(1) + d = {} + for e in edges: + v, max_e = sorted(e.vertices_matrix().columns()) + if v in d: + d[v].append(max_e) + else: + d[v] = [max_e] + for v in self.n_maximal_cells(0): + d[v] = [] + return Graph(d) + + def is_connected(self): + """ + True if this cell complex is connected. + + EXAMPLES:: + + sage: pc1 = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: pc1.is_connected() + True + sage: pc2 = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(0, 2)])]) + sage: pc2.is_connected() + False + sage: pc3 = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]), + ....: Polyhedron(vertices=[(-1/2, -1/2)], lines=[(1, -1)]), + ....: Polyhedron(rays=[(1, 0)])]) + sage: pc3.is_connected() + False + sage: pc4 = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(rays=[(1, 0)])]) + sage: pc4.is_connected() + True + """ + if self.is_compact(): + return self.graph().is_connected() #faster than using poset? + else: + return self.face_poset().is_connected() + + def connected_component(self, cell=None): + """ + Return the connected component of this polyhedral complex + containing ``cell``. If ``cell`` is omitted, then return + the connected component containing the self._an_element_. + (If the polyhedral complex is empty or if it does not contain the + given cell, raise an error.) + + EXAMPLES:: + + sage: t1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: t2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: v1 = Polyhedron(vertices=[(1, 1)]) + sage: v2 = Polyhedron(vertices=[(0, 2)]) + sage: v3 = Polyhedron(vertices=[(-1, 0)]) + sage: o = Polyhedron(vertices=[(0, 0)]) + sage: r = Polyhedron(rays=[(1, 0)]) + sage: l = Polyhedron(vertices=[(-1, 0)], lines=[(1, -1)]) + sage: pc1 = PolyhedralComplex([t1, t2]) + sage: pc1.connected_component() == pc1 + True + sage: pc1.connected_component(v1) == pc1 + True + sage: pc2 = PolyhedralComplex([t1, v2]) + sage: pc2.connected_component(t1) == PolyhedralComplex([t1]) + True + sage: pc2.connected_component(o) == PolyhedralComplex([t1]) + True + sage: pc2.connected_component(v3) + Traceback (most recent call last): + ... + ValueError: the polyhedral complex does not contain the given cell + sage: pc2.connected_component(r) + Traceback (most recent call last): + ... + ValueError: the polyhedral complex does not contain the given cell + sage: pc3 = PolyhedralComplex([t1, t2, r]) + sage: pc3.connected_component(v2) == pc3 + True + sage: pc4 = PolyhedralComplex([t1, t2, r, l]) + sage: pc4.connected_component(o) == pc3 + True + sage: pc4.connected_component(v3) + Traceback (most recent call last): + ... + ValueError: the polyhedral complex does not contain the given cell + sage: pc5 = PolyhedralComplex([t1, t2, r, l, v3]) + sage: pc5.connected_component(v3) == PolyhedralComplex([v3]) + True + sage: PolyhedralComplex([]).connected_component() + Traceback (most recent call last): + ... + ValueError: the empty polyhedral complex has no connected components + """ + if self.dimension() == -1: + raise ValueError( + "the empty polyhedral complex has no connected components") + if cell is None: + cell = self._an_element_() + if self.is_compact(): # use graph (faster than poset?) + if not cell.is_compact(): + raise ValueError( + "the polyhedral complex does not contain the given cell") + v = cell.vertices_matrix().columns()[0] + g = self.graph() + if not v in g: + raise ValueError( + "the polyhedral complex does not contain the given cell") + vertices = g.connected_component_containing_vertex(v) + facets = [f for f in self.maximal_cell_iterator() + if any(vf in f.vertices_matrix().columns() for vf in vertices)] + else: # use face_poset + g = self.face_poset().hasse_diagram() + if not cell in g: + raise ValueError( + "the polyhedral complex does not contain the given cell") + faces = g.connected_component_containing_vertex(cell) + facets = [f for f in self.maximal_cell_iterator() + if f in faces] + return PolyhedralComplex(facets, maximality_check=False) + + def connected_components(self): + """ + Return the connected components of this polyhedral complex, + as list of (sub-)PolyhedralComplexes. + + EXAMPLES:: + + sage: t1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: t2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: v1 = Polyhedron(vertices=[(1, 1)]) + sage: v2 = Polyhedron(vertices=[(0, 2)]) + sage: v3 = Polyhedron(vertices=[(-1, 0)]) + sage: o = Polyhedron(vertices=[(0, 0)]) + sage: r = Polyhedron(rays=[(1, 0)]) + sage: l = Polyhedron(vertices=[(-1, 0)], lines=[(1, -1)]) + sage: pc1 = PolyhedralComplex([t1, t2]) + sage: len(pc1.connected_components()) + 1 + sage: pc2 = PolyhedralComplex([t1, v2]) + sage: len(pc2.connected_components()) + 2 + sage: pc3 = PolyhedralComplex([t1, t2, r]) + sage: len(pc3.connected_components()) + 1 + sage: pc4 = PolyhedralComplex([t1, t2, r, l]) + sage: len(pc4.connected_components()) + 2 + sage: pc5 = PolyhedralComplex([t1, t2, r, l, v3]) + sage: len(pc5.connected_components()) + 3 + sage: PolyhedralComplex([]).connected_components() + Traceback (most recent call last): + ... + ValueError: the empty polyhedral complex has no connected components + """ + if self.dimension() == -1: + raise ValueError( + "the empty polyhedral complex has no connected components") + if self.is_compact(): # use graph (faster than poset)? + g = self.graph() + lists_of_vertices = g.connected_components(sort=False) + lists_of_facets = [ [f for f in self.maximal_cell_iterator() + if any(vf in f.vertices_matrix().columns() for vf in vertices)] + for vertices in lists_of_vertices ] + else: # use face_poset + g = self.face_poset().hasse_diagram() + lists_of_faces = g.connected_components(sort=False) + lists_of_facets =[ + [f for f in self.maximal_cell_iterator() if f in faces] + for faces in lists_of_faces] + results = [PolyhedralComplex(facets, maximality_check=False) + for facets in lists_of_facets] + return results + + def n_skeleton(self, n): + """ + The `n`-skeleton of this polyhedral complex. + + The `n`-skeleton of a polyhedral complex is obtained by discarding + all of the cells in dimensions larger than `n`. + + :param n: non-negative integer + + .. SEEALSO:: + + :meth:`stratify` + + EXAMPLES:: + + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + sage: pc.n_skeleton(2) + Cell complex with 4 vertices and 11 cells + sage: pc.n_skeleton(1) + Cell complex with 4 vertices and 9 cells + sage: pc.n_skeleton(0) + Cell complex with 4 vertices and 4 cells + """ + if n >= self.dimension(): + return copy(self) + facets = [f for f in self.maximal_cell_iterator() if f.dimension() < n] + facets.extend(self.n_cells(n)) + return PolyhedralComplex(facets, maximality_check=False) + + def stratify(self, n): + """ + Return the pure sub-polyhedral complex which is constructed from the + n-dimensional maximal cells of this polyhedral complex. + + .. SEEALSO:: + + :meth:`n_skeleton` + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2, p3]) + sage: pc.stratify(2) == pc + True + sage: pc.stratify(1) + Cell complex with 0 vertices and 0 cells + + Wrong answer due to ``maximality_check=False``:: + + sage: pc_invalid = PolyhedralComplex([p1, p2, p3], + ....: maximality_check=False) + sage: pc_invalid.stratify(1) + Cell complex with 2 vertices and 3 cells + """ + n_faces = self.n_maximal_cells(n) + return PolyhedralComplex(n_faces, maximality_check=False) + + def boundary_subcomplex(self): + """ + Return the sub-polyhedral complex that is the boundary of ``self``. + + A point `P` is on the boundary of a set `S` if `P` is in the + closure of `S` but not in the interoir of `S`. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: bd = PolyhedralComplex([p1, p2]).boundary_subcomplex() + sage: len(bd.n_maximal_cells(2)) + 0 + sage: len(bd.n_maximal_cells(1)) + 4 + sage: pt = PolyhedralComplex([p3]) + sage: pt.boundary_subcomplex() == pt + True + + Test on polyhedral complex which is not pure:: + + sage: pc_non_pure = PolyhedralComplex([p1, p3]) + sage: pc_non_pure.boundary_subcomplex() == pc_non_pure.n_skeleton(1) + True + + Test with ``maximality_check == False``:: + + sage: pc_invalid = PolyhedralComplex([p2, p3], + ....: maximality_check=False) + sage: pc_invalid.boundary_subcomplex() == pc_invalid.n_skeleton(1) + True + + Test unbounded cases:: + + sage: pc1 = PolyhedralComplex([ + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]])]) + sage: pc1.boundary_subcomplex() == pc1.n_skeleton(1) + True + sage: pc1b = PolyhedralComplex([Polyhedron( + ....: vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0],[0,1,0]])]) + sage: pc1b.boundary_subcomplex() == pc1b + True + sage: pc2 = PolyhedralComplex([ + ....: Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]])]) + sage: pc2.boundary_subcomplex() == pc2.n_skeleton(1) + True + sage: pc3 = PolyhedralComplex([ + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), + ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) + sage: pc3.boundary_subcomplex() == pc3.n_skeleton(1) + False + """ + if self.is_full_dimensional(): + return PolyhedralComplex(self.relative_boundary_cells()) + else: + return copy(self) + + def relative_boundary_cells(self): + r""" + Return the maximal cells of the relative-boundary sub-complex. + + A point `P` is in the relative boundary of a set `S` if `P` is in the + closure of `S` but not in the relative interoir of `S`. + + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: p4 = Polyhedron(vertices=[(2, 2)]) + sage: pc = PolyhedralComplex([p1, p2]) + sage: rbd_cells = pc.relative_boundary_cells() + sage: len(rbd_cells) + 4 + sage: all(p.dimension() == 1 for p in rbd_cells) + True + sage: pc_lower_dim = PolyhedralComplex([p3]) + sage: [p.vertices() for p in pc_lower_dim.relative_boundary_cells()] + [(A vertex at (0, 2),), (A vertex at (1, 2),)] + + Test on polyhedral complex which is not pure:: + + sage: pc_non_pure = PolyhedralComplex([p1, p3, p4]) + sage: set(pc_non_pure.relative_boundary_cells()) == set( + ....: [f.as_polyhedron() for f in p1.faces(1)] + [p3, p4]) + True + + Test with ``maximality_check == False``:: + + sage: pc_invalid = PolyhedralComplex([p2, p3], + ....: maximality_check=False) + sage: set(pc_invalid.relative_boundary_cells()) == set( + ....: [f.as_polyhedron() for f in p2.faces(1)]) + True + + Test unbounded case:: + + sage: pc3 = PolyhedralComplex([ + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), + ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) + sage: len(pc3.relative_boundary_cells()) + 4 + """ + d = self.dimension() + poset = self.face_poset() + faces = self.n_cells(d - 1) + ans = [face for face in faces if len(poset.upper_covers(face)) == 1] + if not self.is_pure(): + ans += [p for p in poset.maximal_elements() if p.dimension() < d] + return ans + + def is_convex(self): + """ + Return whether the set of points in ``self`` is a convex set. + + When ``self`` is convex, the union of its cells is a Polyhedron. + + .. SEEALSO:: + + :meth:`union_as_polyhedron` + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(0, 0), (1, 1), (2, 0)]) + sage: p4 = Polyhedron(vertices=[(2, 2)]) + sage: PolyhedralComplex([p1, p2]).is_convex() + True + sage: PolyhedralComplex([p1, p3]).is_convex() + False + sage: PolyhedralComplex([p1, p4]).is_convex() + False + + Test unbounded cases:: + + sage: pc1 = PolyhedralComplex([ + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]])]) + sage: pc1.is_convex() + True + sage: pc2 = PolyhedralComplex([ + ....: Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]])]) + sage: pc2.is_convex() + True + sage: pc3 = PolyhedralComplex([ + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), + ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) + sage: pc3.is_convex() + False + sage: pc4 = PolyhedralComplex([Polyhedron(rays=[[1,0], [-1,1]]), + ....: Polyhedron(rays=[[1,0], [-1,-1]])]) + sage: pc4.is_convex() + False + + The whole 3d space minus the first orthant is not convex:: + + sage: pc5 = PolyhedralComplex([ + ....: Polyhedron(rays=[[1,0,0], [0,1,0], [0,0,-1]]), + ....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,-1]]), + ....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,1]]), + ....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,-1]]), + ....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,1]]), + ....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,-1]]), + ....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,1]])]) + sage: pc5.is_convex() + False + + Test some non-full-dimensional examples:: + + sage: l = PolyhedralComplex([Polyhedron(vertices=[(1, 2), (0, 2)])]) + sage: l.is_convex() + True + sage: pc1b = PolyhedralComplex([Polyhedron( + ....: vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0],[0,1,0]])]) + sage: pc1b.is_convex() + True + sage: pc4b = PolyhedralComplex([ + ....: Polyhedron(rays=[[1,0,0], [-1,1,0]]), + ....: Polyhedron(rays=[[1,0,0], [-1,-1,0]])]) + sage: pc4b.is_convex() + False + """ + if self._is_convex is not None: + return self._is_convex + if not self.is_pure(): + self._is_convex = False + return False + d = self.dimension() + if not self.is_full_dimensional(): + # if max cells must lie in different subspaces, can't be convex. + from sage.modules.free_module import span + f = self.n_maximal_cells(d)[0] + affine_space = span(f.equations_list(), f.base_ring()) + for f in self.n_maximal_cells(d)[1::]: + if span(f.equations_list(), f.base_ring()) != affine_space: + self._is_convex = False + return False + # orient the (relative) boundary halfspaces toward a strict convex + # combination of the vertices. Then check if all vertices are contained + # After making sure that the affine hulls of the cells are the same, + # it does not matter that is not full dimensional. + boundaries = self.relative_boundary_cells() + vertices = set([]) + rays = set([]) + lines = set([]) + for cell in boundaries: + # it suffices to consider only vertices on the boundaries + # Note that a line (as polyhedron) has vertex too + for v in cell.vertices_list(): + vv = vector(v) + vv.set_immutable() + vertices.add(vv) + for cell in self.n_maximal_cells(d): + for r in cell.rays_list(): + rr = vector(r) + rr.set_immutable() + rays.add(rr) + for l in cell.lines_list(): + ll = vector(l) + ll.set_immutable() + lines.add(ll) + center = sum(vertices) / len(vertices) + for cell in boundaries: + for equation in cell.equations_list(): + coeff = vector(equation[1::]) + const = equation[0] + if const + coeff * center == 0: + sign = 0 + elif const + coeff * center > 0: + sign = 1 + for v in vertices: + if const + coeff * v < 0: + self._is_convex = False + return False + elif const + coeff * center < 0: + sign = -1 + for v in vertices: + if const + coeff * v > 0: + self._is_convex = False + return False + for r in rays: + if sign == 0: + sign = coeff * r + else: + if sign * (coeff * r) < 0: + self._is_convex = False + return False + # lines are in the affine space of each boundary cell already + self._is_convex = True + self._polyhedron = Polyhedron(vertices=vertices, rays=rays, lines=lines) + return True + + def union_as_polyhedron(self): + """ + Assuming the polyhedral complex is convex, return it as a Polyhedron. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(0, 0), (1, 1), (2, 0)]) + sage: P = PolyhedralComplex([p1, p2]).union_as_polyhedron() + sage: P.vertices_list() + [[0, 0], [0, 2], [1, 1], [1, 2]] + sage: PolyhedralComplex([p1, p3]).union_as_polyhedron() + Traceback (most recent call last): + ... + ValueError: The polyhedral complex is not convex. + """ + if not self.is_convex(): + raise ValueError("The polyhedral complex is not convex.") + return self._polyhedron + + ############################################################ + # abstract methods not implemented in generic cell complexe + ############################################################ + + def product(self, right, rename_vertices=True): + raise NotImplementedError + + def disjoint_union(self, right): + raise NotImplementedError + + def wedge(self, right): + raise NotImplementedError + + def join(self, right): + raise NotImplementedError + + ############################################################ + # chain complexes, homology + ############################################################ + def chain_complex(self, subcomplex=None, augmented=False, + verbose=False, check=True, dimensions=None, + base_ring=ZZ, cochain=False): + raise NotImplementedError + + def alexander_whitney(self, cell, dim_left): + raise NotImplementedError + + ############################################################ + # end of chain complexes, homology + ############################################################ + +# TODO: mutable complex: add and update stuff incrementally +# TODO: replace one cell by its triangulation and adapt other cells +# TODO: graph of maximal cells by wall-crossing # use poset.meet instead +# TODO: SimplicialComplex to PolyhedralComplex: geometric realization +# TODO: learn about the boundary stuff of chain complex From 577456a0c3ffddef49d02301c4b59a73a7acd636 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Wed, 28 Apr 2021 11:32:31 -0400 Subject: [PATCH 016/336] make tox happy --- src/sage/homology/polyhedral_complex.py | 78 ++++++++++++++----------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 0011f690cd0..5ac1be4de6a 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -31,6 +31,7 @@ from sage.rings.rational_field import QQ from sage.graphs.graph import Graph + class PolyhedralComplex(GenericCellComplex): r""" Define a PolyhedralComplex. @@ -73,7 +74,7 @@ class PolyhedralComplex(GenericCellComplex): True """ def __init__(self, maximal_cells=None, maximality_check=True, - face_to_face_check=False): + face_to_face_check=False): r""" INPUT: @@ -88,7 +89,7 @@ def __init__(self, maximal_cells=None, maximality_check=True, - ``maximality_check`` -- boolean; default ``True``; if it is ``True``, then the constructor checks that each given - maximal cell is indeed maximal, ane ignores those that are not. + maximal cell is indeed maximal, and ignores those that are not. - ``face_to_face_check`` -- boolean; default ``False``; @@ -149,35 +150,35 @@ def cells_list_to_cells_dict(cells_list): self._dim = max(cells_dict.keys()) self._ambient_dim = next(iter(cells_dict[self._dim])).ambient_dim() self._maximal_cells = cells_dict - if not all( (is_Polyhedron(cell) and - cell.ambient_dim() == self._ambient_dim) - for cell in self.maximal_cell_iterator()): + if not all((is_Polyhedron(cell) and + cell.ambient_dim() == self._ambient_dim) + for cell in self.maximal_cell_iterator()): raise ValueError("The given cells are not polyhedra" + "in the same ambient space.") # initialize the attributes self._is_convex = None self._polyhedron = None - self._maximal_cells_list = None # needed for hash + self._maximal_cells_list = None # needed for hash self._cells = None self._face_poset = None if maximality_check: - cells = self.cells() # compute self._cells and self._face_poset + cells = self.cells() # compute self._cells and self._face_poset self._maximal_cells = cells_list_to_cells_dict( self._face_poset.maximal_elements()) if face_to_face_check: poset = self.face_poset() - maximal_cells = poset.maximal_elements() # a list + maximal_cells = poset.maximal_elements() # a list for i in range(len(maximal_cells)): p = maximal_cells[i] for j in range(i, len(maximal_cells)): q = maximal_cells[j] r = p.intersection(q) if not (r.is_empty() or (r in poset) and - poset.is_gequal(p,r) and poset.is_gequal(q,r)): + poset.is_gequal(p, r) and poset.is_gequal(q, r)): raise ValueError("The given cells are not face-to-face") # For now, a polyhedral complex is immutable - #TODO: is_mutable and is_immutable paramters and set_immutable method. + # TODO: is_mutable and is_immutable parameters and set_immutable method. self._is_immutable = True def cells(self, subcomplex=None): @@ -209,19 +210,19 @@ def cells(self, subcomplex=None): covers = {} for k in range(self._dim, -1, -1): if k in maximal_cells: - if not k in cells: + if k not in cells: cells[k] = set([]) cells[k].update(maximal_cells[k]) if k in cells: for cell in cells[k]: - if not cell in covers: + if cell not in covers: covers[cell] = [] for facet in cell.facets(): p = facet.as_polyhedron() - if not p in covers: + if p not in covers: covers[p] = [] covers[p].append(cell) - if not (k-1) in cells: + if (k-1) not in cells: cells[k-1] = set([]) cells[k-1].add(p) from sage.combinat.posets.posets import Poset @@ -563,6 +564,14 @@ def ambient_dimension(self): def plot(self, **kwds): """ Return a plot of the polyhedral complex, if it is of dim at most 2. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: pc = PolyhedralComplex([p1, p2]) + sage: pc.plot() + Graphics object consisting of 10 graphics primitives """ if self.dimension() > 2: raise ValueError("Cannot plot in high dimension") @@ -633,7 +642,7 @@ def __hash__(self): """ if not self._is_immutable: raise ValueError("This polyhedral complex must be immutable" + - "Call set_immutable().") + "Call set_immutable().") return hash(tuple(self.maximal_cells_list())) def __eq__(self, right): @@ -651,7 +660,8 @@ def __eq__(self, right): sage: pc1 == pc2 True """ - return isinstance(right, PolyhedralComplex) and self.maximal_cells_list() == right.maximal_cells_list() + return isinstance(right, PolyhedralComplex) and ( + self.maximal_cells_list() == right.maximal_cells_list()) def __ne__(self, right): """ @@ -784,7 +794,7 @@ def face_poset(self): Finite poset containing 9 elements """ if self._face_poset is None: - cells = self.cells() # poset is obtained and cached in that method + cells = self.cells() # poset is obtained and cached in cells() return self._face_poset def is_subcomplex(self, other): @@ -911,7 +921,7 @@ def is_connected(self): True """ if self.is_compact(): - return self.graph().is_connected() #faster than using poset? + return self.graph().is_connected() # faster than using poset? else: return self.face_poset().is_connected() @@ -974,26 +984,27 @@ def connected_component(self, cell=None): "the empty polyhedral complex has no connected components") if cell is None: cell = self._an_element_() - if self.is_compact(): # use graph (faster than poset?) + if self.is_compact(): # use graph (faster than poset?) if not cell.is_compact(): raise ValueError( "the polyhedral complex does not contain the given cell") v = cell.vertices_matrix().columns()[0] g = self.graph() - if not v in g: + if v not in g: raise ValueError( "the polyhedral complex does not contain the given cell") vertices = g.connected_component_containing_vertex(v) facets = [f for f in self.maximal_cell_iterator() - if any(vf in f.vertices_matrix().columns() for vf in vertices)] - else: # use face_poset + if any(vf in f.vertices_matrix().columns() + for vf in vertices)] + else: # use face_poset g = self.face_poset().hasse_diagram() - if not cell in g: + if cell not in g: raise ValueError( "the polyhedral complex does not contain the given cell") faces = g.connected_component_containing_vertex(cell) facets = [f for f in self.maximal_cell_iterator() - if f in faces] + if f in faces] return PolyhedralComplex(facets, maximality_check=False) def connected_components(self): @@ -1034,20 +1045,21 @@ def connected_components(self): if self.dimension() == -1: raise ValueError( "the empty polyhedral complex has no connected components") - if self.is_compact(): # use graph (faster than poset)? + if self.is_compact(): # use graph (faster than poset)? g = self.graph() lists_of_vertices = g.connected_components(sort=False) - lists_of_facets = [ [f for f in self.maximal_cell_iterator() - if any(vf in f.vertices_matrix().columns() for vf in vertices)] - for vertices in lists_of_vertices ] - else: # use face_poset + lists_of_facets = [[f for f in self.maximal_cell_iterator() + if any(vf in f.vertices_matrix().columns() + for vf in vertices)] + for vertices in lists_of_vertices] + else: # use face_poset g = self.face_poset().hasse_diagram() lists_of_faces = g.connected_components(sort=False) - lists_of_facets =[ + lists_of_facets = [ [f for f in self.maximal_cell_iterator() if f in faces] for faces in lists_of_faces] results = [PolyhedralComplex(facets, maximality_check=False) - for facets in lists_of_facets] + for facets in lists_of_facets] return results def n_skeleton(self, n): @@ -1341,8 +1353,8 @@ def is_convex(self): rr = vector(r) rr.set_immutable() rays.add(rr) - for l in cell.lines_list(): - ll = vector(l) + for li in cell.lines_list(): + ll = vector(li) ll.set_immutable() lines.add(ll) center = sum(vertices) / len(vertices) From 38aaf756f91cf3a7e652e9dfdcc0696a1c632afd Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Thu, 29 Apr 2021 02:38:47 -0400 Subject: [PATCH 017/336] add doc for html build --- src/doc/en/reference/homology/index.rst | 1 + src/sage/homology/polyhedral_complex.py | 92 ++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst index bf98e0841fd..f61394f9b3a 100644 --- a/src/doc/en/reference/homology/index.rst +++ b/src/doc/en/reference/homology/index.rst @@ -19,6 +19,7 @@ cell complexes. sage/homology/simplicial_complex sage/homology/simplicial_complex_morphism sage/homology/simplicial_complex_homset + sage/homology/polyhedral_complex sage/homology/examples sage/homology/delta_complex sage/homology/cubical_complex diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 5ac1be4de6a..a29bd903a89 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -4,11 +4,98 @@ This module implements the basic structure of finite polyhedral complexes. -Description to be completed. +A polyhedral complex `PC` is a collection of polyhedra in a certain ambient +space `\RR^n` such that + +- If a poyhedron `P` is in `PC`, then all the faces of `P` are in `PC`. + +- If polyhedra `P` and `Q` are in `PC`, then `P \cap Q` is either empty or a face of both `P` and `Q`. + +In this context, a "polyhedron" means the geometric realization +of a polyhedron. This is in contrast to :mod:`simplicial complex +`, whose cells are abstract simplices. +The concept of a polyhedral complex generalizes that of a **geometric** +simplicial complex. + +.. note:: + + This class derives from + :class:`~sage.homology.cell_complex.GenericCellComplex`, and so + inherits its methods. Some of those methods are not listed here; + see the :mod:`Generic Cell Complex ` + page instead. AUTHORS: - Yuan Zhou (2021-04): initial implementation + +List of PolyhedralComplex methods +--------------------------------- + +**Maximal cells and cells** + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :meth:`~PolyhedralComplex.maximal_cells` | Return the dictionary of the maximal cells in this polyhedral complex. + :meth:`~PolyhedralComplex.maximal_cell_iterator` | Return an iterator over maximal cells in this polyhedral complex. + :meth:`~PolyhedralComplex.maximal_cells_list` | Return the sorted list of all maximal cells in this polyhedral complex. + :meth:`~PolyhedralComplex.n_maximal_cells` | List the maximal cells of dimension `n` in this polyhedral complex. + :meth:`~PolyhedralComplex._n_maximal_cells_sorted` | Return the sorted list of maximal cells of dim `n` in this complex. + :meth:`~PolyhedralComplex.has_maximal_cell` | Return ``True`` if the given cell is a maximal cell in this complex. + :meth:`~PolyhedralComplex.cells` | Return the dictionary of the cells in this polyhedral complex. + :meth:`~PolyhedralComplex.cell_iterator` | Return an iterator over cells in this polyhedral complex. + :meth:`~PolyhedralComplex.cells_list` | Return the sorted list of all cells in this polyhedral complex. + :meth:`~sage.homology.cell_complex.GenericCellComplex.n_cells` | List the cells of dimension `n` in this polyhedral complex. + :meth:`~PolyhedralComplex._n_cells_sorted` | Return the sorted list of `n`-cells in this polyhedral complex. + :meth:`~PolyhedralComplex.has_cell` | Return ``True`` if the given cell is in this polyhedral complex. + :meth:`~PolyhedralComplex.face_poset` | Return the poset of nonempty cells in the polyhedral complex. + :meth:`~PolyhedralComplex.relative_boundary_cells` | List the maximal cells on the boundary of the polyhedral complex. + +**Properties of the polyhedral complex** + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :meth:`~PolyhedralComplex.dimension` | Return the dimension of the polyhedral complex. + :meth:`~PolyhedralComplex.ambient_dimension` | Return the ambient dimension of the polyhedral complex. + :meth:`~PolyhedralComplex.is_pure` | Return ``True`` if the polyhedral complex is pure. + :meth:`~PolyhedralComplex.is_full_dimensional` | Return ``True`` if the polyhedral complex is full dimensional. + :meth:`~PolyhedralComplex.is_compact` | Return ``True`` if the polyhedral complex is bounded. + :meth:`~PolyhedralComplex.is_connected` | Return ``True`` if the polyhedral complex is connected. + :meth:`~PolyhedralComplex.is_subcomplex` | Return ``True`` if this complex is a subcomplex of the other. + :meth:`~PolyhedralComplex.is_convex` | Return ``True`` if the polyhedral complex is convex. + +**New polyhedral complexes from old ones** + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :meth:`~PolyhedralComplex.connected_component` | Return the connected component containing a cell as a subcomplex. + :meth:`~PolyhedralComplex.connected_components` | Return the connected components of this polyhedral complex. + :meth:`~PolyhedralComplex.n_skeleton` | Return the `n`-skeleton of this polyhedral complex. + :meth:`~PolyhedralComplex.stratify` | Return the (pure) subcomplex formed by the maximal cells of dim `n` in this complex. + :meth:`~PolyhedralComplex.boundary_subcomplex` | Return the boundary subcomplex of this polyhedral complex. + +**Miscellaneous** + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :meth:`~PolyhedralComplex.plot` | Return a Graphic object showing the plot of polyhedral complex. + :meth:`~PolyhedralComplex.graph` | Return a directed graph corresponding to the 1-skeleton of this polyhedral complex, given that it is bounded. + :meth:`~PolyhedralComplex.union_as_polyhedron` | Return a ``Polyhedron`` which is the union of cells in this polyhedral complex, given that it is convex. + +Classes and functions +--------------------- """ # **************************************************************************** @@ -60,6 +147,8 @@ class PolyhedralComplex(GenericCellComplex): True sage: pc.is_compact() True + sage: pc.boundary_subcomplex() + Cell complex with 4 vertices and 8 cells sage: pc.is_convex() True sage: pc.union_as_polyhedron().Hrepresentation() @@ -1445,3 +1534,4 @@ def alexander_whitney(self, cell, dim_left): # TODO: graph of maximal cells by wall-crossing # use poset.meet instead # TODO: SimplicialComplex to PolyhedralComplex: geometric realization # TODO: learn about the boundary stuff of chain complex +# TODO: Polyhedral Arrangement to PolyhedralComplex using #25122 From 22c902432730cd46535698aa7e8cd2652b6322c4 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Thu, 29 Apr 2021 17:12:14 -0400 Subject: [PATCH 018/336] new methods product, disjoint_union, join --- src/sage/homology/polyhedral_complex.py | 79 ++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index a29bd903a89..27e25ef20d0 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -82,6 +82,9 @@ :meth:`~PolyhedralComplex.n_skeleton` | Return the `n`-skeleton of this polyhedral complex. :meth:`~PolyhedralComplex.stratify` | Return the (pure) subcomplex formed by the maximal cells of dim `n` in this complex. :meth:`~PolyhedralComplex.boundary_subcomplex` | Return the boundary subcomplex of this polyhedral complex. + :meth:`~PolyhedralComplex.product` | Return the (Cartesian) product of this polyhedral complex with another one. + :meth:`~PolyhedralComplex.disjoint_union` | Return the disjoint union of this polyhedral complex with another one. + :meth:`~PolyhedralComplex.join` | Return the join of this polyhedral complex with another one. **Miscellaneous** @@ -1498,20 +1501,80 @@ def union_as_polyhedron(self): raise ValueError("The polyhedral complex is not convex.") return self._polyhedron - ############################################################ - # abstract methods not implemented in generic cell complexe - ############################################################ + def product(self, right): + """ + The (Cartesian) product of this polyhedral complex with another one. - def product(self, right, rename_vertices=True): - raise NotImplementedError + :param right: the other polyhedral complex (the right-hand + factor) + + :return: the product ``self x right`` + + EXAMPLES:: + + sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) + sage: pc_square = pc.product(pc) + sage: pc_square + Cell complex with 4 vertices and 9 cells + sage: next(pc_square.maximal_cell_iterator()).vertices() + (A vertex at (0, 0), + A vertex at (0, 1), + A vertex at (1, 0), + A vertex at (1, 1)) + """ + maximal_cells = [f.product(g) for f in self.maximal_cell_iterator() + for g in right.maximal_cell_iterator()] + return PolyhedralComplex(maximal_cells, maximality_check=False) def disjoint_union(self, right): - raise NotImplementedError + """ + The disjoint union of this polyhedral complex with another one. - def wedge(self, right): - raise NotImplementedError + :param right: the other polyhedral complex (the right-hand factor) + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(0, 0), (1, 1), (2, 0)]) + sage: pc1 = PolyhedralComplex([p1, p3]) + sage: pc2 = PolyhedralComplex([p2]) + sage: pc = pc1.disjoint_union(pc2) + sage: set(pc.maximal_cell_iterator()) == set([p1, p2, p3]) + True + """ + maximal_cells = list(self.maximal_cell_iterator()) + list( + right.maximal_cell_iterator()) + return PolyhedralComplex(maximal_cells, maximality_check=True, + face_to_face_check=True) def join(self, right): + """ + The join of this polyhedral complex with another one. + + :param right: the other polyhedral complex (the right-hand factor) + + EXAMPLES:: + + sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) + sage: pc_join = pc.join(pc) + sage: pc_join + Cell complex with 4 vertices and 15 cells + sage: next(pc_join.maximal_cell_iterator()).vertices() + (A vertex at (0, 0, 0), + A vertex at (0, 0, 1), + A vertex at (0, 1, 1), + A vertex at (1, 0, 0)) + """ + maximal_cells = [f.join(g) for f in self.maximal_cell_iterator() + for g in right.maximal_cell_iterator()] + return PolyhedralComplex(maximal_cells, maximality_check=False) + + ############################################################ + # abstract methods not implemented in generic cell complexe + ############################################################ + + def wedge(self, right): raise NotImplementedError ############################################################ From add1ec2f6e7220924b3f5eb1e2e9bc64f533801b Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sat, 1 May 2021 02:36:47 -0400 Subject: [PATCH 019/336] change _repr_ to show only maximal cells --- src/sage/homology/polyhedral_complex.py | 121 +++++++++++++----------- 1 file changed, 67 insertions(+), 54 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 27e25ef20d0..c1c98164951 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -126,6 +126,30 @@ class PolyhedralComplex(GenericCellComplex): r""" Define a PolyhedralComplex. + INPUT: + + - ``maximal_cells`` -- a list, a tuple, or a dictionary (indexed by + dimension) of cells of the Complex. Each cell is of class + :class:`Polyhedron` of the same ambient dimension. To set up a + :class:PolyhedralComplex, it is sufficient to provide the maximal + faces. Use keyword argument partial=``True`` to set up a partial + polyhedral complex, which is a subset of the faces (viewed as + relatively open) of a polyhedral complex that is not necessarily + closed under taking intersection. + + - ``maximality_check`` -- boolean; default ``True``; + if it is ``True``, then the constructor checks that each given + maximal cell is indeed maximal, and ignores those that are not. + + + - ``face_to_face_check`` -- boolean; default ``False``; + if it is ``True``, then the constructor checks whether the cells + are face-to-face, and it raises a ValueError if they are not. + + - ``is_mutable`` and ``is_immutable`` -- boolean; default ``True`` and + ``False``, respectively; Set ``is_mutable=False`` or ``is_immutable=True`` + to make this polyhedral complex immutable. + EXAMPLES:: sage: pc = PolyhedralComplex([ @@ -151,7 +175,7 @@ class PolyhedralComplex(GenericCellComplex): sage: pc.is_compact() True sage: pc.boundary_subcomplex() - Cell complex with 4 vertices and 8 cells + Polyhedral complex with 4 maximal cells sage: pc.is_convex() True sage: pc.union_as_polyhedron().Hrepresentation() @@ -164,58 +188,47 @@ class PolyhedralComplex(GenericCellComplex): True sage: pc.connected_component() == pc True - """ - def __init__(self, maximal_cells=None, maximality_check=True, - face_to_face_check=False): - r""" - INPUT: - - - ``maximal_cells`` -- a list, a tuple, or a dictionary (indexed by - dimension) of cells of the Complex. Each cell is of class - :class:`Polyhedron` of the same ambient dimension. To set up a - :class:PolyhedralComplex, it is sufficient to provide the maximal - faces. Use keyword argument partial=``True`` to set up a partial - polyhedral complex, which is a subset of the faces (viewed as - relatively open) of a polyhedral complex that is not necessarily - closed under taking intersection. - - - ``maximality_check`` -- boolean; default ``True``; - if it is ``True``, then the constructor checks that each given - maximal cell is indeed maximal, and ignores those that are not. - - - - ``face_to_face_check`` -- boolean; default ``False``; - if it is ``True``, then the constructor checks whether the cells - are face-to-face, and it raises a ValueError if they are not. - TESTS: + TESTS: - Check that non-maximal cells are ignored if ``maximality_check=True``:: + Check that non-maximal cells are ignored if ``maximality_check=True``:: - sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0)]) ]) - sage: pc.maximal_cells() - {2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}} - - Check that non face-to-face can be detected:: + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0)]) ]) + sage: pc.maximal_cells() + {2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}} + + Check that non face-to-face can be detected:: + + sage: PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(2, 2), (0, 0)]) ], + ....: face_to_face_check=True) + Traceback (most recent call last): + ... + ValueError: The given cells are not face-to-face + + Check that all the cells must have the same ambient dimension:: + + sage: PolyhedralComplex([ + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[[2], [0]]) ]) + Traceback (most recent call last): + ... + ValueError: The given cells are not polyhedrain the same ambient space. + """ + def __init__(self, maximal_cells=None, maximality_check=True, + face_to_face_check=False, is_mutable=True, is_immutable=False): + r""" + Define a PolyhedralComplex. - sage: PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(2, 2), (0, 0)]) ], - ....: face_to_face_check=True) - Traceback (most recent call last): - ... - ValueError: The given cells are not face-to-face + See ``PolyhedralComplex`` for more information. - Check that all the cells must have the same ambient dimension:: + EXAMPLES:: - sage: PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[[2], [0]]) ]) - Traceback (most recent call last): - ... - ValueError: The given cells are not polyhedrain the same ambient space. + sage: PolyhedralComplex([Polyhedron(vertices=[(1, 1), (0, 0)])]) + Polyhedral complex with 1 maximal cells """ def cells_list_to_cells_dict(cells_list): cells_dict = {} @@ -1173,11 +1186,11 @@ def n_skeleton(self, n): ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: pc.n_skeleton(2) - Cell complex with 4 vertices and 11 cells + Polyhedral complex with 2 maximal cells sage: pc.n_skeleton(1) - Cell complex with 4 vertices and 9 cells + Polyhedral complex with 5 maximal cells sage: pc.n_skeleton(0) - Cell complex with 4 vertices and 4 cells + Polyhedral complex with 4 maximal cells """ if n >= self.dimension(): return copy(self) @@ -1208,14 +1221,14 @@ def stratify(self, n): sage: pc.stratify(2) == pc True sage: pc.stratify(1) - Cell complex with 0 vertices and 0 cells + Polyhedral complex with 0 maximal cells Wrong answer due to ``maximality_check=False``:: sage: pc_invalid = PolyhedralComplex([p1, p2, p3], ....: maximality_check=False) sage: pc_invalid.stratify(1) - Cell complex with 2 vertices and 3 cells + Polyhedral complex with 1 maximal cells """ n_faces = self.n_maximal_cells(n) return PolyhedralComplex(n_faces, maximality_check=False) @@ -1515,7 +1528,7 @@ def product(self, right): sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) sage: pc_square = pc.product(pc) sage: pc_square - Cell complex with 4 vertices and 9 cells + Polyhedral complex with 1 maximal cells sage: next(pc_square.maximal_cell_iterator()).vertices() (A vertex at (0, 0), A vertex at (0, 1), @@ -1559,7 +1572,7 @@ def join(self, right): sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) sage: pc_join = pc.join(pc) sage: pc_join - Cell complex with 4 vertices and 15 cells + Polyhedral complex with 1 maximal cells sage: next(pc_join.maximal_cell_iterator()).vertices() (A vertex at (0, 0, 0), A vertex at (0, 0, 1), From 614dd2d3fa4f54d404f0c4078923c01ae093e7b3 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sat, 1 May 2021 02:38:42 -0400 Subject: [PATCH 020/336] rename maximal_cells_list to maximal_cells_sorted, and rename cells_list to cells_sorted --- src/sage/homology/polyhedral_complex.py | 34 ++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index c1c98164951..2a701849509 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -41,13 +41,13 @@ :meth:`~PolyhedralComplex.maximal_cells` | Return the dictionary of the maximal cells in this polyhedral complex. :meth:`~PolyhedralComplex.maximal_cell_iterator` | Return an iterator over maximal cells in this polyhedral complex. - :meth:`~PolyhedralComplex.maximal_cells_list` | Return the sorted list of all maximal cells in this polyhedral complex. + :meth:`~PolyhedralComplex.maximal_cells_sorted` | Return the sorted list of all maximal cells in this polyhedral complex. :meth:`~PolyhedralComplex.n_maximal_cells` | List the maximal cells of dimension `n` in this polyhedral complex. :meth:`~PolyhedralComplex._n_maximal_cells_sorted` | Return the sorted list of maximal cells of dim `n` in this complex. :meth:`~PolyhedralComplex.has_maximal_cell` | Return ``True`` if the given cell is a maximal cell in this complex. :meth:`~PolyhedralComplex.cells` | Return the dictionary of the cells in this polyhedral complex. :meth:`~PolyhedralComplex.cell_iterator` | Return an iterator over cells in this polyhedral complex. - :meth:`~PolyhedralComplex.cells_list` | Return the sorted list of all cells in this polyhedral complex. + :meth:`~PolyhedralComplex.cells_sorted` | Return the sorted list of all cells in this polyhedral complex. :meth:`~sage.homology.cell_complex.GenericCellComplex.n_cells` | List the cells of dimension `n` in this polyhedral complex. :meth:`~PolyhedralComplex._n_cells_sorted` | Return the sorted list of `n`-cells in this polyhedral complex. :meth:`~PolyhedralComplex.has_cell` | Return ``True`` if the given cell is in this polyhedral complex. @@ -155,7 +155,7 @@ class PolyhedralComplex(GenericCellComplex): sage: pc = PolyhedralComplex([ ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1/7, 2/7)]), ....: Polyhedron(vertices=[(1/7, 2/7), (0, 0), (0, 1/4)])]) - sage: [p.Vrepresentation() for p in pc.cells_list()] + sage: [p.Vrepresentation() for p in pc.cells_sorted()] [(A vertex at (0, 0), A vertex at (0, 1/4), A vertex at (1/7, 2/7)), (A vertex at (0, 0), A vertex at (1/3, 1/3), A vertex at (1/7, 2/7)), (A vertex at (0, 0), A vertex at (0, 1/4)), @@ -263,7 +263,7 @@ def cells_list_to_cells_dict(cells_list): # initialize the attributes self._is_convex = None self._polyhedron = None - self._maximal_cells_list = None # needed for hash + self._maximal_cells_sorted = None # needed for hash self._cells = None self._face_poset = None @@ -398,7 +398,7 @@ def _n_cells_sorted(self, n, subcomplex=None): return sorted(n_cells, key=lambda p: (p.vertices(), p.rays(), p.lines())) - def cells_list(self, subcomplex=None): + def cells_sorted(self, subcomplex=None): """ The sorted list of the cells of this polyhedral complex in non-increasing dimensions. @@ -414,9 +414,9 @@ def cells_list(self, subcomplex=None): sage: pc = PolyhedralComplex([ ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) - sage: len(pc.cells_list()) + sage: len(pc.cells_sorted()) 11 - sage: pc.cells_list()[0].Vrepresentation() + sage: pc.cells_sorted()[0].Vrepresentation() (A vertex at (0, 0), A vertex at (0, 2), A vertex at (1, 2)) """ cells = [] @@ -564,25 +564,25 @@ def _n_maximal_cells_sorted(self, n): return sorted(n_maximal_cells, key=lambda p: (p.vertices(), p.rays(), p.lines())) - def maximal_cells_list(self): + def maximal_cells_sorted(self): """ - The sorted list of the maximal cells of this polyhedral complex - in non-increasing dimensions. + Return the sorted list of the maximal cells of this polyhedral complex + by non-increasing dimensions. EXAMPLES:: sage: pc = PolyhedralComplex([ ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) - sage: [p.vertices_list() for p in pc.maximal_cells_list()] + sage: [p.vertices_list() for p in pc.maximal_cells_sorted()] [[[0, 0], [0, 2], [1, 2]], [[0, 0], [1, 1], [1, 2]]] """ - if self._maximal_cells_list is None: + if self._maximal_cells_sorted is None: maximal_cells = [] for n in range(self._dim, -1, -1): maximal_cells += self._n_maximal_cells_sorted(n) - self._maximal_cells_list = maximal_cells - return self._maximal_cells_list + self._maximal_cells_sorted = maximal_cells + return self._maximal_cells_sorted def has_maximal_cell(self, c): """ @@ -732,7 +732,7 @@ def is_full_dimensional(self): def __hash__(self): """ - Compute the hash value of ``self`` using its ``maximal_cells_list``. + Compute the hash value of ``self`` using its ``maximal_cells_sorted``. EXAMPLES:: @@ -748,7 +748,7 @@ def __hash__(self): if not self._is_immutable: raise ValueError("This polyhedral complex must be immutable" + "Call set_immutable().") - return hash(tuple(self.maximal_cells_list())) + return hash(tuple(self.maximal_cells_sorted())) def __eq__(self, right): """ @@ -766,7 +766,7 @@ def __eq__(self, right): True """ return isinstance(right, PolyhedralComplex) and ( - self.maximal_cells_list() == right.maximal_cells_list()) + self.maximal_cells_sorted() == right.maximal_cells_sorted()) def __ne__(self, right): """ From 0bd40423ef06c9c62dc227eb0f756a0d0290e535 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sat, 1 May 2021 02:40:28 -0400 Subject: [PATCH 021/336] is_immutable default is False, set_immutable() --- src/sage/homology/polyhedral_complex.py | 153 +++++++++++++++++++++--- 1 file changed, 135 insertions(+), 18 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 2a701849509..f93541eace0 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -69,6 +69,8 @@ :meth:`~PolyhedralComplex.is_connected` | Return ``True`` if the polyhedral complex is connected. :meth:`~PolyhedralComplex.is_subcomplex` | Return ``True`` if this complex is a subcomplex of the other. :meth:`~PolyhedralComplex.is_convex` | Return ``True`` if the polyhedral complex is convex. + :meth:`~PolyhedralComplex.is_mutable` | Return ``True`` if the polyhedral complex is mutable. + :meth:`~PolyhedralComplex.is_immutable` | Return ``True`` if the polyhedral complex is not mutable. **New polyhedral complexes from old ones** @@ -86,6 +88,15 @@ :meth:`~PolyhedralComplex.disjoint_union` | Return the disjoint union of this polyhedral complex with another one. :meth:`~PolyhedralComplex.join` | Return the join of this polyhedral complex with another one. +**Update polyhedral complexe** + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :meth:`~PolyhedralComplex.set_immutable` | Make this polyhedral complex immutable. + **Miscellaneous** .. csv-table:: @@ -282,9 +293,9 @@ def cells_list_to_cells_dict(cells_list): if not (r.is_empty() or (r in poset) and poset.is_gequal(p, r) and poset.is_gequal(q, r)): raise ValueError("The given cells are not face-to-face") - # For now, a polyhedral complex is immutable - # TODO: is_mutable and is_immutable parameters and set_immutable method. - self._is_immutable = True + self._is_immutable = False + if not is_mutable or is_immutable: + self.set_immutable() def cells(self, subcomplex=None): """ @@ -738,12 +749,17 @@ def __hash__(self): sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]) sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]) - sage: pc1 = PolyhedralComplex([p1, p2]) + sage: pc1 = PolyhedralComplex([p1, p2], is_mutable=False) sage: hash(pc1) == hash(pc1) True - sage: pc2 = PolyhedralComplex([p2, p1]) + sage: pc2 = PolyhedralComplex([p2, p1], is_mutable=False) sage: hash(pc1) == hash(pc2) True + sage: pc3 = PolyhedralComplex([p1, p2]) + sage: hash(pc3) + Traceback (most recent call last): + ... + ValueError: This polyhedral complex must be immutableCall set_immutable(). """ if not self._is_immutable: raise ValueError("This polyhedral complex must be immutable" + @@ -785,13 +801,13 @@ def __ne__(self, right): def __copy__(self): """ - Return a copy of ``self``. + Return a mutable copy of ``self``. EXAMPLES:: - sage: p1 = Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]) - sage: p2 = copy(p1) - sage: p1 == p2 + sage: pc1 = PolyhedralComplex([Polyhedron(vertices=[(0, 0)])]) + sage: pc2 = copy(pc1) + sage: pc1 == pc2 True """ return PolyhedralComplex(self._maximal_cells, maximality_check=False) @@ -1110,7 +1126,8 @@ def connected_component(self, cell=None): faces = g.connected_component_containing_vertex(cell) facets = [f for f in self.maximal_cell_iterator() if f in faces] - return PolyhedralComplex(facets, maximality_check=False) + return PolyhedralComplex(facets, maximality_check=False, + is_immutable=self._is_immutable) def connected_components(self): """ @@ -1163,7 +1180,8 @@ def connected_components(self): lists_of_facets = [ [f for f in self.maximal_cell_iterator() if f in faces] for faces in lists_of_faces] - results = [PolyhedralComplex(facets, maximality_check=False) + results = [PolyhedralComplex(facets, maximality_check=False, + is_immutable=self._is_immutable) for facets in lists_of_facets] return results @@ -1196,7 +1214,8 @@ def n_skeleton(self, n): return copy(self) facets = [f for f in self.maximal_cell_iterator() if f.dimension() < n] facets.extend(self.n_cells(n)) - return PolyhedralComplex(facets, maximality_check=False) + return PolyhedralComplex(facets, maximality_check=False, + is_immutable=self._is_immutable) def stratify(self, n): """ @@ -1231,7 +1250,8 @@ def stratify(self, n): Polyhedral complex with 1 maximal cells """ n_faces = self.n_maximal_cells(n) - return PolyhedralComplex(n_faces, maximality_check=False) + return PolyhedralComplex(n_faces, maximality_check=False, + is_immutable=self._is_immutable) def boundary_subcomplex(self): """ @@ -1288,9 +1308,13 @@ def boundary_subcomplex(self): False """ if self.is_full_dimensional(): - return PolyhedralComplex(self.relative_boundary_cells()) + return PolyhedralComplex(self.relative_boundary_cells(), + is_immutable=self._is_immutable) else: - return copy(self) + ans = copy(self) + if self._is_immutable: + ans.set_immutable() + return ans def relative_boundary_cells(self): r""" @@ -1537,7 +1561,9 @@ def product(self, right): """ maximal_cells = [f.product(g) for f in self.maximal_cell_iterator() for g in right.maximal_cell_iterator()] - return PolyhedralComplex(maximal_cells, maximality_check=False) + return PolyhedralComplex(maximal_cells, maximality_check=False, + is_immutable=(self._is_immutable and + right._is_immutable)) def disjoint_union(self, right): """ @@ -1559,7 +1585,9 @@ def disjoint_union(self, right): maximal_cells = list(self.maximal_cell_iterator()) + list( right.maximal_cell_iterator()) return PolyhedralComplex(maximal_cells, maximality_check=True, - face_to_face_check=True) + face_to_face_check=True, + is_immutable=(self._is_immutable and + right._is_immutable)) def join(self, right): """ @@ -1581,7 +1609,9 @@ def join(self, right): """ maximal_cells = [f.join(g) for f in self.maximal_cell_iterator() for g in right.maximal_cell_iterator()] - return PolyhedralComplex(maximal_cells, maximality_check=False) + return PolyhedralComplex(maximal_cells, maximality_check=False, + is_immutable=(self._is_immutable and + right._is_immutable)) ############################################################ # abstract methods not implemented in generic cell complexe @@ -1605,6 +1635,93 @@ def alexander_whitney(self, cell, dim_left): # end of chain complexes, homology ############################################################ + # this function overrides the standard one for GenericCellComplex, + # this one counts the number of maximal cells, not all cells, to + # avoid calling and computing self.cells() + def _repr_(self): + """ + Print representation. + + .. WARNING:: + + This may give the wrong answer if the polyhedral complex + was constructed with ``maximality_check`` set to ``False``. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) + sage: PolyhedralComplex([p1, p2, p3]) + Polyhedral complex with 2 maximal cells + + Wrong answer due to ``maximality_check=False``:: + + sage: PolyhedralComplex([p1, p2, p3], maximality_check=False) + Polyhedral complex with 3 maximal cells + """ + num = len(list(self.maximal_cell_iterator())) + return "Polyhedral complex with %s maximal cells" % num + + def set_immutable(self): + """ + Make this polyhedral complex immutable. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) + sage: pc.is_mutable() + True + sage: pc.set_immutable() + sage: pc.is_mutable() + False + """ + self._is_immutable = True + + def is_mutable(self): + """ + Return ``True`` if mutable. + + EXAMPLES:: + + sage: pc1 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) + sage: pc1.is_mutable() + True + sage: pc2 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])], + ....: is_mutable=False) + sage: pc2.is_mutable() + False + sage: pc1 == pc2 + True + sage: pc3 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])], + ....: is_immutable=True) + sage: pc3.is_mutable() + False + sage: pc2 == pc3 + True + """ + return not self._is_immutable + + def is_immutable(self): + """ + Return ``True`` if immutable. + + EXAMPLES:: + + sage: pc1 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) + sage: pc1.is_immutable() + False + sage: pc2 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])], + ....: is_mutable=False) + sage: pc2.is_immutable() + True + sage: pc3 = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])], + ....: is_immutable=True) + sage: pc3.is_immutable() + True + """ + return self._is_immutable + # TODO: mutable complex: add and update stuff incrementally # TODO: replace one cell by its triangulation and adapt other cells # TODO: graph of maximal cells by wall-crossing # use poset.meet instead From ff216bd78bd66001f72c6a0fc97cd35cb996cc3a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 19 Mar 2021 12:16:43 -0700 Subject: [PATCH 022/336] WIP --- build/pkgs/deformation/SPKG.rst | 2 ++ build/pkgs/deformation/checksums.ini | 1 + build/pkgs/deformation/package-version.txt | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build/pkgs/deformation/SPKG.rst b/build/pkgs/deformation/SPKG.rst index 2f9a2675dae..732b8a49e5d 100644 --- a/build/pkgs/deformation/SPKG.rst +++ b/build/pkgs/deformation/SPKG.rst @@ -17,3 +17,5 @@ Upstream Contact ---------------- - Sebastian Pancratz: sebastian.pancratz@gmail.com + +- We use the fork at https://github.com/jpflori/deformation diff --git a/build/pkgs/deformation/checksums.ini b/build/pkgs/deformation/checksums.ini index 993b9e6ff2e..5d7d8524d42 100644 --- a/build/pkgs/deformation/checksums.ini +++ b/build/pkgs/deformation/checksums.ini @@ -2,3 +2,4 @@ tarball=deformation-VERSION.tar.bz2 sha1=317fb76c884fa4b6b92ed0b171a0b9fdb3bdc90f md5=e4af9b93ddc85ebb52d9fa1fedd75887 cksum=4134074975 +upstream_url=https://github.com/jpflori/deformation/archive/refs/heads/VERSION.zip diff --git a/build/pkgs/deformation/package-version.txt b/build/pkgs/deformation/package-version.txt index 036881eb8b0..6532c4b028a 100644 --- a/build/pkgs/deformation/package-version.txt +++ b/build/pkgs/deformation/package-version.txt @@ -1 +1 @@ -d05941b.p0 +cf2d82c22c07b13e584e37720db4cec2024de14f From 197696c447d4b39385ca3c670d7a543f604cdc09 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 26 Apr 2021 14:23:00 +0100 Subject: [PATCH 023/336] updated to the latest fix, using GMP --- build/pkgs/deformation/checksums.ini | 8 ++++---- build/pkgs/deformation/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/deformation/checksums.ini b/build/pkgs/deformation/checksums.ini index 5d7d8524d42..38777b3b31f 100644 --- a/build/pkgs/deformation/checksums.ini +++ b/build/pkgs/deformation/checksums.ini @@ -1,5 +1,5 @@ tarball=deformation-VERSION.tar.bz2 -sha1=317fb76c884fa4b6b92ed0b171a0b9fdb3bdc90f -md5=e4af9b93ddc85ebb52d9fa1fedd75887 -cksum=4134074975 -upstream_url=https://github.com/jpflori/deformation/archive/refs/heads/VERSION.zip +sha1=b87e51439e652829b3e488a0ae51417edc026ddc +md5=88a0922bc1340c31afdece094ab5c392 +cksum=109346983 +upstream_url=https://github.com/jpflori/deformation/archive/refs/tags/VERSION.tar.gz diff --git a/build/pkgs/deformation/package-version.txt b/build/pkgs/deformation/package-version.txt index 6532c4b028a..2982d08bc62 100644 --- a/build/pkgs/deformation/package-version.txt +++ b/build/pkgs/deformation/package-version.txt @@ -1 +1 @@ -cf2d82c22c07b13e584e37720db4cec2024de14f +mpir_fix From 4822d1cf8029fa5de0452ceeb61ac487fae25659 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 3 May 2021 11:47:20 +0100 Subject: [PATCH 024/336] update to 20210503, new location for releases --- build/pkgs/deformation/checksums.ini | 2 +- build/pkgs/deformation/package-version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/deformation/checksums.ini b/build/pkgs/deformation/checksums.ini index 38777b3b31f..a1d4003b8b7 100644 --- a/build/pkgs/deformation/checksums.ini +++ b/build/pkgs/deformation/checksums.ini @@ -2,4 +2,4 @@ tarball=deformation-VERSION.tar.bz2 sha1=b87e51439e652829b3e488a0ae51417edc026ddc md5=88a0922bc1340c31afdece094ab5c392 cksum=109346983 -upstream_url=https://github.com/jpflori/deformation/archive/refs/tags/VERSION.tar.gz +upstream_url=https://github.com/sagemath/deformation/archive/refs/tags/VERSION.tar.gz diff --git a/build/pkgs/deformation/package-version.txt b/build/pkgs/deformation/package-version.txt index 2982d08bc62..891293d2df1 100644 --- a/build/pkgs/deformation/package-version.txt +++ b/build/pkgs/deformation/package-version.txt @@ -1 +1 @@ -mpir_fix +20210503 From b21456b039b6b6a90e66a6f0c828fcb9aa8e2430 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 3 May 2021 12:30:35 +0100 Subject: [PATCH 025/336] update checksum --- build/pkgs/deformation/checksums.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/deformation/checksums.ini b/build/pkgs/deformation/checksums.ini index a1d4003b8b7..a0996128077 100644 --- a/build/pkgs/deformation/checksums.ini +++ b/build/pkgs/deformation/checksums.ini @@ -1,5 +1,5 @@ tarball=deformation-VERSION.tar.bz2 -sha1=b87e51439e652829b3e488a0ae51417edc026ddc -md5=88a0922bc1340c31afdece094ab5c392 -cksum=109346983 +sha1=0f5fd78a91da207d06b5be59bf466f16c2614eda +md5=e2c365e20778117d402fb664fc145d72 +cksum=3789646827 upstream_url=https://github.com/sagemath/deformation/archive/refs/tags/VERSION.tar.gz From 44b1d0a533e9a2b5df3e6785b07464504daaa770 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 3 May 2021 12:35:24 +0100 Subject: [PATCH 026/336] update upstream info --- build/pkgs/deformation/SPKG.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/pkgs/deformation/SPKG.rst b/build/pkgs/deformation/SPKG.rst index 732b8a49e5d..71b821459e2 100644 --- a/build/pkgs/deformation/SPKG.rst +++ b/build/pkgs/deformation/SPKG.rst @@ -16,6 +16,7 @@ GLPv3 Upstream Contact ---------------- -- Sebastian Pancratz: sebastian.pancratz@gmail.com +- Sebastian Pancratz: sebastian.pancratz@gmail.com, sage-devel@googlegroups.com -- We use the fork at https://github.com/jpflori/deformation +- We use the fork at https://github.com/sagemath/deformation + the fork uses GMP instead of MPIR, and Flint 2.7+. From 3cc98f59d6b1e89b177c99d8b3bcb5662e0d4a04 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Tue, 4 May 2021 18:31:17 -0400 Subject: [PATCH 027/336] improve _repr_, factor out cells_list_to_cells_dict --- src/sage/homology/polyhedral_complex.py | 68 +++++++++++++++++-------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index f93541eace0..aa5a34b55dd 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -131,6 +131,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.graphs.graph import Graph +from sage.combinat.posets.posets import Poset class PolyhedralComplex(GenericCellComplex): @@ -227,7 +228,7 @@ class PolyhedralComplex(GenericCellComplex): ....: Polyhedron(vertices=[[2], [0]]) ]) Traceback (most recent call last): ... - ValueError: The given cells are not polyhedrain the same ambient space. + ValueError: The given cells are not polyhedra in the same ambient space. """ def __init__(self, maximal_cells=None, maximality_check=True, face_to_face_check=False, is_mutable=True, is_immutable=False): @@ -239,18 +240,8 @@ def __init__(self, maximal_cells=None, maximality_check=True, EXAMPLES:: sage: PolyhedralComplex([Polyhedron(vertices=[(1, 1), (0, 0)])]) - Polyhedral complex with 1 maximal cells + Polyhedral complex with 1 maximal cell """ - def cells_list_to_cells_dict(cells_list): - cells_dict = {} - for cell in cells_list: - d = cell.dimension() - if d in cells_dict: - cells_dict[d].add(cell) - else: - cells_dict[d] = set([cell]) - return cells_dict - if maximal_cells is None: cells_dict = {} elif isinstance(maximal_cells, (list, tuple)): @@ -269,7 +260,7 @@ def cells_list_to_cells_dict(cells_list): if not all((is_Polyhedron(cell) and cell.ambient_dim() == self._ambient_dim) for cell in self.maximal_cell_iterator()): - raise ValueError("The given cells are not polyhedra" + + raise ValueError("The given cells are not polyhedra " + "in the same ambient space.") # initialize the attributes self._is_convex = None @@ -341,7 +332,6 @@ def cells(self, subcomplex=None): if (k-1) not in cells: cells[k-1] = set([]) cells[k-1].add(p) - from sage.combinat.posets.posets import Poset self._face_poset = Poset(covers) self._cells = cells return self._cells @@ -623,7 +613,8 @@ def has_maximal_cell(self, c): True """ d = c.dimension() - return (c in self.n_maximal_cells(d)) + # return (c in self.n_maximal_cells(d)) # use set instead of list + return (d in self.maximal_cells()) and (c in self.maximal_cells()[d]) def has_cell(self, c): """ @@ -641,7 +632,7 @@ def has_cell(self, c): True """ d = c.dimension() - return (c in self.n_cells(d)) + return (d in self.cells()) and (c in self.cells()[d]) def dimension(self): """ @@ -1240,14 +1231,14 @@ def stratify(self, n): sage: pc.stratify(2) == pc True sage: pc.stratify(1) - Polyhedral complex with 0 maximal cells + Polyhedral complex with 0 maximal cell Wrong answer due to ``maximality_check=False``:: sage: pc_invalid = PolyhedralComplex([p1, p2, p3], ....: maximality_check=False) sage: pc_invalid.stratify(1) - Polyhedral complex with 1 maximal cells + Polyhedral complex with 1 maximal cell """ n_faces = self.n_maximal_cells(n) return PolyhedralComplex(n_faces, maximality_check=False, @@ -1552,7 +1543,7 @@ def product(self, right): sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) sage: pc_square = pc.product(pc) sage: pc_square - Polyhedral complex with 1 maximal cells + Polyhedral complex with 1 maximal cell sage: next(pc_square.maximal_cell_iterator()).vertices() (A vertex at (0, 0), A vertex at (0, 1), @@ -1600,7 +1591,7 @@ def join(self, right): sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) sage: pc_join = pc.join(pc) sage: pc_join - Polyhedral complex with 1 maximal cells + Polyhedral complex with 1 maximal cell sage: next(pc_join.maximal_cell_iterator()).vertices() (A vertex at (0, 0, 0), A vertex at (0, 0, 1), @@ -1661,7 +1652,10 @@ def _repr_(self): Polyhedral complex with 3 maximal cells """ num = len(list(self.maximal_cell_iterator())) - return "Polyhedral complex with %s maximal cells" % num + if num <= 1: + return "Polyhedral complex with %s maximal cell" % num + else: + return "Polyhedral complex with %s maximal cells" % num def set_immutable(self): """ @@ -1722,6 +1716,38 @@ def is_immutable(self): """ return self._is_immutable +############################################################ +# Helper functions +############################################################ + + +def cells_list_to_cells_dict(cells_list): + r""" + Helper function that returns the dictionary whose keys are the dimensions, + and the value associated to an integer `d` is the set of `d`-dimensional + polyhedra in the given list. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 1), (0, 0)]) + sage: p3 = Polyhedron(vertices=[(0, 0)]) + sage: p4 = Polyhedron(vertices=[(1, 1)]) + sage: sage.homology.polyhedral_complex.cells_list_to_cells_dict([p1, p2, p3, p4]) + {0: {A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex, + A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex}, + 1: {A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices}, + 2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}} + """ + cells_dict = {} + for cell in cells_list: + d = cell.dimension() + if d in cells_dict: + cells_dict[d].add(cell) + else: + cells_dict[d] = set([cell]) + return cells_dict + # TODO: mutable complex: add and update stuff incrementally # TODO: replace one cell by its triangulation and adapt other cells # TODO: graph of maximal cells by wall-crossing # use poset.meet instead From 18c1a33531445265b88c2b1bcd517165d16463d1 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Tue, 4 May 2021 18:32:26 -0400 Subject: [PATCH 028/336] def add_cell --- src/sage/homology/polyhedral_complex.py | 139 ++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index aa5a34b55dd..69bad8ca526 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -96,6 +96,8 @@ :delim: | :meth:`~PolyhedralComplex.set_immutable` | Make this polyhedral complex immutable. + :meth:`~PolyhedralComplex.add_cell` | Add a cell to this polyhedral complex. + :meth:`~PolyhedralComplex.remove_cell` | Remove a cell from this polyhedral complex. **Miscellaneous** @@ -1716,6 +1718,143 @@ def is_immutable(self): """ return self._is_immutable + def add_cell(self, cell): + """ + Add a cell to this polyhedral complex. + + :param cell: a polyhedron + + This *changes* the polyhedral complex, by adding a new cell and all + of its subfaces. + + EXAMPLES: + + If you add a cell which is already present, there is no effect:: + + sage: pc = PolyhedralComplex([Polyhedron(vertices=[(1, 2), (0, 2)])]) + sage: pc + Polyhedral complex with 1 maximal cell + sage: pc.add_cell(Polyhedron(vertices=[(1, 2)])) + sage: pc + Polyhedral complex with 1 maximal cell + sage: pc.dimension() + 1 + + Add a cell and check that dimension is correctly updated:: + + sage: pc.add_cell(Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])) + sage: pc.dimension() + 2 + sage: pc.maximal_cells() + {2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}} + sage: pc.is_convex() + True + + Add another cell and check that the properties are correctly updated:: + + sage: pc.add_cell(Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)])) + sage: pc + Polyhedral complex with 2 maximal cells + sage: len(pc._cells[1]) + 5 + sage: pc._face_poset + Finite poset containing 11 elements + sage: pc._is_convex + True + sage: pc._polyhedron.vertices_list() + [[0, 0], [0, 2], [1, 1], [1, 2]] + + Add a ray which makes the complex non convex:: + + sage: pc.add_cell(Polyhedron(rays=[(1, 0)])) + sage: pc + Polyhedral complex with 3 maximal cells + sage: len(pc._cells[1]) + 6 + sage: (pc._is_convex is False) and (pc._polyhedron is None) + True + + TESTS:: + + sage: pc.add_cell(Polyhedron(vertices=[[0]])) + Traceback (most recent call last): + ... + ValueError: The given cell is not a polyhedron in the same ambient space. + sage: pc.add_cell(Polyhedron(vertices=[(1, 1), (0, 0), (2, 0)])) + Traceback (most recent call last): + ... + ValueError: The cell is not face-to-face with complex + sage: pc.set_immutable() + sage: pc.add_cell(Polyhedron(vertices=[(-1, -1)])) + Traceback (most recent call last): + ... + ValueError: This polyhedral complex is not mutable + """ + if self._is_immutable: + raise ValueError("This polyhedral complex is not mutable") + if not is_Polyhedron(cell) or cell.ambient_dim() != self._ambient_dim: + raise ValueError("The given cell is not a polyhedron " + + "in the same ambient space.") + # if cell is already in self, do nothing. + if self.has_cell(cell): + return + # update cells and face poset + cells = self.cells() + covers = {p: self.face_poset().upper_covers(p) + for p in self.cell_iterator()} + d = cell.dimension() + d_cells = [cell] + if d not in cells: + cells[d] = set(d_cells) + else: + cells[d].add(cell) + covers[cell] = [] + while d > 0: + d = d - 1 + new_facets = [] + for c in d_cells: + for facet in c.facets(): + p = facet.as_polyhedron() + if d not in cells: + cells[d] = set([]) + if p not in cells[d]: + cells[d].add(p) + covers[p] = [c] + new_facets.append(p) + else: + covers[p].append(c) + d_cells = new_facets + self._face_poset = poset = Poset(covers) + self._cells = cells + # check face-to-face between cell and previous maximal cells + for p in self.maximal_cell_iterator(): + r = p.intersection(cell) + if not (r.is_empty() or (r in poset) and + poset.is_gequal(p, r) and poset.is_gequal(cell, r)): + raise ValueError("The cell is not face-to-face with complex") + # update dim and maximal cells + d = cell.dimension() + if d > self._dim: + self._dim = d + maximal_cells = poset.maximal_elements() # a list + self._maximal_cells = cells_list_to_cells_dict(maximal_cells) + # update convexity if self was known to be convex, reset otherwise. + if self._is_convex: + try: + new_complex = PolyhedralComplex([self._polyhedron, cell], + face_to_face_check=True) + except ValueError: + self._is_convex = False + self._polyhedron = None + else: + self._is_convex = new_complex.is_convex() + self._polyhedron = new_complex._polyhedron + else: + self._is_convex = None + self._polyhedron = None + # reset cached attribute + self._maximal_cells_sorted = None # needed for hash + ############################################################ # Helper functions ############################################################ From 45f080c775da363fa7bb729305a6094b3e32c9d9 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Wed, 5 May 2021 20:44:23 -0400 Subject: [PATCH 029/336] implement PolyhedralComplex.remove_cell() --- src/sage/homology/polyhedral_complex.py | 136 ++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 7 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 69bad8ca526..6f8da4cac32 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -1855,6 +1855,135 @@ def add_cell(self, cell): # reset cached attribute self._maximal_cells_sorted = None # needed for hash + def remove_cell(self, cell, check=False): + """ + Remove the given cell from this polyhedral complex. In addition, + it removes all the cells that contain the given cell as a subface. + + :param cell: a cell of the polyhedral complex + + :param check: boolean; optional, default ``False``. If + ``True``, raise an error if ``cell`` is not a + cell of this polyhedral complex + + This does not return anything; instead, it *changes* the + polyhedral complex. + + EXAMPLES: + + If you add a cell which is already present, there is no effect:: + + sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: r = Polyhedron(rays=[(1, 0)]) + sage: pc = PolyhedralComplex([p1, p2, r]) + sage: pc.dimension() + 2 + sage: pc.remove_cell(Polyhedron(vertices=[(0, 0), (1, 2)])) + sage: pc.dimension() + 1 + sage: pc + Polyhedral complex with 5 maximal cells + sage: pc.remove_cell(Polyhedron(vertices=[(1, 2)])) + sage: pc.dimension() + 1 + sage: pc + Polyhedral complex with 3 maximal cells + sage: pc.remove_cell(Polyhedron(vertices=[(0, 0)])) + sage: pc.dimension() + 0 + + TESTS: + + Check that ValueError and empty complex are treated properly:: + + sage: p = Polyhedron(vertices=[[1]]) + sage: pc = PolyhedralComplex([p]) + sage: pc.remove_cell(Polyhedron(vertices=[[0]]), check=True) + Traceback (most recent call last): + ... + ValueError: Trying to remove a cell which is not in the polyhedral complex + sage: pc.remove_cell(Polyhedron(vertices=[(1, 1)])) + Traceback (most recent call last): + ... + ValueError: The given cell is not a polyhedron in the same ambient space. + sage: pc.remove_cell(p) + sage: pc.dimension() + -1 + sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0]])], is_mutable=False) + sage: pc.remove_cell(Polyhedron(vertices=[[0]])) + Traceback (most recent call last): + ... + ValueError: This polyhedral complex is not mutable + + Check that this function is coherent with + :meth:`~sage.homology.simplicial_complex.SimplicialComplex.remove_face`:: + + sage: v1 = (1, 0, 0, 0); v2 = (0, 1, 0, 0); v3 = (0, 0, 1, 0); v4 = (0, 0, 0, 1) + sage: Z = PolyhedralComplex([Polyhedron(vertices=[v1, v2, v3, v4])]); Z + Polyhedral complex with 1 maximal cell + sage: Z.remove_cell(Polyhedron(vertices=[v1, v2])) + sage: Z + Polyhedral complex with 2 maximal cells + sage: [c.vertices_list() for c in Z.maximal_cells_sorted()] + [[[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], + [[0, 0, 0, 1], [0, 0, 1, 0], [1, 0, 0, 0]]] + + sage: v0 = (0, 0, 0, 0) + sage: S = PolyhedralComplex([Polyhedron(vertices=[v0, v1, v2]), Polyhedron(vertices=[v2, v3])]) + sage: S.maximal_cells() + {1: {A 1-dimensional polyhedron in ZZ^4 defined as the convex hull of 2 vertices}, + 2: {A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices}} + sage: S.remove_cell(Polyhedron(vertices=[v0, v1, v2])) + sage: S + Polyhedral complex with 4 maximal cells + sage: [c.vertices_list() for c in S.maximal_cells_sorted()] + [[[0, 0, 0, 0], [0, 1, 0, 0]], + [[0, 0, 0, 0], [1, 0, 0, 0]], + [[0, 0, 1, 0], [0, 1, 0, 0]], + [[0, 1, 0, 0], [1, 0, 0, 0]]] + + sage: T = PolyhedralComplex([Polyhedron(vertices=[[1], [2]]), Polyhedron(vertices=[[1], [-3]])]) + sage: T.remove_cell(Polyhedron(vertices=[[-3], [1]])) + sage: [c.vertices_list() for c in T.maximal_cells_sorted()] + [[[1], [2]], [[-3]]] + sage: [c.vertices_list() for c in T.cells_sorted()] + [[[1], [2]], [[-3]], [[1]], [[2]]] + """ + if self._is_immutable: + raise ValueError("This polyhedral complex is not mutable") + if not is_Polyhedron(cell) or cell.ambient_dim() != self._ambient_dim: + raise ValueError("The given cell is not a polyhedron " + + "in the same ambient space.") + # if cell is not in self, delete nothing. + if not self.has_cell(cell): # self.cells() is called + if check: + raise ValueError("Trying to remove a cell which is not " + + "in the polyhedral complex") + return + # update cells and face poset + poset = self._face_poset + deleting = poset.order_filter([cell]) + for c in deleting: + d = c.dimension() + self._cells[d].remove(c) + if not self._cells[d]: + del self._cells[d] + covers = {p: [q for q in poset.upper_covers(p) if q not in deleting] + for p in self.cell_iterator()} + self._face_poset = Poset(covers) + # update dim and maximal cells + maximal_cells = self._face_poset.maximal_elements() # a list + self._maximal_cells = cells_list_to_cells_dict(maximal_cells) + if not maximal_cells: + self._dim = -1 + else: + self._dim = max(self._maximal_cells.keys()) + # reset cached attributes + self._maximal_cells_sorted = None # needed for hash + self._is_convex = None + self._polyhedron = None + ############################################################ # Helper functions ############################################################ @@ -1886,10 +2015,3 @@ def cells_list_to_cells_dict(cells_list): else: cells_dict[d] = set([cell]) return cells_dict - -# TODO: mutable complex: add and update stuff incrementally -# TODO: replace one cell by its triangulation and adapt other cells -# TODO: graph of maximal cells by wall-crossing # use poset.meet instead -# TODO: SimplicialComplex to PolyhedralComplex: geometric realization -# TODO: learn about the boundary stuff of chain complex -# TODO: Polyhedral Arrangement to PolyhedralComplex using #25122 From 33be3ab58e1e72eeb0cbc6eb66584411e30620dc Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 9 May 2021 12:25:30 -0400 Subject: [PATCH 030/336] implement subdivide, is_simplicial_complex, is_polyhedral_fan, is_simplicial_fan; allow 3d plot --- src/sage/homology/polyhedral_complex.py | 247 +++++++++++++++++++++++- 1 file changed, 244 insertions(+), 3 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 6f8da4cac32..3ae448b31dd 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -27,7 +27,7 @@ AUTHORS: -- Yuan Zhou (2021-04): initial implementation +- Yuan Zhou (2021-05): initial implementation List of PolyhedralComplex methods --------------------------------- @@ -71,6 +71,9 @@ :meth:`~PolyhedralComplex.is_convex` | Return ``True`` if the polyhedral complex is convex. :meth:`~PolyhedralComplex.is_mutable` | Return ``True`` if the polyhedral complex is mutable. :meth:`~PolyhedralComplex.is_immutable` | Return ``True`` if the polyhedral complex is not mutable. + :meth:`~PolyhedralComplex.is_simplicial_complex` | Return ``True`` if the polyhedral complex is a simplicial complex. + :meth:`~PolyhedralComplex.is_polyhedral_fan` | Return ``True`` if the polyhedral complex is a fan. + :meth:`~PolyhedralComplex.is_simplicial_fan` | Return ``True`` if the polyhedral complex is a simplicial fan. **New polyhedral complexes from old ones** @@ -87,6 +90,7 @@ :meth:`~PolyhedralComplex.product` | Return the (Cartesian) product of this polyhedral complex with another one. :meth:`~PolyhedralComplex.disjoint_union` | Return the disjoint union of this polyhedral complex with another one. :meth:`~PolyhedralComplex.join` | Return the join of this polyhedral complex with another one. + :meth:`~PolyhedralComplex.subdivide` | Return a new polyhedral complex (with option ``make_simplicial``) subdividing this one. **Update polyhedral complexe** @@ -134,6 +138,7 @@ from sage.rings.rational_field import QQ from sage.graphs.graph import Graph from sage.combinat.posets.posets import Poset +from sage.misc.misc import powerset class PolyhedralComplex(GenericCellComplex): @@ -672,7 +677,7 @@ def ambient_dimension(self): def plot(self, **kwds): """ - Return a plot of the polyhedral complex, if it is of dim at most 2. + Return a plot of the polyhedral complex, if it is of dim at most 3. EXAMPLES:: @@ -682,7 +687,7 @@ def plot(self, **kwds): sage: pc.plot() Graphics object consisting of 10 graphics primitives """ - if self.dimension() > 2: + if self.dimension() > 3: raise ValueError("Cannot plot in high dimension") return sum(cell.plot(**kwds) for cell in self.maximal_cell_iterator()) @@ -1984,6 +1989,242 @@ def remove_cell(self, cell, check=False): self._is_convex = None self._polyhedron = None + def is_simplicial_complex(self): + """ + Test if this polyhedral complex is a simplicial complex. + + A polyhedral complex is **simplicial** if all of its (maximal) cells + are simplices, i.e., every cell is a bounded polytope with `d+1` + vertices, where `d` is the dimension of the polytope. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(0, 0), (1, 1), (1, 2)]) + sage: p2 = Polyhedron(rays=[(1, 0)]) + sage: PolyhedralComplex([p1]).is_simplicial_complex() + True + sage: PolyhedralComplex([p2]).is_simplicial_complex() + False + """ + return all(p.is_simplex() for p in self.maximal_cell_iterator()) + + def is_polyhedral_fan(self): + """ + Test if this polyhedral complex is a polyhedral fan. + + A polyhedral complex is a **fan** if all of its (maximal) cells + are cones. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(0, 0), (1, 1), (1, 2)]) + sage: p2 = Polyhedron(rays=[(1, 0)]) + sage: PolyhedralComplex([p1]).is_polyhedral_fan() + False + sage: PolyhedralComplex([p2]).is_polyhedral_fan() + True + sage: halfplane = Polyhedron(rays=[(1, 0), (-1, 0), (0, 1)]) + sage: PolyhedralComplex([halfplane]).is_polyhedral_fan() + True + """ + return all((p.n_vertices() == 1) and ( + vector(p.vertices_list()[0]) == p.ambient_space().zero()) + for p in self.maximal_cell_iterator()) + + def is_simplicial_fan(self): + """ + Test if this polyhedral complex is a simplicial fan. + + A polyhedral complex is a **simplicial fan** if all of its (maximal) + cells are simplical cones, i.e., every cell is a pointed cone (with + vertex being the origin) generated by `d` linearly independent rays, + where `d` is the dimension of the cone. + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(0, 0), (1, 1), (1, 2)]) + sage: p2 = Polyhedron(rays=[(1, 0)]) + sage: PolyhedralComplex([p1]).is_simplicial_fan() + False + sage: PolyhedralComplex([p2]).is_simplicial_fan() + True + sage: halfplane = Polyhedron(rays=[(1, 0), (-1, 0), (0, 1)]) + sage: PolyhedralComplex([halfplane]).is_simplicial_fan() + False + """ + return self.is_polyhedral_fan() and all( + (p.n_lines() == 0 and p.n_rays() == p.dimension()) + for p in self.maximal_cell_iterator()) + + def subdivide(self, make_simplicial=False, + new_vertices=None, new_rays=None): + """ + Construct a new polyhedral complex by iterative stellar subdivision of + ``self`` for each new vertex/ray given. + + Currently, subdivision is only supported for bounded polyhedral complex + or polyhedral fan. + + :param make_simplicial: boolean; optional, default ``False``. + If ``True``, the returned polyhedral complex is simplicial. + + :param new_vertices, new_rays: list; optional, default ``None``. + New generators to be added during subdivision. + + EXAMPLES:: + + sage: square_vertices = [(1, 1, 1), (-1, 1, 1), (-1, -1, 1), (1, -1, 1)] + sage: pc = PolyhedralComplex([ + ....: Polyhedron(vertices=[(0, 0, 0)] + square_vertices), + ....: Polyhedron(vertices=[(0, 0, 2)] + square_vertices)]) + sage: pc.is_compact() and not pc.is_simplicial_complex() + True + sage: subdivided_pc = pc.subdivide(new_vertices=[(0, 0, 1)]) + sage: subdivided_pc + Polyhedral complex with 8 maximal cells + sage: subdivided_pc.is_simplicial_complex() + True + sage: simplicial_pc = pc.subdivide(make_simplicial=True) + sage: simplicial_pc + Polyhedral complex with 4 maximal cells + sage: simplicial_pc.is_simplicial_complex() + True + + sage: fan = PolyhedralComplex([Polyhedron(rays=square_vertices)]) + sage: fan.is_polyhedral_fan() and not fan.is_simplicial_fan() + True + sage: fan.subdivide(new_vertices=[(0, 0, 1)]) + Traceback (most recent call last): + ... + ValueError: new vertices cannot be used for subdivision. + sage: subdivided_fan = fan.subdivide(new_rays=[(0, 0, 1)]) + sage: subdivided_fan + Polyhedral complex with 4 maximal cells + sage: subdivided_fan.is_simplicial_fan() + True + sage: simplicial_fan = fan.subdivide(make_simplicial=True) + sage: simplicial_fan + Polyhedral complex with 2 maximal cells + sage: simplicial_fan.is_simplicial_fan() + True + + sage: halfspace = PolyhedralComplex([Polyhedron(rays=[(0, 0, 1)], + ....: lines=[(1, 0, 0), (0, 1, 0)])]) + sage: halfspace.is_simplicial_fan() + False + sage: subdiv_halfspace = halfspace.subdivide(make_simplicial=True) + sage: subdiv_halfspace + Polyhedral complex with 4 maximal cells + sage: subdiv_halfspace.is_simplicial_fan() + True + """ + if self.is_compact(): + if new_rays: + raise ValueError("rays/lines cannot be used for subdivision.") + # bounded version of `fan.subdivide`; not require rational. + vertices = set([]) + if make_simplicial and not self.is_simplicial_complex(): + for p in self.maximal_cell_iterator(): + for v in p.vertices_list(): + vertices.add(tuple(v)) + if new_vertices: + for v in new_vertices: + vertices.add(tuple(v)) + if not vertices: + return self # Nothing has to be done + # bounded version of `fan._subdivide_stellar`; not require rational. + cells = list(self.maximal_cell_iterator()) + for v in vertices: + new = [] + for cell in cells: + if v in cell: + for cell_facet in cell.facets(): + facet = cell_facet.as_polyhedron() + if v in facet: + continue + p = facet.convex_hull(Polyhedron(vertices=[v])) + new.append(p) + else: + new.append(cell) + cells = new + return PolyhedralComplex(cells, maximality_check=False) + elif self.is_polyhedral_fan(): + if new_vertices and any(vi != 0 for v in new_vertices for vi in v): + raise ValueError("new vertices cannot be used for subdivision.") + # mimic :meth:`~sage.geometry.fan .subdivide` + # but here we allow for non-pointed cones, and we subdivide them. + rays_normalized = set([]) + self_rays = [] + cones = [] + for p in self.maximal_cell_iterator(): + prays = p.rays_list() + for r in prays: + r_n = vector(r).normalized() + r_n.set_immutable() + if r_n not in rays_normalized: + rays_normalized.add(r_n) + self_rays.append(vector(r)) + plines = p.lines_list() + if not plines: + cones.append(p) + continue + # consider a line as two rays + for pl in plines: + l_plus = vector(pl).normalized() + l_plus.set_immutable() + if l_plus not in rays_normalized: + rays_normalized.add(l_plus) + self_rays.append(vector(pl)) + l_minus = (-vector(pl)).normalized() + l_minus.set_immutable() + if l_minus not in rays_normalized: + rays_normalized.add(l_minus) + self_rays.append(-vector(pl)) + # subdivide the non-pointed p into pointed cones + # we rely on the canonical V-repr of Sage polyhedra. + num_lines = len(plines) + for neg_rays in powerset(range(num_lines)): + lines = [vector(plines[i]) if i not in neg_rays + else -vector(plines[i]) for i in range(num_lines)] + cones.append(Polyhedron(rays=(prays + lines))) + rays = [] + if new_rays: + for r in new_rays: + if vector(r).is_zero(): + raise ValueError("zero cannot be used for subdivision.") + r_n = vector(r).normalized() + r_n.set_immutable() + if r_n not in rays_normalized: + rays_normalized.add(r_n) + rays.append(vector(r)) + if make_simplicial and not self.is_simplicial_fan(): + rays = self_rays + rays + if not rays: + return self # Nothing has to be done + # mimic :class:`RationalPolyhedralFan`._subdivide_stellar(rays) + # start with self maximal cells (subdivided into pointed cones) + for ray in rays: + new = [] + for cone in cones: + if ray in cone: + for cone_facet in cone.facets(): + facet = cone_facet.as_polyhedron() + if ray in facet: + continue + new_cone = facet.convex_hull(Polyhedron(rays=[ray])) + new.append(new_cone) + else: + new.append(cone) + cones = new + return PolyhedralComplex(cones, maximality_check=False) + else: + # TODO: `self`` is unbounded, make it projectively simplicial. + # (1) homogenize self of dim d to fan in space of dim d+1; + # (2) call fan.subdivide(make_simplicial=True); + # (3) take section back to the space of dim d. + raise NotImplementedError('subdivision of a non-compact polyhedral ' + + 'complex that is not a fan is not supported') + ############################################################ # Helper functions ############################################################ From dfe5cb8e57280ad49f1c4febf898dbbdceba0c4e Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 9 May 2021 13:33:20 -0400 Subject: [PATCH 031/336] make backend optional argument to PolyhedralComplex --- src/sage/homology/polyhedral_complex.py | 81 ++++++++++++++++++++----- 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 3ae448b31dd..4cf78f23d9f 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -166,9 +166,23 @@ class PolyhedralComplex(GenericCellComplex): are face-to-face, and it raises a ValueError if they are not. - ``is_mutable`` and ``is_immutable`` -- boolean; default ``True`` and - ``False``, respectively; Set ``is_mutable=False`` or ``is_immutable=True`` + ``False``, respectively. Set ``is_mutable=False`` or ``is_immutable=True`` to make this polyhedral complex immutable. + - ``backend`` -- string; default ``None``. The name of the backend used for + computations on Sage polyhedra. If it is ``None``, then each cell has its + own backend. Otherwise, all cells will use the given backend from: + + * ``ppl`` the Parma Polyhedra Library + + * ``cdd`` CDD + + * ``normaliz`` normaliz + + * ``polymake`` polymake + + * ``field`` a generic Sage implementation + EXAMPLES:: sage: pc = PolyhedralComplex([ @@ -236,8 +250,18 @@ class PolyhedralComplex(GenericCellComplex): Traceback (most recent call last): ... ValueError: The given cells are not polyhedra in the same ambient space. + + Check that backend is passed to all the cells:: + + sage: P = Polyhedron(vertices=[(0, 0), (1, 1)]) + sage: P.backend() + 'ppl' + sage: pc = PolyhedralComplex([P], backend='cdd') + sage: Q = pc.maximal_cells_sorted()[0] + sage: Q.backend() + 'cdd' """ - def __init__(self, maximal_cells=None, maximality_check=True, + def __init__(self, maximal_cells=None, backend=None, maximality_check=True, face_to_face_check=False, is_mutable=True, is_immutable=False): r""" Define a PolyhedralComplex. @@ -249,12 +273,22 @@ def __init__(self, maximal_cells=None, maximality_check=True, sage: PolyhedralComplex([Polyhedron(vertices=[(1, 1), (0, 0)])]) Polyhedral complex with 1 maximal cell """ + self._backend = backend if maximal_cells is None: cells_dict = {} elif isinstance(maximal_cells, (list, tuple)): + if backend: + maximal_cells = [p.base_extend(p.base_ring(), backend) + for p in maximal_cells] cells_dict = cells_list_to_cells_dict(maximal_cells) elif isinstance(maximal_cells, dict): - cells_dict = {k: set(l) for (k, l) in maximal_cells.items()} + cells_dict = {} + for (k, l) in maximal_cells.items(): + if backend: + cells_dict[k] = set([p.base_extend(p.base_ring(), backend) + for p in l]) + else: + cells_dict[k] = set(l) else: raise ValueError if not cells_dict: @@ -808,7 +842,8 @@ def __copy__(self): sage: pc1 == pc2 True """ - return PolyhedralComplex(self._maximal_cells, maximality_check=False) + return PolyhedralComplex(self._maximal_cells, maximality_check=False, + backend=self._backend) def _an_element_(self): """ @@ -1125,7 +1160,8 @@ def connected_component(self, cell=None): facets = [f for f in self.maximal_cell_iterator() if f in faces] return PolyhedralComplex(facets, maximality_check=False, - is_immutable=self._is_immutable) + is_immutable=self._is_immutable, + backend=self._backend) def connected_components(self): """ @@ -1179,7 +1215,8 @@ def connected_components(self): [f for f in self.maximal_cell_iterator() if f in faces] for faces in lists_of_faces] results = [PolyhedralComplex(facets, maximality_check=False, - is_immutable=self._is_immutable) + is_immutable=self._is_immutable, + backend=self._backend) for facets in lists_of_facets] return results @@ -1213,7 +1250,8 @@ def n_skeleton(self, n): facets = [f for f in self.maximal_cell_iterator() if f.dimension() < n] facets.extend(self.n_cells(n)) return PolyhedralComplex(facets, maximality_check=False, - is_immutable=self._is_immutable) + is_immutable=self._is_immutable, + backend=self._backend) def stratify(self, n): """ @@ -1249,7 +1287,8 @@ def stratify(self, n): """ n_faces = self.n_maximal_cells(n) return PolyhedralComplex(n_faces, maximality_check=False, - is_immutable=self._is_immutable) + is_immutable=self._is_immutable, + backend=self._backend) def boundary_subcomplex(self): """ @@ -1307,7 +1346,8 @@ def boundary_subcomplex(self): """ if self.is_full_dimensional(): return PolyhedralComplex(self.relative_boundary_cells(), - is_immutable=self._is_immutable) + is_immutable=self._is_immutable, + backend=self._backend) else: ans = copy(self) if self._is_immutable: @@ -1512,7 +1552,8 @@ def is_convex(self): return False # lines are in the affine space of each boundary cell already self._is_convex = True - self._polyhedron = Polyhedron(vertices=vertices, rays=rays, lines=lines) + self._polyhedron = Polyhedron(vertices=vertices, rays=rays, lines=lines, + backend=self._backend) return True def union_as_polyhedron(self): @@ -1561,7 +1602,8 @@ def product(self, right): for g in right.maximal_cell_iterator()] return PolyhedralComplex(maximal_cells, maximality_check=False, is_immutable=(self._is_immutable and - right._is_immutable)) + right._is_immutable), + backend=self._backend) def disjoint_union(self, right): """ @@ -1585,7 +1627,8 @@ def disjoint_union(self, right): return PolyhedralComplex(maximal_cells, maximality_check=True, face_to_face_check=True, is_immutable=(self._is_immutable and - right._is_immutable)) + right._is_immutable), + backend=self._backend) def join(self, right): """ @@ -1609,7 +1652,8 @@ def join(self, right): for g in right.maximal_cell_iterator()] return PolyhedralComplex(maximal_cells, maximality_check=False, is_immutable=(self._is_immutable and - right._is_immutable)) + right._is_immutable), + backend=self._backend) ############################################################ # abstract methods not implemented in generic cell complexe @@ -1803,6 +1847,8 @@ def add_cell(self, cell): # if cell is already in self, do nothing. if self.has_cell(cell): return + if self._backend: + cell = cell.base_extend(cell.base_ring(), self._backend) # update cells and face poset cells = self.cells() covers = {p: self.face_poset().upper_covers(p) @@ -2147,7 +2193,8 @@ def subdivide(self, make_simplicial=False, else: new.append(cell) cells = new - return PolyhedralComplex(cells, maximality_check=False) + return PolyhedralComplex(cells, maximality_check=False, + backend=self._backend) elif self.is_polyhedral_fan(): if new_vertices and any(vi != 0 for v in new_vertices for vi in v): raise ValueError("new vertices cannot be used for subdivision.") @@ -2186,7 +2233,8 @@ def subdivide(self, make_simplicial=False, for neg_rays in powerset(range(num_lines)): lines = [vector(plines[i]) if i not in neg_rays else -vector(plines[i]) for i in range(num_lines)] - cones.append(Polyhedron(rays=(prays + lines))) + cones.append(Polyhedron(rays=(prays + lines), + backend=self._backend)) rays = [] if new_rays: for r in new_rays: @@ -2216,7 +2264,8 @@ def subdivide(self, make_simplicial=False, else: new.append(cone) cones = new - return PolyhedralComplex(cones, maximality_check=False) + return PolyhedralComplex(cones, maximality_check=False, + backend=self._backend) else: # TODO: `self`` is unbounded, make it projectively simplicial. # (1) homogenize self of dim d to fan in space of dim d+1; From 752011fc6cb7e1ad97b6c4c675e3ba0533df3a91 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Thu, 13 May 2021 16:59:40 -0400 Subject: [PATCH 032/336] zero is followed by plural countable nouns --- src/sage/homology/polyhedral_complex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 4cf78f23d9f..645b9e647b5 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -1276,7 +1276,7 @@ def stratify(self, n): sage: pc.stratify(2) == pc True sage: pc.stratify(1) - Polyhedral complex with 0 maximal cell + Polyhedral complex with 0 maximal cells Wrong answer due to ``maximality_check=False``:: @@ -1703,7 +1703,7 @@ def _repr_(self): Polyhedral complex with 3 maximal cells """ num = len(list(self.maximal_cell_iterator())) - if num <= 1: + if num == 1: return "Polyhedral complex with %s maximal cell" % num else: return "Polyhedral complex with %s maximal cells" % num From 6f3480194c8fd7cab195369cecf0913729e535be Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 13:07:33 -0700 Subject: [PATCH 033/336] build/pkgs/lrslib: Update lrslib to 071a+autotools-2021-05-17 --- build/pkgs/lrslib/checksums.ini | 7 ++++--- build/pkgs/lrslib/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/lrslib/checksums.ini b/build/pkgs/lrslib/checksums.ini index 88276625302..217c2129a94 100644 --- a/build/pkgs/lrslib/checksums.ini +++ b/build/pkgs/lrslib/checksums.ini @@ -1,4 +1,5 @@ tarball=lrslib-VERSION.tar.gz -sha1=c644600ca4a51eb08e61ba175e1d4ec999dc3e4b -md5=bb435ccdd5faf292102e246827926680 -cksum=3830123478 +sha1=1c8aa6b4e2e00b883745645aae876a3ba6621a23 +md5=20a8e4f2d7ce7d53494be20f091f88b1 +cksum=3776689498 +upstream_url=https://github.com/mkoeppe/lrslib/releases/download/lrslib-VERSION/lrslib-VERSION.tar.gz diff --git a/build/pkgs/lrslib/package-version.txt b/build/pkgs/lrslib/package-version.txt index 54baf794ad3..def311cb473 100644 --- a/build/pkgs/lrslib/package-version.txt +++ b/build/pkgs/lrslib/package-version.txt @@ -1 +1 @@ -062+autotools-2017-03-03.p1 +071a+autotools-2021-05-17 From a6ba76004206a0dd82fb90a286b3566c5aa13fb5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 13:34:05 -0700 Subject: [PATCH 034/336] build/pkgs/lrslib/spkg-install.in: Remove LRS_QUIET - in 071a, it makes the output unusable --- build/pkgs/lrslib/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/lrslib/spkg-install.in b/build/pkgs/lrslib/spkg-install.in index 14a634cc5e5..30dacb8b8e4 100644 --- a/build/pkgs/lrslib/spkg-install.in +++ b/build/pkgs/lrslib/spkg-install.in @@ -1,4 +1,4 @@ cd src/ -sdh_configure CPPFLAGS="-DLRS_QUIET $CPPFLAGS" +sdh_configure sdh_make sdh_make_install From d1bbfe466512e5d98dd7961adfcda5918d530b30 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 13:56:03 -0700 Subject: [PATCH 035/336] src/sage/features/lrs.py: Accept output from lrslib 071a --- src/sage/features/lrs.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/features/lrs.py b/src/sage/features/lrs.py index be62d14f9f0..ab1871a6314 100644 --- a/src/sage/features/lrs.py +++ b/src/sage/features/lrs.py @@ -54,9 +54,12 @@ def is_functional(self): return FeatureTestResult(self, False, reason="Call to `{command}` failed with exit code {e.returncode}.".format(command=" ".join(command), e=e)) - expected = "Volume= 1" - if lines.find(expected) == -1: + expected_list = ["Volume= 1", "Volume=1"] + if all(lines.find(expected) == -1 for expected in expected_list): + print(lines) return FeatureTestResult(self, False, - reason="Output of `{command}` did not contain the expected result `{expected}`.".format(command=" ".join(command), expected=expected)) + reason="Output of `{command}` did not contain the expected result {expected}.".format( + command=" ".join(command), + expected=" or ".join(expected_list))) return FeatureTestResult(self, True) From 5d256d5832875c04fb8d1f3044e352a1620c379e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 16:13:50 -0700 Subject: [PATCH 036/336] src/sage/game_theory/parser.py: Make the parser accept lrslib-072a output --- src/sage/game_theory/parser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index 09596a90e90..00cc8efa5ee 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -222,7 +222,10 @@ def format_lrs(self): equilibria = [] from sage.misc.sage_eval import sage_eval from itertools import groupby - for collection in [list(x[1]) for x in groupby(self.raw_string[7:], lambda x: x == '\n')]: + lines = iter(self.raw_string) + while not next(lines).startswith("*****"): + pass + for collection in [list(x[1]) for x in groupby(lines, lambda x: x == '\n')]: if collection[0].startswith('2'): s1 = tuple([sage_eval(k) for k in collection[-1].split()][1:-1]) for s2 in collection[:-1]: From d09bb906aea4e9e0d01546225a8d15d51fed2865 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 18:11:31 -0700 Subject: [PATCH 037/336] build/pkgs/polymake: Update to 4.3 --- build/pkgs/polymake/checksums.ini | 7 ++++--- build/pkgs/polymake/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/polymake/checksums.ini b/build/pkgs/polymake/checksums.ini index d5dbf191a01..69dc213915e 100644 --- a/build/pkgs/polymake/checksums.ini +++ b/build/pkgs/polymake/checksums.ini @@ -1,4 +1,5 @@ tarball=polymake-VERSION-minimal.tar.bz2 -sha1=e02bc7590910cff633c612cd95baa0c3aeade036 -md5=b0c6332c148060741d84b6f0eaed8343 -cksum=1380625742 +sha1=d531b6d6075b158b5ccabf1c356acc859fe90014 +md5=373fc8f46a98946968e929198a1655a0 +cksum=174960302 +upstream_url=https://polymake.org/lib/exe/fetch.php/download/polymake-VERSION-minimal.tar.bz2 diff --git a/build/pkgs/polymake/package-version.txt b/build/pkgs/polymake/package-version.txt index 2f4b60750dc..69df05f33b7 100644 --- a/build/pkgs/polymake/package-version.txt +++ b/build/pkgs/polymake/package-version.txt @@ -1 +1 @@ -3.4 +4.3 From 70c112de9af75ab9173cb22bf4e832a3e695c8c7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 18:17:12 -0700 Subject: [PATCH 038/336] build/pkgs/polymake/patches/: Remove --- ...liz-support-configure.pl-Conditional.patch | 27 ---- ...L_SET_CONTEXT-for-thread-correctness.patch | 128 ------------------ .../0003-Shell-Mock-Add-compile_scope.patch | 24 ---- 3 files changed, 179 deletions(-) delete mode 100644 build/pkgs/polymake/patches/0001-bundled-libnormaliz-support-configure.pl-Conditional.patch delete mode 100644 build/pkgs/polymake/patches/0002-Add-PERL_SET_CONTEXT-for-thread-correctness.patch delete mode 100644 build/pkgs/polymake/patches/0003-Shell-Mock-Add-compile_scope.patch diff --git a/build/pkgs/polymake/patches/0001-bundled-libnormaliz-support-configure.pl-Conditional.patch b/build/pkgs/polymake/patches/0001-bundled-libnormaliz-support-configure.pl-Conditional.patch deleted file mode 100644 index 1c85673467f..00000000000 --- a/build/pkgs/polymake/patches/0001-bundled-libnormaliz-support-configure.pl-Conditional.patch +++ /dev/null @@ -1,27 +0,0 @@ -From ddef1df5781ad60d65de71e7689b56dfad7288b5 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe -Date: Thu, 25 Apr 2019 14:59:19 +0200 -Subject: [PATCH 1/3] bundled/libnormaliz/support/configure.pl: Conditionalize - - include of omp.h on _OPENMP ---- - bundled/libnormaliz/support/configure.pl | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/bundled/libnormaliz/support/configure.pl b/bundled/libnormaliz/support/configure.pl -index 6d669270..6b512bd0 100644 ---- a/bundled/libnormaliz/support/configure.pl -+++ b/bundled/libnormaliz/support/configure.pl -@@ -74,7 +74,9 @@ int main() { - #include - #include - #include -+#ifdef _OPENMP - #include -+#endif - #include - #include - #include --- -2.19.0 - diff --git a/build/pkgs/polymake/patches/0002-Add-PERL_SET_CONTEXT-for-thread-correctness.patch b/build/pkgs/polymake/patches/0002-Add-PERL_SET_CONTEXT-for-thread-correctness.patch deleted file mode 100644 index a6b1c38614d..00000000000 --- a/build/pkgs/polymake/patches/0002-Add-PERL_SET_CONTEXT-for-thread-correctness.patch +++ /dev/null @@ -1,128 +0,0 @@ -From 51f36ef29e7d2482c23ea4da697e626cc61da611 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe -Date: Thu, 2 May 2019 13:31:00 +0200 -Subject: [PATCH 2/3] Add PERL_SET_CONTEXT for thread correctness - ---- - lib/callable/src/perl/methods.cc | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - -diff --git a/lib/callable/src/perl/methods.cc b/lib/callable/src/perl/methods.cc -index 11beefb2..fccf63b7 100644 ---- a/lib/callable/src/perl/methods.cc -+++ b/lib/callable/src/perl/methods.cc -@@ -40,6 +40,7 @@ const char Extension[]="Polymake::Core::Extension"; - void Main::set_application(const AnyString& appname) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(1); - mPUSHp(appname.ptr, appname.len); - PUTBACK; -@@ -49,6 +50,7 @@ void Main::set_application(const AnyString& appname) - void Main::set_application_of(const Object& x) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(1); - PUSHs(x.obj_ref); - PUTBACK; -@@ -58,6 +60,7 @@ void Main::set_application_of(const Object& x) - void Main::add_extension(const AnyString& path) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(2); - mPUSHp(Extension, sizeof(Extension)-1); - mPUSHp(path.ptr, path.len); -@@ -83,6 +86,7 @@ void Main::reset_preference(const AnyString& label_exp) - SV* Main::lookup_extension(const AnyString& path) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(2); - mPUSHp(Extension, sizeof(Extension)-1); - mPUSHp(path.ptr, path.len); -@@ -93,6 +97,7 @@ SV* Main::lookup_extension(const AnyString& path) - void Main::call_app_method(const char* method, const AnyString& arg) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(2); - SV* const app=glue::get_current_application(aTHX); - PUSHs(app); -@@ -104,6 +109,7 @@ void Main::call_app_method(const char* method, const AnyString& arg) - void Main::set_custom_var(const AnyString& name, const AnyString& key, Value& x) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(3); - mPUSHp(name.ptr, name.len); - if (key.ptr) mPUSHp(key.ptr, key.len); -@@ -115,6 +121,7 @@ void Main::set_custom_var(const AnyString& name, const AnyString& key, Value& x) - void Main::reset_custom(const AnyString& name, const AnyString& key) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(2); - mPUSHp(name.ptr, name.len); - if (key.ptr) mPUSHp(key.ptr, key.len); -@@ -125,6 +132,7 @@ void Main::reset_custom(const AnyString& name, const AnyString& key) - Scope Main::newScope() - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(0); - return Scope(this, call_func_scalar(aTHX_ new_scope_cv)); - } -@@ -137,6 +145,7 @@ void Scope::prefer_now(const AnyString& labels) const - void Scope::set_custom_var(const AnyString& name, const AnyString& key, Value& x) const - { - dTHXa(pm_main->pi); -+ PERL_SET_CONTEXT(pm_main->pi); - PmStartFuncall(3); - mPUSHp(name.ptr, name.len); - if (key.ptr) mPUSHp(key.ptr, key.len); -@@ -148,6 +157,7 @@ void Scope::set_custom_var(const AnyString& name, const AnyString& key, Value& x - std::string Main::greeting(int verbose) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(1); - mPUSHi(verbose); - PUTBACK; -@@ -157,6 +167,7 @@ std::string Main::greeting(int verbose) - void Main::shell_enable() - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(0); - glue::call_func_void(aTHX_ shell_enable_cv); - } -@@ -167,6 +178,7 @@ Main::shell_execute_t Main::shell_execute(const std::string& input) - return shell_execute_t(true, input, input, input); - - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(1); - mPUSHp(input.c_str(), input.size()); - PUTBACK; -@@ -187,6 +199,7 @@ Main::shell_execute_t Main::shell_execute(const std::string& input) - Main::shell_complete_t Main::shell_complete(const std::string& input) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(1); - mPUSHp(input.c_str(), input.size()); - PUTBACK; -@@ -209,6 +222,7 @@ Main::shell_complete_t Main::shell_complete(const std::string& input) - std::vector Main::shell_context_help(const std::string& input, size_t pos, bool full, bool html) - { - dTHXa(pi); -+ PERL_SET_CONTEXT(pi); - PmStartFuncall(4); - mPUSHp(input.c_str(), input.size()); - if (pos == std::string::npos) --- -2.19.0 - diff --git a/build/pkgs/polymake/patches/0003-Shell-Mock-Add-compile_scope.patch b/build/pkgs/polymake/patches/0003-Shell-Mock-Add-compile_scope.patch deleted file mode 100644 index 0e51c353852..00000000000 --- a/build/pkgs/polymake/patches/0003-Shell-Mock-Add-compile_scope.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 50ce55eea115b74eae9421de43a4405e254ef579 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe -Date: Fri, 10 May 2019 14:59:44 +0200 -Subject: [PATCH 3/3] Shell::Mock: Add compile_scope - ---- - perllib/Polymake/Core/ShellMock.pm | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/perllib/Polymake/Core/ShellMock.pm b/perllib/Polymake/Core/ShellMock.pm -index 46d59654..137f0b24 100644 ---- a/perllib/Polymake/Core/ShellMock.pm -+++ b/perllib/Polymake/Core/ShellMock.pm -@@ -34,6 +34,7 @@ use Polymake::Struct ( - - sub term { shift } - sub interactive { 0 } -+sub compile_scope { undef } - - sub complete { - my ($self, $string)=@_; --- -2.19.0 - From 5161de92b46fbf9bed4db768ec5bb99f637a9b0d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 18:22:25 -0700 Subject: [PATCH 039/336] build/pkgs/lrslib: Use lrslib-071a+autotools-2021-05-17a --- build/pkgs/lrslib/checksums.ini | 6 +++--- build/pkgs/lrslib/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/lrslib/checksums.ini b/build/pkgs/lrslib/checksums.ini index 217c2129a94..536f8f83701 100644 --- a/build/pkgs/lrslib/checksums.ini +++ b/build/pkgs/lrslib/checksums.ini @@ -1,5 +1,5 @@ tarball=lrslib-VERSION.tar.gz -sha1=1c8aa6b4e2e00b883745645aae876a3ba6621a23 -md5=20a8e4f2d7ce7d53494be20f091f88b1 -cksum=3776689498 +sha1=7037a6e87e2fdfef209b53fdc2970b4b7890df39 +md5=64e77f97f22e69a22782ff7652a41dac +cksum=2150131340 upstream_url=https://github.com/mkoeppe/lrslib/releases/download/lrslib-VERSION/lrslib-VERSION.tar.gz diff --git a/build/pkgs/lrslib/package-version.txt b/build/pkgs/lrslib/package-version.txt index def311cb473..4e78d8b4aea 100644 --- a/build/pkgs/lrslib/package-version.txt +++ b/build/pkgs/lrslib/package-version.txt @@ -1 +1 @@ -071a+autotools-2021-05-17 +071a+autotools-2021-05-17a From 29a2e12b9bd782e4e05813145d7e4ec52b4ed47a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 18:40:03 -0700 Subject: [PATCH 040/336] build/pkgs/lrslib: Use lrslib-071a+autotools-2021-05-17b --- build/pkgs/lrslib/checksums.ini | 6 +++--- build/pkgs/lrslib/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/lrslib/checksums.ini b/build/pkgs/lrslib/checksums.ini index 536f8f83701..f5dfff67121 100644 --- a/build/pkgs/lrslib/checksums.ini +++ b/build/pkgs/lrslib/checksums.ini @@ -1,5 +1,5 @@ tarball=lrslib-VERSION.tar.gz -sha1=7037a6e87e2fdfef209b53fdc2970b4b7890df39 -md5=64e77f97f22e69a22782ff7652a41dac -cksum=2150131340 +sha1=b3591f3b282c231229a7e08b384fa5265da7af72 +md5=68c527d0a240bd3292705065450c67aa +cksum=1517171501 upstream_url=https://github.com/mkoeppe/lrslib/releases/download/lrslib-VERSION/lrslib-VERSION.tar.gz diff --git a/build/pkgs/lrslib/package-version.txt b/build/pkgs/lrslib/package-version.txt index 4e78d8b4aea..91a8729ee3a 100644 --- a/build/pkgs/lrslib/package-version.txt +++ b/build/pkgs/lrslib/package-version.txt @@ -1 +1 @@ -071a+autotools-2021-05-17a +071a+autotools-2021-05-17b From bc664901244ae2f9e524c18e672c6635b4f975d4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 23:49:19 -0700 Subject: [PATCH 041/336] build/pkgs/libxml2/spkg-install: Make it executable --- build/pkgs/libxml2/spkg-install | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build/pkgs/libxml2/spkg-install diff --git a/build/pkgs/libxml2/spkg-install b/build/pkgs/libxml2/spkg-install old mode 100644 new mode 100755 From 3456c2c2f2bea87c89a800c6f3884f70c9a70d0b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 May 2021 09:56:17 -0700 Subject: [PATCH 042/336] build/pkgs/polymake: Update to 4.4 --- build/pkgs/polymake/checksums.ini | 6 +++--- build/pkgs/polymake/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/polymake/checksums.ini b/build/pkgs/polymake/checksums.ini index 69dc213915e..e059b0e2bbd 100644 --- a/build/pkgs/polymake/checksums.ini +++ b/build/pkgs/polymake/checksums.ini @@ -1,5 +1,5 @@ tarball=polymake-VERSION-minimal.tar.bz2 -sha1=d531b6d6075b158b5ccabf1c356acc859fe90014 -md5=373fc8f46a98946968e929198a1655a0 -cksum=174960302 +sha1=5370b16300ff8aed4eb5fa6bb5232d25935e6303 +md5=8f6a8e87e3b8bf5ccf22f26790a4dd1a +cksum=1398515202 upstream_url=https://polymake.org/lib/exe/fetch.php/download/polymake-VERSION-minimal.tar.bz2 diff --git a/build/pkgs/polymake/package-version.txt b/build/pkgs/polymake/package-version.txt index 69df05f33b7..515be8f918d 100644 --- a/build/pkgs/polymake/package-version.txt +++ b/build/pkgs/polymake/package-version.txt @@ -1 +1 @@ -4.3 +4.4 From 3296f423148f0dee92d8f0de18211e391f3bc987 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 May 2021 15:29:03 -0700 Subject: [PATCH 043/336] build/pkgs/sage_numerical_backends_gurobi/checksums.ini: Update to 9.3.1 --- build/pkgs/sage_numerical_backends_gurobi/checksums.ini | 6 +++--- .../pkgs/sage_numerical_backends_gurobi/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/sage_numerical_backends_gurobi/checksums.ini b/build/pkgs/sage_numerical_backends_gurobi/checksums.ini index f81be90befd..d5e6fd8e2b1 100644 --- a/build/pkgs/sage_numerical_backends_gurobi/checksums.ini +++ b/build/pkgs/sage_numerical_backends_gurobi/checksums.ini @@ -1,5 +1,5 @@ tarball=sage_numerical_backends_gurobi-VERSION.tar.gz -sha1=64253f735ee6779b6ded809643b455061a105dc4 -md5=3df7d18fe53af89e0a796974a2d788b5 -cksum=3824076545 +sha1=36a2bfa1cfa2f4fdcb9dc979e80177580bb7aac2 +md5=0a77377fad705950c4b7b14b366ebaa6 +cksum=478792673 upstream_url=https://pypi.io/packages/source/s/sage_numerical_backends_gurobi/sage_numerical_backends_gurobi-VERSION.tar.gz diff --git a/build/pkgs/sage_numerical_backends_gurobi/package-version.txt b/build/pkgs/sage_numerical_backends_gurobi/package-version.txt index f7ee06693c1..3b74042132a 100644 --- a/build/pkgs/sage_numerical_backends_gurobi/package-version.txt +++ b/build/pkgs/sage_numerical_backends_gurobi/package-version.txt @@ -1 +1 @@ -9.0.0 +9.3.1 From 720c62472b69367754979ae346fe7690508342f6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 May 2021 16:18:54 -0700 Subject: [PATCH 044/336] build/pkgs/lrslib: Use 071a+autotools-2021-05-18 --- build/pkgs/lrslib/checksums.ini | 6 +++--- build/pkgs/lrslib/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/lrslib/checksums.ini b/build/pkgs/lrslib/checksums.ini index f5dfff67121..36edfab83c9 100644 --- a/build/pkgs/lrslib/checksums.ini +++ b/build/pkgs/lrslib/checksums.ini @@ -1,5 +1,5 @@ tarball=lrslib-VERSION.tar.gz -sha1=b3591f3b282c231229a7e08b384fa5265da7af72 -md5=68c527d0a240bd3292705065450c67aa -cksum=1517171501 +sha1=7c5156b10cb31e69af8a27f8faff8eb957dc1d3a +md5=54f8c29d402b7a278770ee6f8808f77e +cksum=3493213188 upstream_url=https://github.com/mkoeppe/lrslib/releases/download/lrslib-VERSION/lrslib-VERSION.tar.gz diff --git a/build/pkgs/lrslib/package-version.txt b/build/pkgs/lrslib/package-version.txt index 91a8729ee3a..073b7651f1f 100644 --- a/build/pkgs/lrslib/package-version.txt +++ b/build/pkgs/lrslib/package-version.txt @@ -1 +1 @@ -071a+autotools-2021-05-17b +071a+autotools-2021-05-18 From 3504a7d5b3e83099b166c3f3e8d8ac74cc7875dc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 May 2021 22:31:09 -0700 Subject: [PATCH 045/336] build/pkgs/polymake/spkg-install.in: Pass number of threads to ninja --- build/pkgs/polymake/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/polymake/spkg-install.in b/build/pkgs/polymake/spkg-install.in index 5a74ff5c609..ed742e88d64 100644 --- a/build/pkgs/polymake/spkg-install.in +++ b/build/pkgs/polymake/spkg-install.in @@ -26,5 +26,5 @@ esac --bindir="$SAGE_LOCAL"/bin \ --libdir="$SAGE_LOCAL"/lib \ $more_configure_options || sdh_die "Error configuring Polymake" -sdh_make +ninja -C Opt -j${SAGE_NUM_THREADS} sdh_make_install From 06284e94163eb5189ddfc7cfc0ba58f8dc1c716b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 May 2021 22:43:02 -0700 Subject: [PATCH 046/336] Fixup --- build/pkgs/polymake/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/polymake/spkg-install.in b/build/pkgs/polymake/spkg-install.in index ed742e88d64..ff2e6718363 100644 --- a/build/pkgs/polymake/spkg-install.in +++ b/build/pkgs/polymake/spkg-install.in @@ -26,5 +26,5 @@ esac --bindir="$SAGE_LOCAL"/bin \ --libdir="$SAGE_LOCAL"/lib \ $more_configure_options || sdh_die "Error configuring Polymake" -ninja -C Opt -j${SAGE_NUM_THREADS} +ninja -C build/Opt -j${SAGE_NUM_THREADS} sdh_make_install From 52c250565b372a9de0ee49444dc5361f1eb4df09 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Sep 2020 10:34:01 -0700 Subject: [PATCH 047/336] tox.ini: Add nvidia-cuda --- tox.ini | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tox.ini b/tox.ini index 702c8be311e..d37fbc6bd1d 100644 --- a/tox.ini +++ b/tox.ini @@ -348,6 +348,16 @@ setenv = manylinux-ppc64le: ARCH_IMAGE_SUFFIX=_ppc64le manylinux-s390x: ARCH_IMAGE_SUFFIX=_s390x # + # https://hub.docker.com/r/nvidia/cuda + # + ubuntu-nvidia-cuda: BASE_IMAGE=nvidia/cuda + centos-nvidia-cuda: BASE_IMAGE=nvidia/cuda + nvidia-cuda: ARCH_TAG_PREFIX=11.0-devel- + nvidia-cuda-11.0: ARCH_TAG_PREFIX=11.0-devel- + ubuntu-focal-nvidia-cuda: BASE_TAG=ubuntu20.04 + ubuntu-bionic-nvidia-cuda: BASE_TAG=ubuntu18.04 + ubuntu-xenial-nvidia-cuda: BASE_TAG=ubuntu16.04 + # # Resulting full image:tag name # docker: FULL_BASE_IMAGE_AND_TAG={env:ARCH_IMAGE_PREFIX:}{env:BASE_IMAGE}{env:ARCH_IMAGE_SUFFIX:}:{env:ARCH_TAG_PREFIX:}{env:BASE_TAG}{env:ARCH_TAG_SUFFIX:} From 77bce1f4e9eadf47e96593360704ef6cff7e6a47 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 20 May 2021 10:41:46 +1000 Subject: [PATCH 048/336] Cleaning up the interface file. --- src/sage/interfaces/polymake.py | 186 ++++++++++++++++---------------- 1 file changed, 96 insertions(+), 90 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 521e1dc4f18..e40826c4b16 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -112,7 +112,7 @@ class PolymakeAbstract(ExtraTabCompletion, Interface): but through its subclasses Polymake (Pexpect interface) or PolymakeJuPyMake (JuPyMake interface). - EXAMPLES: + EXAMPLES:: sage: from sage.interfaces.polymake import PolymakeAbstract, polymake_expect, polymake_jupymake @@ -363,9 +363,9 @@ def _start(self): """ Start the polymake interface in the application "polytope". - NOTE: + .. NOTE:: - There should be no need to call this explicitly. + There should be no need to call this explicitly. TESTS:: @@ -442,12 +442,12 @@ def _next_var_name(self): return r'SAGE{}'.format(self.__seq) def clear(self, var): - """ - Clear the variable named var. + r""" + Clear the variable named ``var``. - NOTE: + .. NOTE:: - This is implicitly done when deleting an element in the interface. + This is implicitly done when deleting an element in the interface. TESTS:: @@ -469,24 +469,24 @@ def _create(self, value, name=None): INPUT: - - ``value``, a string: Polymake command (or value) whose result + - ``value`` -- string; Polymake command (or value) whose result is to be assigned to a variable - - ``name``, optional string: If given, the new variable has this name. - Otherwise, the name is automatically generated. + - ``name`` -- (optional) string; if given, the new variable has this + name; otherwise, the name is automatically generated RETURN: The command by which the assigned value can now be retrieved. - NOTE: + .. NOTE:: - In order to overcome problems with the perl programming language, - we store *all* data as arrays. If the given value is an array - of length different from one, then the new variable contains that - array. Otherwise, the new variable is an array of length one whose - only entry is the given value, which has to be a scalar (which - also includes Perl references). In other words, perl hashes - are not suitable. + In order to overcome problems with the perl programming language, + we store *all* data as arrays. If the given value is an array + of length different from one, then the new variable contains that + array. Otherwise, the new variable is an array of length one whose + only entry is the given value, which has to be a scalar (which + also includes Perl references). In other words, perl hashes + are not suitable. EXAMPLES:: @@ -666,15 +666,15 @@ def help(self, topic, pager=True): return H def _tab_completion(self): - """ - Returns a list of polymake function names. + r""" + Return a list of polymake function names. - NOTE: + ..NOTE:: - - The list of functions depends on the current application. The - result is cached, of course separately for each application. - - It is generally not the case that all the returned function names - can actually successfully be called. + - The list of functions depends on the current application. The + result is cached, of course separately for each application. + - It is generally not the case that all the returned function + names can actually successfully be called. TESTS:: @@ -683,19 +683,18 @@ def _tab_completion(self): True sage: polymake.application('polytope') # optional - polymake - Since 'normal_fan' is not defined in the polymake application 'polytope', - we now get - :: + Since ``'normal_fan'`` is not defined in the polymake application + ``'polytope'``, we now get:: sage: 'normal_fan' in dir(polymake) # optional - polymake False - Global functions from 'core' are available:: + Global functions from ``'core'`` are available:: sage: 'show_credits' in dir(polymake) # optional - polymake True - Global functions from 'common' are available:: + Global functions from ``'common'`` are available:: sage: 'lex_ordered' in dir(polymake) # optional - polymake True @@ -709,7 +708,9 @@ def _tab_completion(self): s = self.eval("apropos '';").split('\n') out = [] for name in s: - if name.startswith("/common/functions/") or name.startswith("/core/functions") or name.startswith("/" + self._application + "/functions/"): + if (name.startswith("/common/functions/") + or name.startswith("/core/functions") + or name.startswith("/" + self._application + "/functions/")): out.append(name.split("/")[-1]) self.__tab_completion[self._application] = sorted(out) return self.__tab_completion[self._application] @@ -1040,12 +1041,12 @@ def known_properties(self): """ List the names of properties that have been computed so far on this element. - NOTE: + .. NOTE:: - This is in many cases equivalent to use polymake's ``list_properties``, - which returns a blank separated string representation of the list of properties. - However, on some elements, ``list_properties`` would simply result in - an error. + This is in many cases equivalent to use polymake's + ``list_properties``, which returns a blank separated string + representation of the list of properties. However, on some + elements, ``list_properties`` would simply result in an error. EXAMPLES:: @@ -1173,14 +1174,14 @@ def _tab_completion(self): """ Return a list of available function and property names. - NOTE: + .. NOTE:: - This currently returns the names of functions defined in the current - application, regardless whether they can be applied to this element - or not, together with the list of properties of this element that - polymake knows how to compute. It does not contain the list of available - member functions of this element. This may change in future versions - of polymake. + This currently returns the names of functions defined in the current + application, regardless whether they can be applied to this element + or not, together with the list of properties of this element that + polymake knows how to compute. It does not contain the list of available + member functions of this element. This may change in future versions + of polymake. EXAMPLES:: @@ -1201,16 +1202,16 @@ def __getattr__(self, attrname): Return a property of this element, or a polymake function with this element as first argument, or a member function of this element. - NOTE: + .. NOTE:: - If the attribute name is known as the name of a property, it is - interpreted as such. Otherwise, if it is known as a function in - the current application, the function is returned with this - element inserted as first argument, and potential further arguments, - when called. Otherwise, it is assumed that it is a member function - of this element, and treated as such. Note that member functions - are currently invisible in tab completion, thus, the user has - to know the name of the member function. + If the attribute name is known as the name of a property, it is + interpreted as such. Otherwise, if it is known as a function in + the current application, the function is returned with this + element inserted as first argument, and potential further arguments, + when called. Otherwise, it is assumed that it is a member function + of this element, and treated as such. Note that member functions + are currently invisible in tab completion, thus, the user has + to know the name of the member function. EXAMPLES: @@ -1265,10 +1266,10 @@ def get_member_function(self, attrname): """ Request a member function of this element. - NOTE: + .. NOTE:: - It is not checked whether a member function with the given name - exists. + It is not checked whether a member function with the given name + exists. EXAMPLES:: @@ -1298,11 +1299,11 @@ def get_member(self, attrname): """ Get a member/property of this element. - NOTE: + .. NOTE:: - Normally, it should be possible to just access the property - in the usual Python syntax for attribute access. However, if - that fails, one can request the member explicitly. + Normally, it should be possible to just access the property + in the usual Python syntax for attribute access. However, if + that fails, one can request the member explicitly. EXAMPLES:: @@ -1420,11 +1421,12 @@ def __len__(self): @cached_method def typeof(self): """ - Returns the type of a polymake "big" object, and its underlying Perl type. + Return the type of a polymake "big" object, and its + underlying Perl type. - NOTE: + .. NOTE:: - This is mainly for internal use. + This is mainly for internal use. EXAMPLES:: @@ -1693,10 +1695,10 @@ def _sage_doc_(self): """ Return documentation of this function. - NOTE: + .. NOTE:: - For unclear reasons, accessing documentation with `?` sometimes - does not include the return value of this method. + For unclear reasons, accessing documentation with ``?`` sometimes + does not include the return value of this method. EXAMPLES:: @@ -1823,9 +1825,9 @@ def _start(self, alt_message=None): """ Start the polymake interface in the application "polytope". - NOTE: + .. NOTE:: - There should be no need to call this explicitly. + There should be no need to call this explicitly. TESTS:: @@ -1837,8 +1839,7 @@ def _start(self, alt_message=None): sage: polymake._start() # optional - polymake Since 'normal_fan' is not defined in the polymake application 'polytope', - we now get - :: + we now get:: sage: 'normal_fan' in dir(polymake) # optional - polymake False @@ -1861,13 +1862,13 @@ def _quit_string(self): return "exit;" def _keyboard_interrupt(self): - """ - Interrupt a computation with + r""" + Interrupt a computation with . TESTS: For reasons that are not clear to the author, the following test - is very flaky. Therefore, this test is marked as "not tested". + is very flaky. Therefore, this test is marked as "not tested". :: sage: from sage.interfaces.polymake import polymake_expect as polymake sage: c = polymake.cube(15) # optional - polymake @@ -1999,24 +2000,26 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if INPUT: - - ``line``, a command (string) to be evaluated - - ``allow_use_file`` (optional bool, default ``True``), whether or not - to use a file if the line is very long. - - ``wait_for_prompt`` (optional, default ``True``), whether or not - to wait before polymake returns a prompt. If it is a string, it is considered - as alternative prompt to be waited for. - - ``restart_if_needed`` (optional bool, default ``True``), whether or + - ``line`` -- string; a command to be evaluated + - ``allow_use_file`` -- (default: ``True``) bool; whether or not + to use a file if the line is very long + - ``wait_for_prompt`` -- (default: ``True``) bool; whether or not + to wait before polymake returns a prompt. If it is a string, it is + considered as alternative prompt to be waited for + - ``restart_if_needed`` (default: ``True``) bool; whether or not to restart polymake in case something goes wrong - further optional arguments (e.g., timeout) that will be passed to :meth:`pexpect.pty_spawn.spawn.expect`. Note that they are ignored if the line is too long and thus is evaluated via a file. So, - if a timeout is defined, it should be accompanied by ``allow_use_file=False``. + if a timeout is defined, it should be accompanied by + ``allow_use_file=False``. Different reaction types of polymake, including warnings, comments, errors, request for user interaction, and yielding a continuation prompt, are taken into account. - Usually, this method is indirectly called via :meth:`~sage.interfaces.expect.Expect.eval`. + Usually, this method is indirectly called via + :meth:`~sage.interfaces.expect.Expect.eval`. EXAMPLES:: @@ -2385,7 +2388,6 @@ def application(self, app): class PolymakeJuPyMake(PolymakeAbstract): - r""" Interface to the polymake interpreter using JuPyMake. @@ -2433,6 +2435,8 @@ class PolymakeJuPyMake(PolymakeAbstract): To obtain Perl strings, use strings containing double-quote characters. Python dicts are translated to Perl hashes. + :: + sage: L = polymake.db_query({'"_id"': '"F.4D.0047"'}, # long time, optional - jupymake internet perl_mongodb ....: db='"LatticePolytopes"', ....: collection='"SmoothReflexive"'); L @@ -2453,7 +2457,7 @@ def __init__(self, seed=None, verbose=False): INPUT: - ``verbose`` -- boolean (default: ``False``); whether to print the - commands passed to polymake. + commands passed to polymake TESTS:: @@ -2468,7 +2472,7 @@ def __init__(self, seed=None, verbose=False): def is_running(self): """ - Return True if self is currently running. + Return ``True`` if ``self`` is currently running. TESTS:: @@ -2514,7 +2518,7 @@ def eval(self, code, **kwds): INPUT: - - ``code``, a command (string) to be evaluated + - ``code`` -- a command (string) to be evaluated Different reaction types of polymake, including warnings, comments, errors, request for user interaction, and yielding a continuation prompt, @@ -2565,7 +2569,8 @@ def eval(self, code, **kwds): When requesting help, polymake sometimes expect the user to choose from a list. In that situation, we abort with a warning, and show the list from which the user can choose; we could demonstrate this using - the :meth:`help` method, but here we use an explicit code evaluation:: + the :meth:`~sage.interfaces.polymake.PolymakeAbstract.help` method, + but here we use an explicit code evaluation:: sage: print(polymake.eval('help "TRIANGULATION";')) # optional - jupymake # random doctest:warning @@ -2590,7 +2595,8 @@ def eval(self, code, **kwds): ... RuntimeError: Polymake fails to respond timely - We verify that after the timeout, polymake is still able to give answers:: + We verify that after the timeout, polymake is still able + to give answers:: sage: c # optional - jupymake cube of dimension 15 @@ -2601,7 +2607,6 @@ def eval(self, code, **kwds): It may happen that in some situation the interface collapses and thus polymake would automatically be restarted, thereby losing all data that have been computed before. - """ if not self.is_running(): self._start() @@ -2624,8 +2629,8 @@ def eval(self, code, **kwds): def reduce_load_Polymake(): - """ - Returns the polymake interface object defined in :mod:`sage.interfaces.polymake`. + r""" + Return the polymake interface object defined in :mod:`sage.interfaces.polymake`. EXAMPLES:: @@ -2645,3 +2650,4 @@ def reduce_load_Polymake(): polymake = polymake_jupymake else: polymake = polymake_expect + From 3150b21551b2b92d0f60df2ee2333145477297ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 19 May 2021 19:45:19 -0700 Subject: [PATCH 049/336] src/sage/interfaces/polymake.py: Fixes for python3 --- src/sage/interfaces/polymake.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index e40826c4b16..8e68d362d0b 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -32,7 +32,7 @@ from .expect import Expect from .interface import (Interface, InterfaceElement, InterfaceFunctionElement) - +from sage.cpython.string import bytes_to_str from sage.misc.verbose import get_verbose from sage.misc.cachefunc import cached_method from sage.interfaces.tab_completion import ExtraTabCompletion @@ -2176,27 +2176,27 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if if self._terminal_echo: out = E.before else: - out = E.before.rstrip('\n\r') + out = E.before.rstrip(b'\n\r') if self._terminal_echo and first: - i = out.find("\n") - j = out.rfind("\r") - out = out[i + 1:j].replace('\r\n', '\n') + i = out.find(b"\n") + j = out.rfind(b"\r") + out = out[i + 1:j].replace(b'\r\n', b'\n') else: - out = out.strip().replace('\r\n', '\n') + out = out.strip().replace(b'\r\n', b'\n') first = False if have_error: p_errors.append(out) have_error = False - out = "" + out = b"" elif have_warning: p_warnings.append(out) have_warning = False - out = "" + out = b"" elif have_log: if get_verbose() > 0: print(out) have_log = False - out = "" + out = b"" # 0: normal prompt # 1: continuation prompt # 2: user input expected when requestion "help" @@ -2268,7 +2268,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if sleep(0.1) raise RuntimeError("Polymake {}".format(_available_polymake_answers[pat])) else: - out = '' + out = b'' except KeyboardInterrupt: self._keyboard_interrupt() raise KeyboardInterrupt("Ctrl-c pressed while running {}".format(self)) @@ -2276,7 +2276,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if warnings.warn(w, RuntimeWarning) for e in p_errors: raise PolymakeError(e) - return out + return bytes_to_str(out) def application(self, app): """ @@ -2378,7 +2378,7 @@ def application(self, app): pexpect.EOF, # 7: unexpected end of the stream pexpect.TIMEOUT] # 8: timeout self._change_prompt(self._expect.compile_pattern_list(patterns)) - self._sendstr('application "{}";{}'.format(app, self._expect.linesep)) + self._sendstr('application "{}";{}'.format(app, os.linesep)) pat = self._expect.expect_list(self._prompt) if pat: raise RuntimeError("When changing the application, polymake unexpectedly {}".format(_available_polymake_answers[pat])) From 7a59b3d2fc12ce424a664d0e252ff0052f2336d1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 19 May 2021 20:21:15 -0700 Subject: [PATCH 050/336] src/sage/interfaces/polymake.py: More fixes for python3 --- src/sage/interfaces/polymake.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 8e68d362d0b..2b2b093c5cc 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -32,7 +32,7 @@ from .expect import Expect from .interface import (Interface, InterfaceElement, InterfaceFunctionElement) -from sage.cpython.string import bytes_to_str +from sage.cpython.string import bytes_to_str, str_to_bytes from sage.misc.verbose import get_verbose from sage.misc.cachefunc import cached_method from sage.interfaces.tab_completion import ExtraTabCompletion @@ -2231,12 +2231,12 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if warnings.warn("{} expects user interaction. We abort and return the options that {} provides.".format(self, self)) i = pat while i: - self._expect.send(chr(3)) + self._expect.sendline(chr(3)) sleep(0.1) i = self._expect.expect(self._prompt, timeout=0.1) # User interaction is expected to happen when requesting help if line.startswith('help'): - out = os.linesep.join(out.split(os.linesep)[:-1]) + out = str_to_bytes(os.linesep).join(out.split(str_to_bytes(os.linesep))[:-1]) break else: RuntimeError("Polymake unexpectedly {}".format(_available_polymake_answers[pat])) @@ -2273,9 +2273,9 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if self._keyboard_interrupt() raise KeyboardInterrupt("Ctrl-c pressed while running {}".format(self)) for w in p_warnings: - warnings.warn(w, RuntimeWarning) + warnings.warn(bytes_to_str(w), RuntimeWarning) for e in p_errors: - raise PolymakeError(e) + raise PolymakeError(bytes_to_str(e)) return bytes_to_str(out) def application(self, app): From 4b7616ff2fabc2e045518c886773f8eff84e32be Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 19 May 2021 20:21:42 -0700 Subject: [PATCH 051/336] src/sage/interfaces/polymake.py: Adjust user input prompt for polymake 4.4 --- src/sage/interfaces/polymake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 2b2b093c5cc..6b3b1078f24 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -2369,8 +2369,8 @@ def application(self, app): raise ValueError("Unknown polymake application '{}'".format(app)) self._application = app patterns = ["{} > ".format(app), # 0: normal prompt - r"{} \([0-9]+\)> ".format(app), # 1: continuation prompt - "Please choose {}".format(app), # 2: user input expected when requesting "help" + r"{} \([0-9]+\)> ".format(app), # 1: continuation prompt + "Please choose ", # 2: user input expected when requesting "help" "killed by signal", # 3: what we are looking for when interrupting a computation "polymake: +ERROR: +", # 4: error "polymake: +WARNING: +", # 5: warning From 332b72ffd16d9f5a539d7da4c14f6417e74781bf Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 19 May 2021 20:43:09 -0700 Subject: [PATCH 052/336] src/sage/interfaces/polymake.py: Disable minkowski_sum_fukuda test, which seems to take forever with polymake 4.4 --- src/sage/interfaces/polymake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 6b3b1078f24..65a0f0fc651 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1683,7 +1683,7 @@ def __call__(self, *args, **kwds): sage: p.get_schedule('"VERTICES"') # optional - polymake # random sensitivity check for VertexPerm cdd.convex_hull.canon: POINTED, RAYS, LINEALITY_SPACE : INPUT_RAYS - sage: p.minkowski_sum_fukuda(p).F_VECTOR # optional - polymake + sage: p.minkowski_sum_fukuda(p).F_VECTOR # optional - polymake # not tested 13 33 22 """ From 2e412207b6ddee89cfad94403b0e457583dc65c9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 20 May 2021 09:14:51 -0700 Subject: [PATCH 053/336] src/sage/interfaces/polymake.py: Relax version doctest --- src/sage/interfaces/polymake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 65a0f0fc651..1f65640fc22 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -173,8 +173,8 @@ def version(self): EXAMPLES:: - sage: polymake.version() # optional - polymake - '3...' + sage: polymake.version() # optional - polymake # random + '4...' TESTS:: From 5d5ce56b5ee101f80ae2c278d55514aa04913d9a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 20 May 2021 09:15:10 -0700 Subject: [PATCH 054/336] src/sage/interfaces/polymake.py: Another fix for python3 --- src/sage/interfaces/polymake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 1f65640fc22..ecb012c2113 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -2194,7 +2194,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if out = b"" elif have_log: if get_verbose() > 0: - print(out) + print(bytes_to_str(out)) have_log = False out = b"" # 0: normal prompt From 2426e6044e910d17c2432faedaee2c00b2dc43e0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 20 May 2021 09:48:20 -0700 Subject: [PATCH 055/336] src/sage/interfaces/polymake.py: Yet another fix for python3 --- src/sage/interfaces/polymake.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index ecb012c2113..4069e98d54e 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1929,7 +1929,7 @@ def _synchronize(self): sage: polymake('"foobar"') # optional - polymake ) failed: - PolymakeError: Can't locate object method "description" via package "1" + ...PolymakeError: Can't locate object method "description" via package "1" (perhaps you forgot to load "1"?)...> sage: Q.typeof() # optional - polymake # random ('foobar...', 'Polymake::polytope::Polytope__Rational') @@ -1952,7 +1952,7 @@ def _synchronize(self): return rnd = randrange(2147483647) res = str(rnd+1) - cmd = 'print 1+{};' + self._expect.linesep + cmd = 'print 1+{};' + os.linesep self._sendstr(cmd.format(rnd)) pat = self._expect.expect(self._prompt, timeout=0.5) # 0: normal prompt @@ -1974,7 +1974,7 @@ def _synchronize(self): elif pat == 0: # We got the right prompt, but perhaps in a wrong position in the stream # The result of the addition should appear *before* our prompt - if res not in self._expect.before: + if res not in bytes_to_str(self._expect.before): try: warnings.warn("{} seems out of sync: The expected output did not appear before reaching the next prompt.".format(self)) while True: From d1cbbf114d165235fe7895d83f6aa389dd19d7c6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 20 May 2021 09:49:05 -0700 Subject: [PATCH 056/336] src/sage/geometry/polyhedron/backend_polymake.py: Update for changes in sage --- src/sage/geometry/polyhedron/backend_polymake.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 964418d70aa..562294d3d88 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -97,7 +97,7 @@ class Polyhedron_polymake(Polyhedron_base): sage: V = polytopes.dodecahedron().vertices_list() sage: Polyhedron(vertices=V, backend='polymake') # optional - polymake - A 3-dimensional polyhedron in (Number Field in sqrt5 with defining polynomial x^2 - 5)^3 defined as the convex hull of 20 vertices + A 3-dimensional polyhedron in (Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^3 defined as the convex hull of 20 vertices TESTS: @@ -272,9 +272,9 @@ def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbo polymake_field = polymake(self.base_ring().fraction_field()) p = polymake.new_object("Polytope<{}>".format(polymake_field), CONE_AMBIENT_DIM=1+self.parent().ambient_dim(), - POINTS= [ [1] + v for v in vertices ] \ - + [ [0] + r for r in rays ], - INPUT_LINEALITY=[ [0] + l for l in lines ]) + POINTS= [ [1] + list(v) for v in vertices ] \ + + [ [0] + list(r) for r in rays ], + INPUT_LINEALITY=[ [0] + list(l) for l in lines ]) self._init_from_polymake_polytope(p) def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): From a265a71c8b8f0fa8b65fa2fcd3914eb3500c0093 Mon Sep 17 00:00:00 2001 From: Steven Trogdon Date: Sat, 22 May 2021 09:41:53 -0600 Subject: [PATCH 057/336] update ntl/spkg-config.m4 to use system ntl.pc, when available, to set NTL_LIBDIR --- build/pkgs/ntl/spkg-configure.m4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build/pkgs/ntl/spkg-configure.m4 b/build/pkgs/ntl/spkg-configure.m4 index 48e41de6ea7..5f83cfaac43 100644 --- a/build/pkgs/ntl/spkg-configure.m4 +++ b/build/pkgs/ntl/spkg-configure.m4 @@ -44,7 +44,10 @@ SAGE_SPKG_CONFIGURE([ntl], [ ntl_inc_dir=`AS_DIRNAME(["$ntl_inc_ntl_dir"])` ntl_prefix=`AS_DIRNAME(["$ntl_inc_dir"])` AC_SUBST(NTL_INCDIR, [$ntl_inc_dir]) - AC_SUBST(NTL_LIBDIR, [$ntl_prefix/lib]) + PKG_CHECK_VAR([ntl_libdir], [ntl], [libdir]) + AS_IF([test "x$ntl_libdir" = x], [AC_SUBST(NTL_LIBDIR, [$ntl_prefix/lib])], + [test "x$ntl_libdir" != x], [AC_SUBST(NTL_LIBDIR, [$ntl_prefix/[$(basename $ntl_libdir)]])] + ) fi ]) From 426385df211816ce6f5d7bcf00d64a64b5d98f66 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 23 May 2021 12:42:46 -0700 Subject: [PATCH 058/336] Polyhedra: Raise an error if backend 'polymake' is used with an unsupported field --- src/sage/geometry/polyhedron/parent.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 57f3a349bfc..c9b4a0f91b3 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -104,6 +104,10 @@ def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, * Traceback (most recent call last): ... ValueError: invalid base ring: Number Field in I with defining polynomial x^2 + 1 with I = 1*I cannot be coerced to a real field + sage: Polyhedra(AA, 3, backend='polymake') # optional - polymake + Traceback (most recent call last): + ... + ValueError: the 'polymake' backend for polyhedron cannot be used with Algebraic Real Field """ if ambient_space_or_base_ring is not None: @@ -157,7 +161,13 @@ def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, * elif backend == 'cdd' and base_ring is RDF: return Polyhedra_RDF_cdd(RDF, ambient_dim, backend) elif backend == 'polymake': - return Polyhedra_polymake(base_ring.fraction_field(), ambient_dim, backend) + base_field = base_ring.fraction_field() + try: + from sage.interfaces.polymake import polymake + polymake_base_field = polymake(base_field) + except TypeError: + raise ValueError(f"the 'polymake' backend for polyhedron cannot be used with {base_field}") + return Polyhedra_polymake(base_field, ambient_dim, backend) elif backend == 'field': if not base_ring.is_exact(): raise ValueError("the 'field' backend for polyhedron cannot be used with non-exact fields") From 29c267bfb73c13c2844569d8d7fb5ae7e4879f99 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 23 May 2021 13:24:13 -0700 Subject: [PATCH 059/336] Polyhedron_polymake: Implement pickling --- .../geometry/polyhedron/backend_polymake.py | 188 ++++++++++++++++-- 1 file changed, 167 insertions(+), 21 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 562294d3d88..6cee542c913 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -57,7 +57,7 @@ class Polyhedron_polymake(Polyhedron_base): sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)], rays=[(1,1)], # optional - polymake ....: lines=[], backend='polymake') - sage: TestSuite(p).run(skip='_test_pickling') # optional - polymake + sage: TestSuite(p).run() # optional - polymake A lower-dimensional affine cone; we test that there are no mysterious inequalities coming in from the homogenization:: @@ -211,13 +211,13 @@ def __init__(self, parent, Vrep, Hrep, polymake_polytope=None, **kwds): not implemented:: sage: p = Polyhedron(backend='polymake') # optional - polymake - sage: TestSuite(p).run(skip="_test_pickling") # optional - polymake + sage: TestSuite(p).run() # optional - polymake sage: p = Polyhedron(vertices=[(1, 1)], rays=[(0, 1)], # optional - polymake ....: backend='polymake') - sage: TestSuite(p).run(skip="_test_pickling") # optional - polymake + sage: TestSuite(p).run() # optional - polymake sage: p = Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)], # optional - polymake ....: backend='polymake') - sage: TestSuite(p).run(skip="_test_pickling") # optional - polymake + sage: TestSuite(p).run() # optional - polymake """ if polymake_polytope is not None: if Hrep is not None or Vrep is not None: @@ -247,7 +247,7 @@ def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbo INPUT: - - ``vertices`` -- list of point; each point can be specified + - ``vertices`` -- list of points; each point can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements @@ -269,14 +269,35 @@ def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbo sage: Polyhedron_polymake._init_from_Vrepresentation(p, [], [], []) # optional - polymake """ from sage.interfaces.polymake import polymake + data = self._polymake_Vrepresentation_data(vertices, rays, lines) polymake_field = polymake(self.base_ring().fraction_field()) - p = polymake.new_object("Polytope<{}>".format(polymake_field), - CONE_AMBIENT_DIM=1+self.parent().ambient_dim(), - POINTS= [ [1] + list(v) for v in vertices ] \ - + [ [0] + list(r) for r in rays ], - INPUT_LINEALITY=[ [0] + list(l) for l in lines ]) + p = polymake.new_object("Polytope<{}>".format(polymake_field), **data) self._init_from_polymake_polytope(p) + def _polymake_Vrepresentation_data(self, vertices, rays, lines): + r""" + Compute a dictionary with V-representation input for a polymake Polytope object. + + INPUT: + + - ``vertices`` -- list of points; each point can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements + + - ``rays`` -- list of rays; each ray can be specified as any + iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements + + - ``lines`` -- list of lines; each line can be specified as + any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements + + """ + return dict(CONE_AMBIENT_DIM=1+self.parent().ambient_dim(), + POINTS=( [ [1] + list(v) for v in vertices ] + + [ [0] + list(r) for r in rays ]), + INPUT_LINEALITY=[ [0] + list(l) for l in lines ]) + def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): r""" Construct polyhedron from H-representation data. @@ -303,26 +324,45 @@ def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): sage: Polyhedron_polymake._init_from_Hrepresentation(p, [], []) # optional - polymake """ from sage.interfaces.polymake import polymake - if ieqs is None: ieqs = [] - if eqns is None: eqns = [] + data = self._polymake_Hrepresentation_data(ieqs, eqns) + polymake_field = polymake(self.base_ring().fraction_field()) + p = polymake.new_object("Polytope<{}>".format(polymake_field), **data) + self._init_from_polymake_polytope(p) + + def _polymake_Hrepresentation_data(self, ieqs, eqns): + r""" + Compute a dictionary with H-representation input for a polymake Polytope object. + + INPUT: + + - ``ieqs`` -- list of inequalities; each line can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements + + - ``eqns`` -- list of equalities; each line can be specified + as any iterable container of + :meth:`~sage.geometry.polyhedron.base.base_ring` elements + + """ + if ieqs is None: + ieqs = [] + if eqns is None: + eqns = [] # Polymake 3.0r2 and 3.1 crash with a segfault for a test case # using QuadraticExtension, when some all-zero inequalities are input. # https://forum.polymake.org/viewtopic.php?f=8&t=547 # Filter them out. - ieqs = [ v for v in ieqs if not all(self._is_zero(x) for x in v) ] + ieqs = [ list(v) for v in ieqs if not all(self._is_zero(x) for x in v) ] # We do a similar filtering for equations. # Since Polymake 3.2, we can not give all zero vectors in equations - eqns = [ v for v in eqns if not all(self._is_zero(x) for x in v) ] + eqns = [ list(v) for v in eqns if not all(self._is_zero(x) for x in v) ] if not ieqs: # Put in one trivial (all-zero) inequality. This is so that # the ambient dimension is set correctly. # Since Polymake 3.2, the constant should not be zero. ieqs.append([1] + [0]*self.ambient_dim()) - polymake_field = polymake(self.base_ring().fraction_field()) - p = polymake.new_object("Polytope<{}>".format(polymake_field), - EQUATIONS=eqns, - INEQUALITIES=ieqs) - self._init_from_polymake_polytope(p) + return dict(EQUATIONS=eqns, + INEQUALITIES=ieqs) def _init_Vrepresentation_from_polymake(self): r""" @@ -426,6 +466,112 @@ def _polymake_(self, polymake): else: return super(Polyhedron_polymake, self)._polymake_(polymake) + def __getstate__(self): + r""" + Remove the polymake polytope object for pickling. + + TESTS:: + + sage: P = polytopes.simplex(backend='polymake') # optional - polymake + sage: P.__getstate__() # optional - polymake + (Polyhedra in QQ^4, + {'_Hrepresentation': (An inequality (0, -1, -1, -1) x + 1 >= 0, + An inequality (0, 1, 0, 0) x + 0 >= 0, + An inequality (0, 0, 1, 0) x + 0 >= 0, + An inequality (0, 0, 0, 1) x + 0 >= 0, + An equation (1, 1, 1, 1) x - 1 == 0), + '_Vrepresentation': (A vertex at (1, 0, 0, 0), + A vertex at (0, 1, 0, 0), + A vertex at (0, 0, 1, 0), + A vertex at (0, 0, 0, 1)), + '_pickle_equations': [(-1, 1, 1, 1, 1)], + '_pickle_inequalities': [(1, 0, -1, -1, -1), + (0, 0, 1, 0, 0), + (0, 0, 0, 1, 0), + (0, 0, 0, 0, 1)], + '_pickle_lines': [], + '_pickle_rays': [], + '_pickle_vertices': [(1, 0, 0, 0), + (0, 1, 0, 0), + (0, 0, 1, 0), + (0, 0, 0, 1)]}) + """ + state = super().__getstate__() + state = (state[0], state[1].copy()) + # Remove the unpicklable entries. + if '_polymake_polytope' in state[1]: + del state[1]['_polymake_polytope'] + state[1]["_pickle_vertices"] = [v._vector for v in self.vertices()] + state[1]["_pickle_rays"] = [v._vector for v in self.rays()] + state[1]["_pickle_lines"] = [v._vector for v in self.lines()] + state[1]["_pickle_inequalities"] = [v._vector for v in self.inequalities()] + state[1]["_pickle_equations"] = [v._vector for v in self.equations()] + return state + + def __setstate__(self, state): + r""" + Initialize the polymake polytope object after pickling. + + TESTS: + + Test that the obtained cone is valid:: + + sage: from sage.geometry.polyhedron.backend_polymake import Polyhedron_polymake # optional - polymake + sage: P = polytopes.permutahedron(4, backend='polymake') # optional - polymake + sage: P1 = loads(dumps(P)) # optional - polymake + sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake + sage: P2 == P # optional - polymake + True + + sage: P = Polyhedron(lines=[[1,0], [0,1]], backend='polymake') # optional - polymake + sage: P1 = loads(dumps(P)) # optional - polymake + sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake + sage: P2 == P # optional - polymake + True + + sage: P = Polyhedron(backend='polymake') # optional - polymake + sage: P1 = loads(dumps(P)) # optional - polymake + sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake + sage: P2 == P # optional - polymake + True + + sage: P = polytopes.permutahedron(4, backend='polymake') * Polyhedron(lines=[[1]], backend='polymake') # optional - polymake + sage: P1 = loads(dumps(P)) # optional - polymake + sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake + sage: P2 == P # optional - polymake + True + + sage: print("Possible output"); P = polytopes.dodecahedron(backend='polymake') # optional - polymake + Possible output... + sage: P1 = loads(dumps(P)) # optional - polymake + sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake + sage: P == P2 # optional - polymake + True + + """ + if "_pickle_vertices" in state[1]: + vertices = state[1].pop("_pickle_vertices") + rays = state[1].pop("_pickle_rays") + lines = state[1].pop("_pickle_lines") + inequalities = state[1].pop("_pickle_inequalities") + equations = state[1].pop("_pickle_equations") + else: + vertices = None + + super().__setstate__(state) + + if vertices is None: + vertices = self.vertices() + rays = self.rays() + lines = self.lines() + inequalities = self.inequalities() + equations = self.equations() + + from sage.interfaces.polymake import polymake + data = self._polymake_Vrepresentation_data(vertices, rays, lines) + data.update(self._polymake_Hrepresentation_data(inequalities, equations)) + polymake_field = polymake(self.base_ring().fraction_field()) + self._polymake_polytope = polymake.new_object("Polytope<{}>".format(polymake_field), **data) ######################################################################### class Polyhedron_QQ_polymake(Polyhedron_polymake, Polyhedron_QQ): @@ -442,7 +588,7 @@ class Polyhedron_QQ_polymake(Polyhedron_polymake, Polyhedron_QQ): sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)], # optional - polymake ....: rays=[(1,1)], lines=[], ....: backend='polymake', base_ring=QQ) - sage: TestSuite(p).run(skip='_test_pickling') # optional - polymake + sage: TestSuite(p).run() # optional - polymake """ pass @@ -462,7 +608,7 @@ class Polyhedron_ZZ_polymake(Polyhedron_polymake, Polyhedron_ZZ): sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)], # optional - polymake ....: rays=[(1,1)], lines=[], ....: backend='polymake', base_ring=ZZ) - sage: TestSuite(p).run(skip='_test_pickling') # optional - polymake + sage: TestSuite(p).run() # optional - polymake """ pass From b4366f131faa5f88ea943f0c5d1aa3d37e9d9f14 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 23 May 2021 14:30:03 -0700 Subject: [PATCH 060/336] PolymakeElement._sage_, Polyhedron_polymake._from_polymake_polytope: Determine the base ring --- .../geometry/polyhedron/backend_polymake.py | 20 +++++++++++++++++++ src/sage/interfaces/polymake.py | 12 +---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 6cee542c913..8195cfdeaa4 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -22,6 +22,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +import itertools from sage.structure.element import Element @@ -447,6 +448,25 @@ def _from_polymake_polytope(cls, parent, polymake_polytope): sage: parent = Polyhedra(QQ, 2, backend='polymake') # optional - polymake sage: Q=Polyhedron_polymake._from_polymake_polytope(parent, PP) # optional - polymake """ + if parent is None: + from .parent import Polyhedra + from sage.rings.rational_field import QQ + from sage.rings.qqbar import AA + if polymake_polytope.typeof()[0] == 'Polymake::polytope::Polytope__Rational': + base_ring = QQ + else: + from sage.structure.element import coercion_model + data = [g.sage() + for g in itertools.chain(polymake_polytope.VERTICES, + polymake_polytope.LINEALITY_SPACE, + polymake_polytope.FACETS, + polymake_polytope.AFFINE_HULL)] + if data: + base_ring = coercion_model.common_parent(*data).base_ring() + else: + base_ring = QQ + ambient_dim = polymake_polytope.AMBIENT_DIM() + parent = Polyhedra(base_ring=base_ring, ambient_dim=ambient_dim, backend='polymake') return cls(parent, None, None, polymake_polytope=polymake_polytope) def _polymake_(self, polymake): diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 4069e98d54e..66afe4193ba 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1533,17 +1533,7 @@ def _sage_(self): return matrix([x.sage() for x in self]) elif T1 == 'Polytope': from sage.geometry.polyhedron.backend_polymake import Polyhedron_polymake - from sage.geometry.polyhedron.parent import Polyhedra - from sage.rings.rational_field import QQ - from sage.rings.qqbar import AA - if self.typeof()[0] == 'Polymake::polytope::Polytope__Rational': - base_ring = QQ - else: - # We could try to find out a more specific field. - base_ring = AA - ambient_dim = self.AMBIENT_DIM() - parent = Polyhedra(base_ring, ambient_dim, backend='polymake') - return Polyhedron_polymake._from_polymake_polytope(parent, self) + return Polyhedron_polymake._from_polymake_polytope(None, self) else: return super(PolymakeElement, self)._sage_() From 3cb688857f390f5739b6ef14bdb3b97b4c597a7f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 23 May 2021 16:39:22 -0700 Subject: [PATCH 061/336] PolymakeElement: Adjust doctest --- src/sage/interfaces/polymake.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 66afe4193ba..f5891cc0cf6 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1502,7 +1502,9 @@ def _sage_(self): sage: polymake.cube(3).sage() # optional - polymake A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices sage: polymake.icosahedron().sage() # optional - polymake - A 3-dimensional polyhedron in AA^3 defined as the convex hull of 12 vertices + A 3-dimensional polyhedron in + (Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?)^3 + defined as the convex hull of 12 vertices """ T1, T2 = self.typeof() From 89614bbfdb4d8aa7ccd9dd11f091ceed97ca34ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 23 May 2021 16:41:47 -0700 Subject: [PATCH 062/336] build/pkgs/polymake/SPKG.rst: Recommend installing jupymake --- build/pkgs/polymake/SPKG.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/build/pkgs/polymake/SPKG.rst b/build/pkgs/polymake/SPKG.rst index c8ab646b67a..3e3dead1fcd 100644 --- a/build/pkgs/polymake/SPKG.rst +++ b/build/pkgs/polymake/SPKG.rst @@ -51,7 +51,7 @@ you will need the local::lib Perl module installed:: cpan -i XML::Writer XML::LibXML XML::LibXSLT File::Slurp Term::ReadLine::Gnu JSON SVG MongoDB Several Sage packages should be installed before installing the polymake -package to give a more featureful Polymake installation: +package to give a more featureful Polymake installation:: sage -i 4ti2 latte_int topcom qhull @@ -65,6 +65,13 @@ Information on missing Polymake prerequisites after installing polymake:: (sage-sh) $ polymake polytope> show_unconfigured; +It is strongly recommended to also install JuPyMake:: + + sage -i jupymake + +When JuPyMake is present, Sage is able to use a more robust interface +to Polymake. + Debugging polymake install problems ----------------------------------- From 9b3e7bd259d30308b6fee23c54c4ffe0a895f88c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 23 May 2021 17:00:43 -0700 Subject: [PATCH 063/336] src/sage/interfaces/polymake.py: Deprecate PolymakeExpect --- src/sage/interfaces/polymake.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index f5891cc0cf6..51dbaf6cf6a 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -119,13 +119,18 @@ class PolymakeAbstract(ExtraTabCompletion, Interface): We test the verbosity management with very early doctests because messages will not be repeated. - Testing the Pexpect interface:: + Testing the deprecated pexpect-based interface:: sage: type(polymake_expect) <...sage.interfaces.polymake.PolymakeExpect... sage: isinstance(polymake_expect, PolymakeAbstract) True sage: p = polymake_expect.rand_sphere(4, 20, seed=5) # optional - polymake + doctest...: DeprecationWarning: the pexpect-based interface to + polymake is deprecated. + Install package jupymake so that Sage can use the more robust + jupymake-based interface to polymake + See https://trac.sagemath.org/27745 for details. sage: p # optional - polymake Random spherical polytope of dimension 4; seed=5... sage: set_verbose(3) @@ -1829,6 +1834,11 @@ def _start(self, alt_message=None): True sage: polymake.quit() # optional - polymake sage: polymake._start() # optional - polymake + doctest...: DeprecationWarning: the pexpect-based interface to + polymake is deprecated. + Install package jupymake so that Sage can use the more robust + jupymake-based interface to polymake + See https://trac.sagemath.org/27745 for details. Since 'normal_fan' is not defined in the polymake application 'polytope', we now get:: @@ -1837,6 +1847,9 @@ def _start(self, alt_message=None): False """ + from sage.misc.superseded import deprecation + deprecation(27745, "the pexpect-based interface to polymake is deprecated. Install package jupymake so that Sage can use the more robust jupymake-based interface to polymake") + if not self.is_running(): self._change_prompt("polytope > ") Expect._start(self, alt_message=None) From 1f79230d95a09b0ad9c0407d25b4837230b8bab5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 24 May 2021 16:17:55 -0700 Subject: [PATCH 064/336] src/sage/geometry/polyhedron/backend_polymake.py: Skip the failing TestSuite test _test_lawrence --- src/sage/geometry/polyhedron/backend_polymake.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 8195cfdeaa4..49c5011ae26 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -208,17 +208,18 @@ def __init__(self, parent, Vrep, Hrep, polymake_polytope=None, **kwds): TESTS: - We skip the pickling test because pickling is currently - not implemented:: - sage: p = Polyhedron(backend='polymake') # optional - polymake sage: TestSuite(p).run() # optional - polymake sage: p = Polyhedron(vertices=[(1, 1)], rays=[(0, 1)], # optional - polymake ....: backend='polymake') sage: TestSuite(p).run() # optional - polymake + + We skip the Lawrence test because it involves numerically unstable + floating point arithmetic:: + sage: p = Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)], # optional - polymake ....: backend='polymake') - sage: TestSuite(p).run() # optional - polymake + sage: TestSuite(p).run(skip='_test_lawrence') # optional - polymake """ if polymake_polytope is not None: if Hrep is not None or Vrep is not None: From b81dc850448e6a3538d0038f556270401dc49845 Mon Sep 17 00:00:00 2001 From: Steven Trogdon Date: Tue, 25 May 2021 09:10:06 -0600 Subject: [PATCH 065/336] Remove extra // in definition of NTL_LIBDIR --- build/pkgs/ntl/spkg-configure.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/ntl/spkg-configure.m4 b/build/pkgs/ntl/spkg-configure.m4 index 5f83cfaac43..371cf008d69 100644 --- a/build/pkgs/ntl/spkg-configure.m4 +++ b/build/pkgs/ntl/spkg-configure.m4 @@ -46,7 +46,7 @@ SAGE_SPKG_CONFIGURE([ntl], [ AC_SUBST(NTL_INCDIR, [$ntl_inc_dir]) PKG_CHECK_VAR([ntl_libdir], [ntl], [libdir]) AS_IF([test "x$ntl_libdir" = x], [AC_SUBST(NTL_LIBDIR, [$ntl_prefix/lib])], - [test "x$ntl_libdir" != x], [AC_SUBST(NTL_LIBDIR, [$ntl_prefix/[$(basename $ntl_libdir)]])] + [test "x$ntl_libdir" != x], [AC_SUBST(NTL_LIBDIR, [$ntl_libdir])] ) fi ]) From 975ff8659afdb7ac5d30c377391396685ab8c67a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 25 May 2021 09:20:15 -0700 Subject: [PATCH 066/336] build/pkgs/suitesparse: Update to 5.10.1 --- build/pkgs/suitesparse/checksums.ini | 7 ++++--- build/pkgs/suitesparse/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/suitesparse/checksums.ini b/build/pkgs/suitesparse/checksums.ini index 9516ea039ee..fa6fbb6f9a4 100644 --- a/build/pkgs/suitesparse/checksums.ini +++ b/build/pkgs/suitesparse/checksums.ini @@ -1,4 +1,5 @@ tarball=SuiteSparse-VERSION.tar.gz -sha1=3de08b5ab02610ed0446225aad2445696616fae5 -md5=af8b97cbded4cd5c6672e878bc0c37c2 -cksum=3062366378 +sha1=83dd96b32701e12b7577acb7d9aea80138d7e46e +md5=68bb912f3cf3d2b01f30ebafef690302 +cksum=178881779 +upstream_url=https://github.com/DrTimothyAldenDavis/SuiteSparse/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/suitesparse/package-version.txt b/build/pkgs/suitesparse/package-version.txt index 1bc788d3b6d..4e32c7b1caf 100644 --- a/build/pkgs/suitesparse/package-version.txt +++ b/build/pkgs/suitesparse/package-version.txt @@ -1 +1 @@ -5.6.0 +5.10.1 From ba0a4795da6ebee8cf9db55116df6afe737466e2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 25 May 2021 23:14:29 -0700 Subject: [PATCH 067/336] src/sage/interfaces/polymake.py: Mark doctests specific to the deprecated pexpect-based interface as optional - polymake_expect --- src/sage/interfaces/polymake.py | 88 ++++++++++++++++----------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 51dbaf6cf6a..4e0dd05c6f7 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -125,21 +125,21 @@ class PolymakeAbstract(ExtraTabCompletion, Interface): <...sage.interfaces.polymake.PolymakeExpect... sage: isinstance(polymake_expect, PolymakeAbstract) True - sage: p = polymake_expect.rand_sphere(4, 20, seed=5) # optional - polymake + sage: p = polymake_expect.rand_sphere(4, 20, seed=5) # optional - polymake_expect doctest...: DeprecationWarning: the pexpect-based interface to polymake is deprecated. Install package jupymake so that Sage can use the more robust jupymake-based interface to polymake See https://trac.sagemath.org/27745 for details. - sage: p # optional - polymake + sage: p # optional - polymake_expect Random spherical polytope of dimension 4; seed=5... sage: set_verbose(3) - sage: p.H_VECTOR # optional - polymake + sage: p.H_VECTOR # optional - polymake_expect used package ppl The Parma Polyhedra Library ... 1 16 40 16 1 sage: set_verbose(0) - sage: p.F_VECTOR # optional - polymake + sage: p.F_VECTOR # optional - polymake_expect 20 94 148 74 Testing the JuPyMake interface:: @@ -1765,19 +1765,19 @@ class PolymakeExpect(PolymakeAbstract, Expect): sage: from sage.interfaces.polymake import polymake_expect as polymake sage: type(polymake) <...sage.interfaces.polymake.PolymakeExpect... - sage: p = polymake.rand_sphere(4, 20, seed=5) # optional - polymake - sage: p # optional - polymake + sage: p = polymake.rand_sphere(4, 20, seed=5) # optional - polymake_expect + sage: p # optional - polymake_expect Random spherical polytope of dimension 4; seed=5... sage: set_verbose(3) - sage: p.H_VECTOR; # optional - polymake # random + sage: p.H_VECTOR; # optional - polymake_expect # random used package ppl The Parma Polyhedra Library ... - sage: p.H_VECTOR # optional - polymake + sage: p.H_VECTOR # optional - polymake_expect 1 16 40 16 1 sage: set_verbose(0) - sage: p.F_VECTOR # optional - polymake + sage: p.F_VECTOR # optional - polymake_expect 20 94 148 74 - sage: print(p.F_VECTOR._sage_doc_()) # optional - polymake # random + sage: print(p.F_VECTOR._sage_doc_()) # optional - polymake_expect # random property_types/Algebraic Types/Vector: A type for vectors with entries of type Element. @@ -1829,11 +1829,11 @@ def _start(self, alt_message=None): TESTS:: sage: from sage.interfaces.polymake import polymake_expect as polymake - sage: polymake.application('fan') # optional - polymake - sage: 'normal_fan' in dir(polymake) # optional - polymake + sage: polymake.application('fan') # optional - polymake_expect + sage: 'normal_fan' in dir(polymake) # optional - polymake_expect True - sage: polymake.quit() # optional - polymake - sage: polymake._start() # optional - polymake + sage: polymake.quit() # optional - polymake_expect + sage: polymake._start() # optional - polymake_expect doctest...: DeprecationWarning: the pexpect-based interface to polymake is deprecated. Install package jupymake so that Sage can use the more robust @@ -1843,7 +1843,7 @@ def _start(self, alt_message=None): Since 'normal_fan' is not defined in the polymake application 'polytope', we now get:: - sage: 'normal_fan' in dir(polymake) # optional - polymake + sage: 'normal_fan' in dir(polymake) # optional - polymake_expect False """ @@ -1876,7 +1876,7 @@ def _keyboard_interrupt(self): is very flaky. Therefore, this test is marked as "not tested". :: sage: from sage.interfaces.polymake import polymake_expect as polymake - sage: c = polymake.cube(15) # optional - polymake + sage: c = polymake.cube(15) # optional - polymake_expect sage: alarm(1) # not tested sage: try: # not tested # indirect doctest ....: c.F_VECTOR @@ -1892,7 +1892,7 @@ def _keyboard_interrupt(self): Afterwards, the interface should still be running. :: - sage: c.N_FACETS # optional - polymake + sage: c.N_FACETS # optional - polymake_expect 30 """ @@ -1924,32 +1924,32 @@ def _synchronize(self): TESTS:: sage: from sage.interfaces.polymake import polymake_expect as polymake - sage: Q = polymake.cube(4) # optional - polymake - sage: polymake('"ok"') # optional - polymake + sage: Q = polymake.cube(4) # optional - polymake_expect + sage: polymake('"ok"') # optional - polymake_expect ok - sage: polymake._expect.sendline() # optional - polymake + sage: polymake._expect.sendline() # optional - polymake_expect 1 Now the interface is badly out of sync:: - sage: polymake('"foobar"') # optional - polymake + sage: polymake('"foobar"') # optional - polymake_expect ) failed: ...PolymakeError: Can't locate object method "description" via package "1" (perhaps you forgot to load "1"?)...> - sage: Q.typeof() # optional - polymake # random + sage: Q.typeof() # optional - polymake_expect # random ('foobar...', 'Polymake::polytope::Polytope__Rational') - sage: Q.typeof.clear_cache() # optional - polymake + sage: Q.typeof.clear_cache() # optional - polymake_expect After synchronisation, things work again as expected:: - sage: polymake._synchronize() # optional - polymake + sage: polymake._synchronize() # optional - polymake_expect doctest:warning ... UserWarning: Polymake seems out of sync: The expected output did not appear before reaching the next prompt. - sage: polymake('"back to normal"') # optional - polymake + sage: polymake('"back to normal"') # optional - polymake_expect back to normal - sage: Q.typeof() # optional - polymake + sage: Q.typeof() # optional - polymake_expect ('Polymake::polytope::Polytope__Rational', 'ARRAY') """ @@ -2029,13 +2029,13 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if EXAMPLES:: sage: from sage.interfaces.polymake import polymake_expect as polymake - sage: p = polymake.cube(3) # optional - polymake # indirect doctest + sage: p = polymake.cube(3) # optional - polymake_expect # indirect doctest Here we see that remarks printed by polymake are displayed if the verbosity is positive:: sage: set_verbose(1) - sage: p.N_LATTICE_POINTS # optional - polymake # random + sage: p.N_LATTICE_POINTS # optional - polymake_expect # random used package latte LattE (Lattice point Enumeration) is a computer software dedicated to the problems of counting lattice points and integration inside convex polytopes. @@ -2300,20 +2300,20 @@ def application(self, app): and finds that the polytope is very ample:: sage: from sage.interfaces.polymake import polymake_expect as polymake - sage: q = polymake.new_object("Polytope", INEQUALITIES=[[5,-4,0,1],[-3,0,-4,1],[-2,1,0,0],[-4,4,4,-1],[0,0,1,0],[8,0,0,-1],[1,0,-1,0],[3,-1,0,0]]) # optional - polymake - sage: q.H_VECTOR # optional - polymake + sage: q = polymake.new_object("Polytope", INEQUALITIES=[[5,-4,0,1],[-3,0,-4,1],[-2,1,0,0],[-4,4,4,-1],[0,0,1,0],[8,0,0,-1],[1,0,-1,0],[3,-1,0,0]]) # optional - polymake_expect + sage: q.H_VECTOR # optional - polymake_expect 1 5 5 1 - sage: q.F_VECTOR # optional - polymake + sage: q.F_VECTOR # optional - polymake_expect 8 14 8 - sage: q.VERY_AMPLE # optional - polymake + sage: q.VERY_AMPLE # optional - polymake_expect true In the application 'fan', polymake can now compute the normal fan of `q` and its (primitive) rays:: - sage: polymake.application('fan') # optional - polymake - sage: g = q.normal_fan() # optional - polymake - sage: g.RAYS # optional - polymake + sage: polymake.application('fan') # optional - polymake_expect + sage: g = q.normal_fan() # optional - polymake_expect + sage: g.RAYS # optional - polymake_expect -1 0 1/4 0 -1 1/4 1 0 0 @@ -2322,7 +2322,7 @@ def application(self, app): 0 0 -1 0 -1 0 -1 0 0 - sage: g.RAYS.primitive() # optional - polymake + sage: g.RAYS.primitive() # optional - polymake_expect -4 0 1 0 -4 1 1 0 0 @@ -2341,20 +2341,20 @@ def application(self, app): but only in 'tropical', the following shows the effect of changing the application. :: - sage: polymake.application('polytope') # optional - polymake - sage: 'trop_witness' in dir(polymake) # optional - polymake + sage: polymake.application('polytope') # optional - polymake_expect + sage: 'trop_witness' in dir(polymake) # optional - polymake_expect False - sage: polymake.application('tropical') # optional - polymake - sage: 'trop_witness' in dir(polymake) # optional - polymake + sage: polymake.application('tropical') # optional - polymake_expect + sage: 'trop_witness' in dir(polymake) # optional - polymake_expect True - sage: polymake.application('polytope') # optional - polymake - sage: 'trop_witness' in dir(polymake) # optional - polymake + sage: polymake.application('polytope') # optional - polymake_expect + sage: 'trop_witness' in dir(polymake) # optional - polymake_expect False For completeness, we show what happens when asking for an application that doesn't exist:: - sage: polymake.application('killerapp') # optional - polymake + sage: polymake.application('killerapp') # optional - polymake_expect Traceback (most recent call last): ... ValueError: Unknown polymake application 'killerapp' @@ -2362,7 +2362,7 @@ def application(self, app): Of course, a different error results when we send an explicit command in polymake to change to an unknown application:: - sage: polymake.eval('application "killerapp";') # optional - polymake + sage: polymake.eval('application "killerapp";') # optional - polymake_expect Traceback (most recent call last): ... PolymakeError: Unknown application killerapp From 609e08d347c9f7b73abc82705c1ce6179ce50c5d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 09:19:34 +0200 Subject: [PATCH 068/336] more meaningful error message when pexpect-base interface fails to start --- src/sage/interfaces/polymake.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 51dbaf6cf6a..5c49701725a 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1851,8 +1851,11 @@ def _start(self, alt_message=None): deprecation(27745, "the pexpect-based interface to polymake is deprecated. Install package jupymake so that Sage can use the more robust jupymake-based interface to polymake") if not self.is_running(): - self._change_prompt("polytope > ") - Expect._start(self, alt_message=None) + try: + self._change_prompt("polytope > ") + Expect._start(self, alt_message=None) + except RuntimeError: + raise RuntimeError("runtime error with deprecated pexpect-base interface to polymake; please install jupymake") PolymakeAbstract._start(self) self.eval('use File::Slurp;') From e664be475bd24a0ab5a4bd80bd62de2ee2397c89 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 10:12:02 +0200 Subject: [PATCH 069/336] recreate the polymake polytope unconditionally --- .../geometry/polyhedron/backend_polymake.py | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 49c5011ae26..564082df542 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ The polymake backend for polyhedral computations @@ -276,7 +276,7 @@ def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbo p = polymake.new_object("Polytope<{}>".format(polymake_field), **data) self._init_from_polymake_polytope(p) - def _polymake_Vrepresentation_data(self, vertices, rays, lines): + def _polymake_Vrepresentation_data(self, vertices, rays, lines, minimal=False): r""" Compute a dictionary with V-representation input for a polymake Polytope object. @@ -294,11 +294,24 @@ def _polymake_Vrepresentation_data(self, vertices, rays, lines): any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements + - ``minimal`` -- boolean (default: ``False``); whether the input is already minimal + + .. WARNING:: + + If ``minimal`` the representation is assumed to be correct. + It is not checked. """ - return dict(CONE_AMBIENT_DIM=1+self.parent().ambient_dim(), - POINTS=( [ [1] + list(v) for v in vertices ] - + [ [0] + list(r) for r in rays ]), - INPUT_LINEALITY=[ [0] + list(l) for l in lines ]) + if not minimal: + return dict(CONE_AMBIENT_DIM=1+self.parent().ambient_dim(), + POINTS=( [ [1] + list(v) for v in vertices ] + + [ [0] + list(r) for r in rays ]), + INPUT_LINEALITY=[ [0] + list(l) for l in lines ]) + else: + return dict(CONE_AMBIENT_DIM=1+self.parent().ambient_dim(), + VERTICES=( [ [1] + list(v) for v in vertices ] + + [ [0] + list(r) for r in rays ]), + LINEALITY_SPACE=[ [0] + list(l) for l in lines ]) + def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): r""" @@ -331,7 +344,7 @@ def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): p = polymake.new_object("Polytope<{}>".format(polymake_field), **data) self._init_from_polymake_polytope(p) - def _polymake_Hrepresentation_data(self, ieqs, eqns): + def _polymake_Hrepresentation_data(self, ieqs, eqns, minimal=False): r""" Compute a dictionary with H-representation input for a polymake Polytope object. @@ -345,6 +358,12 @@ def _polymake_Hrepresentation_data(self, ieqs, eqns): as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements + - ``minimal`` -- boolean (default: ``False``); whether the input is already minimal + + .. WARNING:: + + If ``minimal`` the representation is assumed to be correct. + It is not checked. """ if ieqs is None: ieqs = [] @@ -363,8 +382,12 @@ def _polymake_Hrepresentation_data(self, ieqs, eqns): # the ambient dimension is set correctly. # Since Polymake 3.2, the constant should not be zero. ieqs.append([1] + [0]*self.ambient_dim()) - return dict(EQUATIONS=eqns, - INEQUALITIES=ieqs) + if not minimal: + return dict(EQUATIONS=eqns, + INEQUALITIES=ieqs) + else: + return dict(AFFINE_HULL=eqns, + FACETS=ieqs) def _init_Vrepresentation_from_polymake(self): r""" @@ -589,8 +612,8 @@ def __setstate__(self, state): equations = self.equations() from sage.interfaces.polymake import polymake - data = self._polymake_Vrepresentation_data(vertices, rays, lines) - data.update(self._polymake_Hrepresentation_data(inequalities, equations)) + data = self._polymake_Vrepresentation_data(vertices, rays, lines, minimal=True) + data.update(self._polymake_Hrepresentation_data(inequalities, equations, minimal=True)) polymake_field = polymake(self.base_ring().fraction_field()) self._polymake_polytope = polymake.new_object("Polytope<{}>".format(polymake_field), **data) From 310b7004df445f9ef41cda58091bbd7fd0274d89 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 11:13:31 +0200 Subject: [PATCH 070/336] more tests for pickling and some fixes --- .../geometry/polyhedron/backend_polymake.py | 88 ++++++++++++++----- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 564082df542..eadd7d479e0 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -220,6 +220,17 @@ def __init__(self, parent, Vrep, Hrep, polymake_polytope=None, **kwds): sage: p = Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)], # optional - polymake ....: backend='polymake') sage: TestSuite(p).run(skip='_test_lawrence') # optional - polymake + + :: + + sage: p = Polyhedron(rays=[[1,1]], backend='polymake') # optional - polymake + sage: TestSuite(p).run() # optional - polymake + sage: p = Polyhedron(rays=[[1]], backend='polymake') # optional - polymake + sage: TestSuite(p).run() # optional - polymake + sage: p = Polyhedron(rays=[[1,1,1]], lines=[[1,0,0]], backend='polymake') # optional - polymake + sage: TestSuite(p).run() # optional - polymake + sage: p = Polyhedron(vertices=[[]], backend='polymake') # optional - polymake + sage: TestSuite(p).run() # optional - polymake """ if polymake_polytope is not None: if Hrep is not None or Vrep is not None: @@ -312,7 +323,6 @@ def _polymake_Vrepresentation_data(self, vertices, rays, lines, minimal=False): + [ [0] + list(r) for r in rays ]), LINEALITY_SPACE=[ [0] + list(l) for l in lines ]) - def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): r""" Construct polyhedron from H-representation data. @@ -563,35 +573,28 @@ def __setstate__(self, state): sage: from sage.geometry.polyhedron.backend_polymake import Polyhedron_polymake # optional - polymake sage: P = polytopes.permutahedron(4, backend='polymake') # optional - polymake sage: P1 = loads(dumps(P)) # optional - polymake - sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake - sage: P2 == P # optional - polymake - True + sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake + sage: P._test_polymake_pickling(other=P2) # optional - polymake - sage: P = Polyhedron(lines=[[1,0], [0,1]], backend='polymake') # optional - polymake - sage: P1 = loads(dumps(P)) # optional - polymake + sage: P = Polyhedron(lines=[[1,0], [0,1]], backend='polymake') # optional - polymake + sage: P1 = loads(dumps(P)) # optional - polymake sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake - sage: P2 == P # optional - polymake - True + sage: P._test_polymake_pickling(other=P2) # optional - polymake - sage: P = Polyhedron(backend='polymake') # optional - polymake - sage: P1 = loads(dumps(P)) # optional - polymake - sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake - sage: P2 == P # optional - polymake - True + sage: P = Polyhedron(backend='polymake') # optional - polymake + sage: P1 = loads(dumps(P)) # optional - polymake + sage: P._test_polymake_pickling(other=P1) # optional - polymake sage: P = polytopes.permutahedron(4, backend='polymake') * Polyhedron(lines=[[1]], backend='polymake') # optional - polymake - sage: P1 = loads(dumps(P)) # optional - polymake - sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake - sage: P2 == P # optional - polymake - True + sage: P1 = loads(dumps(P)) # optional - polymake + sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake + sage: P._test_polymake_pickling(other=P2) # optional - polymake sage: print("Possible output"); P = polytopes.dodecahedron(backend='polymake') # optional - polymake Possible output... - sage: P1 = loads(dumps(P)) # optional - polymake + sage: P1 = loads(dumps(P)) # optional - polymake sage: P2 = Polyhedron_polymake(P1.parent(), None, None, P1._polymake_polytope) # optional - polymake - sage: P == P2 # optional - polymake - True - + sage: P._test_polymake_pickling(other=P2) # optional - polymake """ if "_pickle_vertices" in state[1]: vertices = state[1].pop("_pickle_vertices") @@ -611,12 +614,53 @@ def __setstate__(self, state): inequalities = self.inequalities() equations = self.equations() + if not vertices and not rays and not lines: + # The empty polyhedron. + return + from sage.interfaces.polymake import polymake data = self._polymake_Vrepresentation_data(vertices, rays, lines, minimal=True) - data.update(self._polymake_Hrepresentation_data(inequalities, equations, minimal=True)) + + # Do not assume minimal Hrepresentation for unbounded polyhedra due to issues with the far facet. + data.update(self._polymake_Hrepresentation_data(inequalities, equations, minimal=(not rays and not lines))) + polymake_field = polymake(self.base_ring().fraction_field()) self._polymake_polytope = polymake.new_object("Polytope<{}>".format(polymake_field), **data) + def _test_polymake_pickling(self, tester=None, other=None, **options): + """ + Run tests to see that our polymake pickling/unpickling works. + + INPUT: + + - ``other`` -- a pickling polytope of ``self`` to be tested against + + TESTS:: + + sage: polytopes.cross_polytope(3, backend='polymake')._test_polymake_pickling() # optional - polymake + """ + if tester is None: + tester = self._tester(**options) + + if other is None: + from sage.misc.persist import loads, dumps + other = loads(dumps(self)) + + tester.assertEqual(self, other) + + if not hasattr(self, '_polymake_polytope'): + tester.assertFalse(hasattr(other, '_polymake_polytope')) + return + + P = self._polymake_polytope + P1 = other._polymake_polytope + + tester.assertEqual(P.F_VECTOR, P1.F_VECTOR) + tester.assertEqual(P.VERTICES, P1.VERTICES) + tester.assertEqual(P.LINEALITY_SPACE, P1.LINEALITY_SPACE) + tester.assertEqual(P.FACETS, P1.FACETS) + tester.assertEqual(P.AFFINE_HULL, P1.AFFINE_HULL) + ######################################################################### class Polyhedron_QQ_polymake(Polyhedron_polymake, Polyhedron_QQ): r""" From f58a2672fb9d5197e9aabc1d2eb5a231754d3bf7 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 11:39:58 +0200 Subject: [PATCH 071/336] fix the far facet issue --- src/sage/geometry/polyhedron/backend_polymake.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index eadd7d479e0..53b73d28966 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -621,8 +621,16 @@ def __setstate__(self, state): from sage.interfaces.polymake import polymake data = self._polymake_Vrepresentation_data(vertices, rays, lines, minimal=True) - # Do not assume minimal Hrepresentation for unbounded polyhedra due to issues with the far facet. - data.update(self._polymake_Hrepresentation_data(inequalities, equations, minimal=(not rays and not lines))) + if rays or lines: + from sage.matrix.constructor import Matrix + polymake_rays = [r for r in data['VERTICES'] if r[0] == 0] + if Matrix(data['VERTICES']).rank() == Matrix(polymake_rays).rank() + 1: + # The recession cone is full-dimensional. + # In this case the homogenized inequalities + # do not ensure nonnegativy in the last coordinate. + # In the homogeneous cone the far face is a facet. + inequalities.append([1] + [0]*self.ambient_dim()) + data.update(self._polymake_Hrepresentation_data(inequalities, equations, minimal=True)) polymake_field = polymake(self.base_ring().fraction_field()) self._polymake_polytope = polymake.new_object("Polytope<{}>".format(polymake_field), **data) From 1d5da2cd562396661ca43b066d68a4d94973befb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 12:41:14 +0200 Subject: [PATCH 072/336] initialize polymake from both Vrep and Hrep --- .../geometry/polyhedron/backend_polymake.py | 96 +++++++++++++++---- 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 53b73d28966..879fa06758c 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -399,6 +399,80 @@ def _polymake_Hrepresentation_data(self, ieqs, eqns, minimal=False): return dict(AFFINE_HULL=eqns, FACETS=ieqs) + def _init_from_Vrepresentation_and_Hrepresentation(self, Vrep, Hrep): + """ + Construct polyhedron from V-representation and H-representation data. + + See :class:`Polyhedron_base` for a description of ``Vrep`` and ``Hrep``. + + .. WARNING:: + + The representation is assumed to be correct. + It is not checked. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.parent import Polyhedra_polymake + sage: from sage.geometry.polyhedron.backend_polymake import Polyhedron_polymake + sage: parent = Polyhedra_polymake(ZZ, 1, 'polymake') + sage: Vrep = [[[0], [1]], [], []] + sage: Hrep = [[[0, 1], [1, -1]], []] + sage: p = Polyhedron_polymake(parent, Vrep, Hrep, + ....: Vrep_minimal=True, Hrep_minimal=True) # indirect doctest # optional - polymake + sage: p # optional - polymake + A 1-dimensional polyhedron in ZZ^1 defined as the convex hull of 2 vertices + """ + Vrep = [list(x) for x in Vrep] + Hrep = [list(x) for x in Hrep] + p = self._polymake_polytope_from_Vrepresentation_and_Hrepresentation(Vrep, Hrep) + if p is None: + self._init_empty_polyhedron() + return + + self._polymake_polytope = p + + # As the conversion from polymake to sage is slow, + # we skip it. + parent = self.parent() + vertices, rays, lines = Vrep + inequalities, equations = Hrep + self._Vrepresentation = [] + self._Hrepresentation = [] + for x in vertices: + parent._make_Vertex(self, x) + for x in rays: + parent._make_Ray(self, x) + for x in lines: + parent._make_Line(self, x) + for x in inequalities: + parent._make_Inequality(self, x) + for x in equations: + parent._make_Equation(self, x) + self._Vrepresentation = tuple(self._Vrepresentation) + self._Hrepresentation = tuple(self._Hrepresentation) + + def _polymake_polytope_from_Vrepresentation_and_Hrepresentation(self, Vrep, Hrep): + if not any(Vrep): + # The empty polyhedron. + return + + from sage.interfaces.polymake import polymake + data = self._polymake_Vrepresentation_data(*Vrep, minimal=True) + + if any(Vrep[1:]): + from sage.matrix.constructor import Matrix + polymake_rays = [r for r in data['VERTICES'] if r[0] == 0] + if Matrix(data['VERTICES']).rank() == Matrix(polymake_rays).rank() + 1: + # The recession cone is full-dimensional. + # In this case the homogenized inequalities + # do not ensure nonnegativy in the last coordinate. + # In the homogeneous cone the far face is a facet. + Hrep[0] += [[1] + [0]*self.ambient_dim()] + data.update(self._polymake_Hrepresentation_data(*Hrep, minimal=True)) + + polymake_field = polymake(self.base_ring().fraction_field()) + return polymake.new_object("Polytope<{}>".format(polymake_field), **data) + def _init_Vrepresentation_from_polymake(self): r""" Create the Vrepresentation objects from the polymake polyhedron. @@ -614,26 +688,10 @@ def __setstate__(self, state): inequalities = self.inequalities() equations = self.equations() - if not vertices and not rays and not lines: - # The empty polyhedron. - return - from sage.interfaces.polymake import polymake - data = self._polymake_Vrepresentation_data(vertices, rays, lines, minimal=True) - - if rays or lines: - from sage.matrix.constructor import Matrix - polymake_rays = [r for r in data['VERTICES'] if r[0] == 0] - if Matrix(data['VERTICES']).rank() == Matrix(polymake_rays).rank() + 1: - # The recession cone is full-dimensional. - # In this case the homogenized inequalities - # do not ensure nonnegativy in the last coordinate. - # In the homogeneous cone the far face is a facet. - inequalities.append([1] + [0]*self.ambient_dim()) - data.update(self._polymake_Hrepresentation_data(inequalities, equations, minimal=True)) - - polymake_field = polymake(self.base_ring().fraction_field()) - self._polymake_polytope = polymake.new_object("Polytope<{}>".format(polymake_field), **data) + p = self._polymake_polytope_from_Vrepresentation_and_Hrepresentation([vertices, rays, lines], [inequalities, equations]) + if p is not None: + self._polymake_polytope = p def _test_polymake_pickling(self, tester=None, other=None, **options): """ From 02a6ef9f81f0973f603fede9fae369647f49e56f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 16:35:28 +0200 Subject: [PATCH 073/336] faster sage eval of polymake --- .../geometry/polyhedron/backend_polymake.py | 14 +++--- src/sage/interfaces/polymake.py | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 53b73d28966..835dab701f7 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -418,8 +418,7 @@ def _init_Vrepresentation_from_polymake(self): self._Vrepresentation = [] parent = self.parent() p = self._polymake_polytope - for g in p.VERTICES: - g = g.sage() + for g in p.VERTICES.fast_sage(): d = g[0] if d == 0: parent._make_Ray(self, g[1:]) @@ -427,8 +426,7 @@ def _init_Vrepresentation_from_polymake(self): parent._make_Vertex(self, g[1:]) else: raise NotImplementedError("Non-normalized vertex encountered: {}".format(g)) - for g in p.LINEALITY_SPACE: - g = g.sage() + for g in p.LINEALITY_SPACE.fast_sage(): d = g[0] if d == 0: parent._make_Line(self, g[1:]) @@ -458,14 +456,14 @@ def _init_Hrepresentation_from_polymake(self): else: self._Hrepresentation = [] parent = self.parent() - for g in p.FACETS: + for g in p.FACETS.fast_sage(): if all(x==0 for x in g[1:]): # Ignore vertical inequality pass else: - parent._make_Inequality(self, g.sage()) - for g in p.AFFINE_HULL: - parent._make_Equation(self, g.sage()) + parent._make_Inequality(self, g) + for g in p.AFFINE_HULL.fast_sage(): + parent._make_Equation(self, g) self._Hrepresentation = tuple(self._Hrepresentation) @classmethod diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 3b83652a583..beb8e74225c 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1544,6 +1544,54 @@ def _sage_(self): else: return super(PolymakeElement, self)._sage_() + def fast_sage(self): + r""" + Convert ``self`` quickly to a sage object. + + Not as clean as :meth:`_sage_`. + """ + T1, T2 = self.typeof() + self._check_valid() + if 'Sparse' in T1: + return self._sage_() + + r = self._repr_() + if 'Float' in T1: + from sage.rings.all import RDF + base_ring = RDF + str_to_base_ring = lambda s: RDF(s) + elif 'QuadraticExtension' in T1 and 'r' in r: + i = r.find('r') + i1 = min((r[i:]+' ').find(' '), (r[i:]+'\n').find('\n')) + d = int(r[i+1:i+i1]) + from sage.rings.number_field.number_field import QuadraticField + base_ring = QuadraticField(d) + + def str_to_base_ring(s): + m = re.match(r'(-?[0-9/]+)[+]?((-?[0-9/]+)r([0-9/]+))?', s) + a, b = m.group(1), m.group(3) + return base_ring(a) + base_ring(b)*base_ring.gen() + + elif 'Rational' in T1: + from sage.rings.all import QQ + base_ring = QQ + str_to_base_ring = lambda s: QQ(s) + else: + return self._sage_() + + if 'Vector' in T1: + from sage.modules.free_module_element import vector + if r == '': + return vector(base_ring) + return vector(base_ring, [str_to_base_ring(s) for s in r.split(' ')]) + elif 'Matrix' in T1: + from sage.matrix.constructor import matrix + if r == '': + return matrix(base_ring) + return matrix(base_ring, [[str_to_base_ring(s) for s in t.split(' ')] for t in r.split('\n')]) + else: + return self._sage_() + def _sage_doc_(self): """ EXAMPLES:: From 2ca121eb5c7d1127cc52007d365c21573c5e9286 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 19:44:33 +0200 Subject: [PATCH 074/336] try faster method directly in _sage_ --- .../geometry/polyhedron/backend_polymake.py | 8 +- src/sage/interfaces/polymake.py | 90 +++++++++---------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 835dab701f7..44c7e71a49e 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -418,7 +418,7 @@ def _init_Vrepresentation_from_polymake(self): self._Vrepresentation = [] parent = self.parent() p = self._polymake_polytope - for g in p.VERTICES.fast_sage(): + for g in p.VERTICES.sage(): d = g[0] if d == 0: parent._make_Ray(self, g[1:]) @@ -426,7 +426,7 @@ def _init_Vrepresentation_from_polymake(self): parent._make_Vertex(self, g[1:]) else: raise NotImplementedError("Non-normalized vertex encountered: {}".format(g)) - for g in p.LINEALITY_SPACE.fast_sage(): + for g in p.LINEALITY_SPACE.sage(): d = g[0] if d == 0: parent._make_Line(self, g[1:]) @@ -456,13 +456,13 @@ def _init_Hrepresentation_from_polymake(self): else: self._Hrepresentation = [] parent = self.parent() - for g in p.FACETS.fast_sage(): + for g in p.FACETS.sage(): if all(x==0 for x in g[1:]): # Ignore vertical inequality pass else: parent._make_Inequality(self, g) - for g in p.AFFINE_HULL.fast_sage(): + for g in p.AFFINE_HULL.sage(): parent._make_Equation(self, g) self._Hrepresentation = tuple(self._Hrepresentation) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index beb8e74225c..94249562c79 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1514,6 +1514,48 @@ def _sage_(self): """ T1, T2 = self.typeof() self._check_valid() + try: + # Try to just read things from the string representation. + if 'Sparse' in T1: + raise NotImplementedError + + r = self._repr_() + if 'Float' in T1: + from sage.rings.all import RDF + base_ring = RDF + str_to_base_ring = lambda s: RDF(s) + elif 'QuadraticExtension' in T1 and 'r' in r: + i = r.find('r') + i1 = min((r[i:]+' ').find(' '), (r[i:]+'\n').find('\n')) + d = int(r[i+1:i+i1]) + from sage.rings.number_field.number_field import QuadraticField + base_ring = QuadraticField(d) + + def str_to_base_ring(s): + m = re.match(r'(-?[0-9/]+)[+]?((-?[0-9/]+)r([0-9/]+))?', s) + a, b = m.group(1), m.group(3) + return base_ring(a) + base_ring(b)*base_ring.gen() + + elif 'Rational' in T1: + from sage.rings.all import QQ + base_ring = QQ + str_to_base_ring = lambda s: QQ(s) + else: + raise NotImplementedError + + if 'Vector' in T1: + from sage.modules.free_module_element import vector + if r == '': + return vector(base_ring) + return vector(base_ring, [str_to_base_ring(s) for s in r.split(' ')]) + elif 'Matrix' in T1: + from sage.matrix.constructor import matrix + if r == '': + return matrix(base_ring) + return matrix(base_ring, [[str_to_base_ring(s) for s in t.split(' ')] for t in r.split('\n')]) + except: + pass + if T1: Temp = self.typename() if Temp: @@ -1544,54 +1586,6 @@ def _sage_(self): else: return super(PolymakeElement, self)._sage_() - def fast_sage(self): - r""" - Convert ``self`` quickly to a sage object. - - Not as clean as :meth:`_sage_`. - """ - T1, T2 = self.typeof() - self._check_valid() - if 'Sparse' in T1: - return self._sage_() - - r = self._repr_() - if 'Float' in T1: - from sage.rings.all import RDF - base_ring = RDF - str_to_base_ring = lambda s: RDF(s) - elif 'QuadraticExtension' in T1 and 'r' in r: - i = r.find('r') - i1 = min((r[i:]+' ').find(' '), (r[i:]+'\n').find('\n')) - d = int(r[i+1:i+i1]) - from sage.rings.number_field.number_field import QuadraticField - base_ring = QuadraticField(d) - - def str_to_base_ring(s): - m = re.match(r'(-?[0-9/]+)[+]?((-?[0-9/]+)r([0-9/]+))?', s) - a, b = m.group(1), m.group(3) - return base_ring(a) + base_ring(b)*base_ring.gen() - - elif 'Rational' in T1: - from sage.rings.all import QQ - base_ring = QQ - str_to_base_ring = lambda s: QQ(s) - else: - return self._sage_() - - if 'Vector' in T1: - from sage.modules.free_module_element import vector - if r == '': - return vector(base_ring) - return vector(base_ring, [str_to_base_ring(s) for s in r.split(' ')]) - elif 'Matrix' in T1: - from sage.matrix.constructor import matrix - if r == '': - return matrix(base_ring) - return matrix(base_ring, [[str_to_base_ring(s) for s in t.split(' ')] for t in r.split('\n')]) - else: - return self._sage_() - def _sage_doc_(self): """ EXAMPLES:: From 4a663578407c7e00ea6cad4e65a6c9ca7dbf4600 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 27 May 2021 09:16:41 +0200 Subject: [PATCH 075/336] improved polymake input --- .../geometry/polyhedron/backend_polymake.py | 2 +- src/sage/interfaces/polymake.py | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 44c7e71a49e..ee22da06338 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -497,7 +497,7 @@ def _from_polymake_polytope(cls, parent, polymake_polytope): base_ring = coercion_model.common_parent(*data).base_ring() else: base_ring = QQ - ambient_dim = polymake_polytope.AMBIENT_DIM() + ambient_dim = polymake_polytope.AMBIENT_DIM().sage() parent = Polyhedra(base_ring=base_ring, ambient_dim=ambient_dim, backend='polymake') return cls(parent, None, None, polymake_polytope=polymake_polytope) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 94249562c79..51e8555a039 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -334,8 +334,42 @@ def convert(y): r = self.new("{" + ",".join(A) + "}") r.__sage_dict = z # do this to avoid having the entries of the list be garbage collected return r - else: - return super(PolymakeAbstract, self)._coerce_impl(x, use_special=use_special) + + from sage.rings.all import Integer, Rational, RDF + + def to_str(x): + if isinstance(x, list): + s = '[' + for y in x: + s += to_str(y) + ', ' + s += ']' + return s + if isinstance(x, (Integer, Rational, int)): + return '{}'.format(x) + + try: + if x.parent().is_exact(): + # No other exact rings are supported. + raise NotImplementedError + except AttributeError: + pass + + try: + x = RDF(x) + return '{}'.format(x) + except: + pass + + raise NotImplementedError + + # Iteratively calling polymake for conversion takes a long time. + # However, it takes iterated arrays of integers, rationals and floats directly. + try: + x = to_str(x) + except NotImplementedError: + pass + + return super(PolymakeAbstract, self)._coerce_impl(x, use_special=use_special) def console(self): """ From 543d2115d88cd57dc6939567982840999f20cd0d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 27 May 2021 14:00:39 +0200 Subject: [PATCH 076/336] fix number field input --- src/sage/interfaces/polymake.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 51e8555a039..3e21b76d619 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -336,6 +336,7 @@ def convert(y): return r from sage.rings.all import Integer, Rational, RDF + from sage.rings.number_field.number_field import is_QuadraticField def to_str(x): if isinstance(x, list): @@ -346,7 +347,17 @@ def to_str(x): return s if isinstance(x, (Integer, Rational, int)): return '{}'.format(x) + parent = None + try: + parent = x.parent() + except AttributeError: + pass + if is_QuadraticField(parent): + c = x._coefficients() + [0,0] + gen = parent.gen() + r = 'new QuadraticExtension({}, {}, {})'.format(c[0], c[1], gen*gen) + return r try: if x.parent().is_exact(): # No other exact rings are supported. @@ -365,7 +376,7 @@ def to_str(x): # Iteratively calling polymake for conversion takes a long time. # However, it takes iterated arrays of integers, rationals and floats directly. try: - x = to_str(x) + return self.new(to_str(x)) except NotImplementedError: pass From c0eb507ad3f5f277e23173d67ed240ab455d02bb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 27 May 2021 20:17:27 +0200 Subject: [PATCH 077/336] use `_polymake_init_` for quadratic fields --- src/sage/interfaces/polymake.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 3e21b76d619..aa237caf771 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -354,10 +354,7 @@ def to_str(x): pass if is_QuadraticField(parent): - c = x._coefficients() + [0,0] - gen = parent.gen() - r = 'new QuadraticExtension({}, {}, {})'.format(c[0], c[1], gen*gen) - return r + return x._polymake_init_() try: if x.parent().is_exact(): # No other exact rings are supported. From 99b5715d63e87ee1b7921d7073b762c3b996376a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 May 2021 12:34:48 -0700 Subject: [PATCH 078/336] src/sage/rings/padics/padic_printing.pyx: Fix up distutils directive --- src/sage/rings/padics/padic_printing.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_printing.pyx b/src/sage/rings/padics/padic_printing.pyx index f0fea52b8d2..654f90fa0e5 100644 --- a/src/sage/rings/padics/padic_printing.pyx +++ b/src/sage/rings/padics/padic_printing.pyx @@ -1,4 +1,4 @@ -# distutils: libraries = NTL_LIBRARIES ntl m +# distutils: libraries = NTL_LIBRARIES gmp m # distutils: extra_compile_args = NTL_CFLAGS # distutils: include_dirs = NTL_INCDIR # distutils: library_dirs = NTL_LIBDIR From 05e8f96af87821472869026d0a672aa20397efef Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Fri, 28 May 2021 21:49:37 -0400 Subject: [PATCH 079/336] add example where disjoint_union raisess an error if the input is not disjoint --- src/sage/homology/polyhedral_complex.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 645b9e647b5..04621752b59 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -1621,6 +1621,11 @@ def disjoint_union(self, right): sage: pc = pc1.disjoint_union(pc2) sage: set(pc.maximal_cell_iterator()) == set([p1, p2, p3]) True + sage: pc4 = Polyhedron(vertices=[(1, 1), (0, 0), (0, 2)]) + sage: pc.disjoint_union(PolyhedralComplex([pc4])) + Traceback (most recent call last): + ... + ValueError: The given cells are not face-to-face """ maximal_cells = list(self.maximal_cell_iterator()) + list( right.maximal_cell_iterator()) From 27af45ad95dda448babc665b58181717dca6fa47 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Fri, 28 May 2021 21:39:26 -0400 Subject: [PATCH 080/336] Typos: interoir->interior, complexe->complex --- src/sage/homology/polyhedral_complex.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 04621752b59..0dff2526880 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -92,7 +92,7 @@ :meth:`~PolyhedralComplex.join` | Return the join of this polyhedral complex with another one. :meth:`~PolyhedralComplex.subdivide` | Return a new polyhedral complex (with option ``make_simplicial``) subdividing this one. -**Update polyhedral complexe** +**Update polyhedral complex** .. csv-table:: :class: contentstable @@ -1295,7 +1295,7 @@ def boundary_subcomplex(self): Return the sub-polyhedral complex that is the boundary of ``self``. A point `P` is on the boundary of a set `S` if `P` is in the - closure of `S` but not in the interoir of `S`. + closure of `S` but not in the interior of `S`. EXAMPLES:: @@ -1359,7 +1359,7 @@ def relative_boundary_cells(self): Return the maximal cells of the relative-boundary sub-complex. A point `P` is in the relative boundary of a set `S` if `P` is in the - closure of `S` but not in the relative interoir of `S`. + closure of `S` but not in the relative interior of `S`. .. WARNING:: @@ -1661,7 +1661,7 @@ def join(self, right): backend=self._backend) ############################################################ - # abstract methods not implemented in generic cell complexe + # abstract methods not implemented in generic cell complex ############################################################ def wedge(self, right): From cf7698c7a3436720cb4c61f350b429e90aad29c6 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Fri, 28 May 2021 22:47:07 -0400 Subject: [PATCH 081/336] methods disjoint_union and union --- src/sage/homology/polyhedral_complex.py | 51 ++++++++++++++++++++----- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 0dff2526880..d87e821a15f 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -89,6 +89,7 @@ :meth:`~PolyhedralComplex.boundary_subcomplex` | Return the boundary subcomplex of this polyhedral complex. :meth:`~PolyhedralComplex.product` | Return the (Cartesian) product of this polyhedral complex with another one. :meth:`~PolyhedralComplex.disjoint_union` | Return the disjoint union of this polyhedral complex with another one. + :meth:`~PolyhedralComplex.union` | Return the union of this polyhedral complex with another one. :meth:`~PolyhedralComplex.join` | Return the join of this polyhedral complex with another one. :meth:`~PolyhedralComplex.subdivide` | Return a new polyhedral complex (with option ``make_simplicial``) subdividing this one. @@ -1613,16 +1614,48 @@ def disjoint_union(self, right): EXAMPLES:: - sage: p1 = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) - sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) - sage: p3 = Polyhedron(vertices=[(0, 0), (1, 1), (2, 0)]) - sage: pc1 = PolyhedralComplex([p1, p3]) - sage: pc2 = PolyhedralComplex([p2]) - sage: pc = pc1.disjoint_union(pc2) - sage: set(pc.maximal_cell_iterator()) == set([p1, p2, p3]) + sage: p1 = Polyhedron(vertices=[(-1, 0), (0, 0), (0, 1)]) + sage: p2 = Polyhedron(vertices=[(0, -1), (0, 0), (1, 0)]) + sage: p3 = Polyhedron(vertices=[(0, -1), (1, -1), (1, 0)]) + sage: pc = PolyhedralComplex([p1]).disjoint_union(PolyhedralComplex([p3])) + sage: set(pc.maximal_cell_iterator()) == set([p1, p3]) True - sage: pc4 = Polyhedron(vertices=[(1, 1), (0, 0), (0, 2)]) - sage: pc.disjoint_union(PolyhedralComplex([pc4])) + sage: pc.disjoint_union(PolyhedralComplex([p2])) + Traceback (most recent call last): + ... + ValueError: The two complexes are not disjoint + """ + maximal_cells_self = list(self.maximal_cell_iterator()) + maximal_cells_right = list(right.maximal_cell_iterator()) + for cell in maximal_cells_self: + for cell_right in maximal_cells_right: + if not cell.intersection(cell_right).is_empty(): + raise ValueError("The two complexes are not disjoint") + return PolyhedralComplex(maximal_cells_self + maximal_cells_right, + maximality_check=False, + face_to_face_check=False, + is_immutable=(self._is_immutable and + right._is_immutable), + backend=self._backend) + + def union(self, right): + """ + The union of this polyhedral complex with another one. + + :param right: the other polyhedral complex (the right-hand factor) + + EXAMPLES:: + + sage: p1 = Polyhedron(vertices=[(-1, 0), (0, 0), (0, 1)]) + sage: p2 = Polyhedron(vertices=[(0, -1), (0, 0), (1, 0)]) + sage: p3 = Polyhedron(vertices=[(0, -1), (1, -1), (1, 0)]) + sage: pc = PolyhedralComplex([p1]).union(PolyhedralComplex([p3])) + sage: set(pc.maximal_cell_iterator()) == set([p1, p3]) + True + sage: pc.union(PolyhedralComplex([p2])) + Polyhedral complex with 3 maximal cells + sage: p4 = Polyhedron(vertices=[(0, -1), (0, 0), (1, 0), (1, -1)]) + sage: pc.union(PolyhedralComplex([p4])) Traceback (most recent call last): ... ValueError: The given cells are not face-to-face From 372a10767ed2eb77b40a6ba0da6a77a576631b2d Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Fri, 28 May 2021 23:04:52 -0400 Subject: [PATCH 082/336] add a keyword argument ambient_dim to __init__ so that one can set up an empty complex in the intended ambient space --- src/sage/homology/polyhedral_complex.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index d87e821a15f..730e9899414 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -184,6 +184,9 @@ class PolyhedralComplex(GenericCellComplex): * ``field`` a generic Sage implementation + - ``ambient_dim`` -- integer; default ``None``; Used to set up an empty + complex in the intended ambient space. + EXAMPLES:: sage: pc = PolyhedralComplex([ @@ -263,7 +266,8 @@ class PolyhedralComplex(GenericCellComplex): 'cdd' """ def __init__(self, maximal_cells=None, backend=None, maximality_check=True, - face_to_face_check=False, is_mutable=True, is_immutable=False): + face_to_face_check=False, is_mutable=True, is_immutable=False, + ambient_dim=None): r""" Define a PolyhedralComplex. @@ -294,10 +298,13 @@ def __init__(self, maximal_cells=None, backend=None, maximality_check=True, raise ValueError if not cells_dict: self._dim = -1 - self._ambient_dim = -1 + if ambient_dim is None: + ambient_dim = -1 else: self._dim = max(cells_dict.keys()) - self._ambient_dim = next(iter(cells_dict[self._dim])).ambient_dim() + if ambient_dim is None: + ambient_dim = next(iter(cells_dict[self._dim])).ambient_dim() + self._ambient_dim = ambient_dim self._maximal_cells = cells_dict if not all((is_Polyhedron(cell) and cell.ambient_dim() == self._ambient_dim) @@ -707,6 +714,9 @@ def ambient_dimension(self): sage: empty_pc = PolyhedralComplex([]) sage: empty_pc.ambient_dimension() -1 + sage: pc0 = PolyhedralComplex(ambient_dim=2) + sage: pc0.ambient_dimension() + 2 """ return self._ambient_dim @@ -1816,11 +1826,15 @@ def add_cell(self, cell): EXAMPLES: - If you add a cell which is already present, there is no effect:: + Set up an empty complex in the intended ambient space, then add a cell:: - sage: pc = PolyhedralComplex([Polyhedron(vertices=[(1, 2), (0, 2)])]) + sage: pc = PolyhedralComplex(ambient_dim=2) + sage: pc.add_cell(Polyhedron(vertices=[(1, 2), (0, 2)])) sage: pc Polyhedral complex with 1 maximal cell + + If you add a cell which is already present, there is no effect:: + sage: pc.add_cell(Polyhedron(vertices=[(1, 2)])) sage: pc Polyhedral complex with 1 maximal cell From 0a40f1f841b07be7862f2a97c4cb501ccca451c2 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sat, 29 May 2021 21:52:11 -0400 Subject: [PATCH 083/336] improve documentation and error messages --- src/sage/homology/polyhedral_complex.py | 197 +++++++++++++++--------- 1 file changed, 121 insertions(+), 76 deletions(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index 730e9899414..d3deea30fc0 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -174,15 +174,15 @@ class PolyhedralComplex(GenericCellComplex): computations on Sage polyhedra. If it is ``None``, then each cell has its own backend. Otherwise, all cells will use the given backend from: - * ``ppl`` the Parma Polyhedra Library + * ``ppl`` the Parma Polyhedra Library - * ``cdd`` CDD + * ``cdd`` CDD - * ``normaliz`` normaliz + * ``normaliz`` normaliz - * ``polymake`` polymake + * ``polymake`` polymake - * ``field`` a generic Sage implementation + * ``field`` a generic Sage implementation - ``ambient_dim`` -- integer; default ``None``; Used to set up an empty complex in the intended ambient space. @@ -244,7 +244,7 @@ class PolyhedralComplex(GenericCellComplex): ....: face_to_face_check=True) Traceback (most recent call last): ... - ValueError: The given cells are not face-to-face + ValueError: the given cells are not face-to-face Check that all the cells must have the same ambient dimension:: @@ -253,7 +253,7 @@ class PolyhedralComplex(GenericCellComplex): ....: Polyhedron(vertices=[[2], [0]]) ]) Traceback (most recent call last): ... - ValueError: The given cells are not polyhedra in the same ambient space. + ValueError: the given cells are not polyhedra in the same ambient space Check that backend is passed to all the cells:: @@ -295,7 +295,7 @@ def __init__(self, maximal_cells=None, backend=None, maximality_check=True, else: cells_dict[k] = set(l) else: - raise ValueError + raise ValueError("the maximal cells are not given in correct form") if not cells_dict: self._dim = -1 if ambient_dim is None: @@ -309,8 +309,8 @@ def __init__(self, maximal_cells=None, backend=None, maximality_check=True, if not all((is_Polyhedron(cell) and cell.ambient_dim() == self._ambient_dim) for cell in self.maximal_cell_iterator()): - raise ValueError("The given cells are not polyhedra " + - "in the same ambient space.") + raise ValueError("the given cells are not polyhedra " + + "in the same ambient space") # initialize the attributes self._is_convex = None self._polyhedron = None @@ -332,7 +332,7 @@ def __init__(self, maximal_cells=None, backend=None, maximality_check=True, r = p.intersection(q) if not (r.is_empty() or (r in poset) and poset.is_gequal(p, r) and poset.is_gequal(q, r)): - raise ValueError("The given cells are not face-to-face") + raise ValueError("the given cells are not face-to-face") self._is_immutable = False if not is_mutable or is_immutable: self.set_immutable() @@ -345,9 +345,11 @@ def cells(self, subcomplex=None): optional argument ``subcomplex`` is present, then return only the cells which are *not* in the subcomplex. - :param subcomplex: a subcomplex of this polyhedral complex. Return - the cells which are not in this subcomplex. - :type subcomplex: optional, default None + INPUT: + + - ``subcomplex`` -- (optional, default ``None``) if a subcomplex + of this polyhedral complex is given, then return the cells which + are not in this subcomplex. EXAMPLES:: @@ -358,7 +360,7 @@ def cells(self, subcomplex=None): [2, 1, 0] """ if subcomplex is not None: - raise NotImplementedError + raise NotImplementedError("providing subcomplex is not implemented") if self._cells is not None: return self._cells maximal_cells = self.maximal_cells() @@ -424,11 +426,13 @@ def _n_cells_sorted(self, n, subcomplex=None): return the ``n``-dimensional cells which are *not* in the subcomplex. - :param n: the dimension - :type n: non-negative integer - :param subcomplex: a subcomplex of this cell complex. Return - the cells which are not in this subcomplex. - :type subcomplex: optional, default ``None`` + INPUT: + + - ``n`` -- (non-negative integer) the dimension + + - ``subcomplex`` -- (optional, default ``None``) if a subcomplex + of this polyhedral complex is given, then return the cells which + are not in this subcomplex. EXAMPLES:: @@ -455,9 +459,11 @@ def cells_sorted(self, subcomplex=None): If the optional argument ``subcomplex`` is present, then return only the cells which are *not* in the subcomplex. - :param subcomplex: a subcomplex of this polyhedral complex. Return - the cells which are not in this subcomplex. - :type subcomplex: optional, default None + INPUT: + + - ``subcomplex`` -- (optional, default ``None``) if a subcomplex + of this polyhedral complex is given, then return the cells which + are not in this subcomplex. EXAMPLES:: @@ -553,8 +559,9 @@ def n_maximal_cells(self, n): """ List of maximal cells of dimension ``n`` of this polyhedral complex. - :param n: the dimension - :type n: non-negative integer + INPUT: + + - ``n`` -- (non-negative integer) the dimension .. NOTE:: @@ -594,8 +601,9 @@ def _n_maximal_cells_sorted(self, n): Sorted list of maximal cells of dimension ``n`` of this polyhedral complex. - :param n: the dimension - :type n: non-negative integer + INPUT: + + - ``n`` -- (non-negative integer) the dimension .. WARNING:: @@ -733,7 +741,7 @@ def plot(self, **kwds): Graphics object consisting of 10 graphics primitives """ if self.dimension() > 3: - raise ValueError("Cannot plot in high dimension") + raise ValueError("cannot plot in high dimension") return sum(cell.plot(**kwds) for cell in self.maximal_cell_iterator()) def is_pure(self): @@ -802,11 +810,11 @@ def __hash__(self): sage: hash(pc3) Traceback (most recent call last): ... - ValueError: This polyhedral complex must be immutableCall set_immutable(). + ValueError: this polyhedral complex must be immutable; call set_immutable() """ if not self._is_immutable: - raise ValueError("This polyhedral complex must be immutable" + - "Call set_immutable().") + raise ValueError("this polyhedral complex must be immutable; " + + "call set_immutable()") return hash(tuple(self.maximal_cells_sorted())) def __eq__(self, right): @@ -863,7 +871,9 @@ def _an_element_(self): EXAMPLES:: sage: PolyhedralComplex()._an_element_() - () + Traceback (most recent call last): + ... + EmptySetError: the complex is empty sage: pc = PolyhedralComplex([ ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) @@ -873,7 +883,8 @@ def _an_element_(self): try: return next(self.maximal_cell_iterator(increasing=False)) except StopIteration: - return () + from sage.categories.sets_cat import EmptySetError + raise EmptySetError("the complex is empty") def __contains__(self, x): """ @@ -966,7 +977,9 @@ def is_subcomplex(self, other): r""" Return True if ``self`` is a subcomplex of ``other``. - :param other: a polyhedral complex + INPUT: + + - ``other`` -- a polyhedral complex Each maximal cell of ``self`` must be a cell of ``other`` for this to be True. @@ -1028,7 +1041,7 @@ def graph(self): sage: PolyhedralComplex([Polyhedron(rays=[(1,1)])]).graph() Traceback (most recent call last): ... - NotImplementedError: The polyhedral complex is unbounded. + NotImplementedError: the polyhedral complex is unbounded Wrong answer due to ``maximality_check=False``:: @@ -1043,7 +1056,7 @@ def graph(self): False """ if not self.is_compact(): - raise NotImplementedError("The polyhedral complex is unbounded.") + raise NotImplementedError("the polyhedral complex is unbounded") edges = self.n_cells(1) d = {} for e in edges: @@ -1238,7 +1251,9 @@ def n_skeleton(self, n): The `n`-skeleton of a polyhedral complex is obtained by discarding all of the cells in dimensions larger than `n`. - :param n: non-negative integer + INPUT: + + - ``n`` -- (non-negative integer) the dimension .. SEEALSO:: @@ -1582,20 +1597,23 @@ def union_as_polyhedron(self): sage: PolyhedralComplex([p1, p3]).union_as_polyhedron() Traceback (most recent call last): ... - ValueError: The polyhedral complex is not convex. + ValueError: the polyhedral complex is not convex """ if not self.is_convex(): - raise ValueError("The polyhedral complex is not convex.") + raise ValueError("the polyhedral complex is not convex") return self._polyhedron def product(self, right): """ The (Cartesian) product of this polyhedral complex with another one. - :param right: the other polyhedral complex (the right-hand - factor) + INPUT: + + - ``right`` -- the other polyhedral complex (the right-hand factor) - :return: the product ``self x right`` + OUTPUT: + + - the product ``self x right`` EXAMPLES:: @@ -1620,7 +1638,9 @@ def disjoint_union(self, right): """ The disjoint union of this polyhedral complex with another one. - :param right: the other polyhedral complex (the right-hand factor) + INPUT: + + - ``right`` -- the other polyhedral complex (the right-hand factor) EXAMPLES:: @@ -1633,14 +1653,14 @@ def disjoint_union(self, right): sage: pc.disjoint_union(PolyhedralComplex([p2])) Traceback (most recent call last): ... - ValueError: The two complexes are not disjoint + ValueError: the two complexes are not disjoint """ maximal_cells_self = list(self.maximal_cell_iterator()) maximal_cells_right = list(right.maximal_cell_iterator()) for cell in maximal_cells_self: for cell_right in maximal_cells_right: if not cell.intersection(cell_right).is_empty(): - raise ValueError("The two complexes are not disjoint") + raise ValueError("the two complexes are not disjoint") return PolyhedralComplex(maximal_cells_self + maximal_cells_right, maximality_check=False, face_to_face_check=False, @@ -1652,7 +1672,9 @@ def union(self, right): """ The union of this polyhedral complex with another one. - :param right: the other polyhedral complex (the right-hand factor) + INPUT: + + - ``right`` -- the other polyhedral complex (the right-hand factor) EXAMPLES:: @@ -1668,7 +1690,7 @@ def union(self, right): sage: pc.union(PolyhedralComplex([p4])) Traceback (most recent call last): ... - ValueError: The given cells are not face-to-face + ValueError: the given cells are not face-to-face """ maximal_cells = list(self.maximal_cell_iterator()) + list( right.maximal_cell_iterator()) @@ -1682,7 +1704,9 @@ def join(self, right): """ The join of this polyhedral complex with another one. - :param right: the other polyhedral complex (the right-hand factor) + INPUT: + + - ``right`` -- the other polyhedral complex (the right-hand factor) EXAMPLES:: @@ -1708,7 +1732,12 @@ def join(self, right): ############################################################ def wedge(self, right): - raise NotImplementedError + """ + The wedge (one-point union) of this cell complex with + another one. Currently not implemented. + """ + raise NotImplementedError("wedge is not implemented for " + + "polyhedral complex") ############################################################ # chain complexes, homology @@ -1716,10 +1745,21 @@ def wedge(self, right): def chain_complex(self, subcomplex=None, augmented=False, verbose=False, check=True, dimensions=None, base_ring=ZZ, cochain=False): - raise NotImplementedError + """ + The chain complex associated to this polyhedral complex. + Currently not implemented. + """ + raise NotImplementedError("chain_complex is not implemented for " + + "polyhedral complex") def alexander_whitney(self, cell, dim_left): - raise NotImplementedError + """ + The decomposition of ``cell`` in this complex into left and right + factors, suitable for computing cup products. + Currently not implemented. + """ + raise NotImplementedError("alexander_whitney is not implemented for " + + "polyhedral complex") ############################################################ # end of chain complexes, homology @@ -1819,7 +1859,9 @@ def add_cell(self, cell): """ Add a cell to this polyhedral complex. - :param cell: a polyhedron + INPUT: + + - ``cell`` -- a polyhedron This *changes* the polyhedral complex, by adding a new cell and all of its subfaces. @@ -1880,22 +1922,22 @@ def add_cell(self, cell): sage: pc.add_cell(Polyhedron(vertices=[[0]])) Traceback (most recent call last): ... - ValueError: The given cell is not a polyhedron in the same ambient space. + ValueError: the given cell is not a polyhedron in the same ambient space sage: pc.add_cell(Polyhedron(vertices=[(1, 1), (0, 0), (2, 0)])) Traceback (most recent call last): ... - ValueError: The cell is not face-to-face with complex + ValueError: the cell is not face-to-face with complex sage: pc.set_immutable() sage: pc.add_cell(Polyhedron(vertices=[(-1, -1)])) Traceback (most recent call last): ... - ValueError: This polyhedral complex is not mutable + ValueError: this polyhedral complex is not mutable """ if self._is_immutable: - raise ValueError("This polyhedral complex is not mutable") + raise ValueError("this polyhedral complex is not mutable") if not is_Polyhedron(cell) or cell.ambient_dim() != self._ambient_dim: - raise ValueError("The given cell is not a polyhedron " + - "in the same ambient space.") + raise ValueError("the given cell is not a polyhedron " + + "in the same ambient space") # if cell is already in self, do nothing. if self.has_cell(cell): return @@ -1934,7 +1976,7 @@ def add_cell(self, cell): r = p.intersection(cell) if not (r.is_empty() or (r in poset) and poset.is_gequal(p, r) and poset.is_gequal(cell, r)): - raise ValueError("The cell is not face-to-face with complex") + raise ValueError("the cell is not face-to-face with complex") # update dim and maximal cells d = cell.dimension() if d > self._dim: @@ -1963,11 +2005,12 @@ def remove_cell(self, cell, check=False): Remove the given cell from this polyhedral complex. In addition, it removes all the cells that contain the given cell as a subface. - :param cell: a cell of the polyhedral complex + INPUT: + + - ``cell`` -- a cell of the polyhedral complex - :param check: boolean; optional, default ``False``. If - ``True``, raise an error if ``cell`` is not a - cell of this polyhedral complex + - ``check`` -- boolean; optional, default ``False``. + If ``True``, raise an error if ``cell`` is not a cell of this complex This does not return anything; instead, it *changes* the polyhedral complex. @@ -2005,11 +2048,11 @@ def remove_cell(self, cell, check=False): sage: pc.remove_cell(Polyhedron(vertices=[[0]]), check=True) Traceback (most recent call last): ... - ValueError: Trying to remove a cell which is not in the polyhedral complex + ValueError: trying to remove a cell which is not in the polyhedral complex sage: pc.remove_cell(Polyhedron(vertices=[(1, 1)])) Traceback (most recent call last): ... - ValueError: The given cell is not a polyhedron in the same ambient space. + ValueError: the given cell is not a polyhedron in the same ambient space sage: pc.remove_cell(p) sage: pc.dimension() -1 @@ -2017,7 +2060,7 @@ def remove_cell(self, cell, check=False): sage: pc.remove_cell(Polyhedron(vertices=[[0]])) Traceback (most recent call last): ... - ValueError: This polyhedral complex is not mutable + ValueError: this polyhedral complex is not mutable Check that this function is coherent with :meth:`~sage.homology.simplicial_complex.SimplicialComplex.remove_face`:: @@ -2054,14 +2097,14 @@ def remove_cell(self, cell, check=False): [[[1], [2]], [[-3]], [[1]], [[2]]] """ if self._is_immutable: - raise ValueError("This polyhedral complex is not mutable") + raise ValueError("this polyhedral complex is not mutable") if not is_Polyhedron(cell) or cell.ambient_dim() != self._ambient_dim: - raise ValueError("The given cell is not a polyhedron " + - "in the same ambient space.") + raise ValueError("the given cell is not a polyhedron " + + "in the same ambient space") # if cell is not in self, delete nothing. if not self.has_cell(cell): # self.cells() is called if check: - raise ValueError("Trying to remove a cell which is not " + + raise ValueError("trying to remove a cell which is not " + "in the polyhedral complex") return # update cells and face poset @@ -2163,10 +2206,12 @@ def subdivide(self, make_simplicial=False, Currently, subdivision is only supported for bounded polyhedral complex or polyhedral fan. - :param make_simplicial: boolean; optional, default ``False``. + INPUT: + + - ``make_simplicial`` -- boolean; optional, default ``False``. If ``True``, the returned polyhedral complex is simplicial. - :param new_vertices, new_rays: list; optional, default ``None``. + - ``new_vertices``, ``new_rays`` -- list; optional, default ``None``. New generators to be added during subdivision. EXAMPLES:: @@ -2194,7 +2239,7 @@ def subdivide(self, make_simplicial=False, sage: fan.subdivide(new_vertices=[(0, 0, 1)]) Traceback (most recent call last): ... - ValueError: new vertices cannot be used for subdivision. + ValueError: new vertices cannot be used for subdivision sage: subdivided_fan = fan.subdivide(new_rays=[(0, 0, 1)]) sage: subdivided_fan Polyhedral complex with 4 maximal cells @@ -2218,7 +2263,7 @@ def subdivide(self, make_simplicial=False, """ if self.is_compact(): if new_rays: - raise ValueError("rays/lines cannot be used for subdivision.") + raise ValueError("rays/lines cannot be used for subdivision") # bounded version of `fan.subdivide`; not require rational. vertices = set([]) if make_simplicial and not self.is_simplicial_complex(): @@ -2249,7 +2294,7 @@ def subdivide(self, make_simplicial=False, backend=self._backend) elif self.is_polyhedral_fan(): if new_vertices and any(vi != 0 for v in new_vertices for vi in v): - raise ValueError("new vertices cannot be used for subdivision.") + raise ValueError("new vertices cannot be used for subdivision") # mimic :meth:`~sage.geometry.fan .subdivide` # but here we allow for non-pointed cones, and we subdivide them. rays_normalized = set([]) @@ -2291,7 +2336,7 @@ def subdivide(self, make_simplicial=False, if new_rays: for r in new_rays: if vector(r).is_zero(): - raise ValueError("zero cannot be used for subdivision.") + raise ValueError("zero cannot be used for subdivision") r_n = vector(r).normalized() r_n.set_immutable() if r_n not in rays_normalized: From f5be3fa7f178d9ab0390374781f8f16ca1b86b39 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sat, 29 May 2021 22:50:09 -0400 Subject: [PATCH 084/336] fix typo --- src/sage/homology/polyhedral_complex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/homology/polyhedral_complex.py index d3deea30fc0..8f5006af093 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/homology/polyhedral_complex.py @@ -7,7 +7,7 @@ A polyhedral complex `PC` is a collection of polyhedra in a certain ambient space `\RR^n` such that -- If a poyhedron `P` is in `PC`, then all the faces of `P` are in `PC`. +- If a polyhedron `P` is in `PC`, then all the faces of `P` are in `PC`. - If polyhedra `P` and `Q` are in `PC`, then `P \cap Q` is either empty or a face of both `P` and `Q`. From f18b8218f1e95348fb78aaeebc4b857c9a06fd9b Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 1 Jun 2021 14:35:47 +0200 Subject: [PATCH 085/336] add doctest --- src/sage/symbolic/integration/external.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 76604397d57..22626f83a03 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -387,10 +387,17 @@ def fricas_integrator(expression, v, a=None, b=None, noPole=True): Check that in case of failure one gets unevaluated integral:: - sage: integral(cos(ln(cos(x))), x, 0, pi/8, algorithm='fricas') # optional - fricas + sage: integral(cos(ln(cos(x))), x, 0, pi/8, algorithm='fricas') # optional - fricas integrate(cos(log(cos(x))), x, 0, 1/8*pi) - sage: integral(cos(ln(cos(x))), x, algorithm='fricas') # optional - fricas + sage: integral(cos(ln(cos(x))), x, algorithm='fricas') # optional - fricas integral(cos(log(cos(x))), x) + + Check that :trac:`29043` is fixed:: + + sage: var("a c d"); f = (I*a*tan(d*x + c) + a)*sec(d*x + c)^10 + (a, c, d) + sage: integrate(f, x, algorithm="fricas") # optional - fricas + 1/315*(64512*I*a*e^(10*I*d*x + 10*I*c) + 53760*I*a*e^(8*I*d*x + 8*I*c) + 30720*I*a*e^(6*I*d*x + 6*I*c) + 11520*I*a*e^(4*I*d*x + 4*I*c) + 2560*I*a*e^(2*I*d*x + 2*I*c) + 256*I*a)/(d*e^(20*I*d*x + 20*I*c) + 10*d*e^(18*I*d*x + 18*I*c) + 45*d*e^(16*I*d*x + 16*I*c) + 120*d*e^(14*I*d*x + 14*I*c) + 210*d*e^(12*I*d*x + 12*I*c) + 252*d*e^(10*I*d*x + 10*I*c) + 210*d*e^(8*I*d*x + 8*I*c) + 120*d*e^(6*I*d*x + 6*I*c) + 45*d*e^(4*I*d*x + 4*I*c) + 10*d*e^(2*I*d*x + 2*I*c) + d) """ if not isinstance(expression, Expression): expression = SR(expression) From 0bf981c8b8b7dd08151f85ef7eb3f83f0c40b90b Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 2 Jun 2021 13:47:48 +0200 Subject: [PATCH 086/336] provide doctest --- src/sage/symbolic/integration/external.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 22626f83a03..cab2439c611 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -392,6 +392,12 @@ def fricas_integrator(expression, v, a=None, b=None, noPole=True): sage: integral(cos(ln(cos(x))), x, algorithm='fricas') # optional - fricas integral(cos(log(cos(x))), x) + Check that :trac:`28630` is fixed:: + + sage: f = polylog(3, x) + sage: f.integral(x, algorithm='fricas') # optional - fricas + -x*dilog(x) - (x - 1)*log(-x + 1) + x*polylog(3, x) + x + Check that :trac:`29043` is fixed:: sage: var("a c d"); f = (I*a*tan(d*x + c) + a)*sec(d*x + c)^10 From 32f9966a2bcb029727ff4f1f51c6b945fbeac367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 8 Jun 2021 13:28:19 +1200 Subject: [PATCH 087/336] update the patches for 5.10.1 --- .../patches/01-no_cmake_project.patch | 50 ++- .../suitesparse/patches/02-darwin_blas.patch | 14 +- .../suitesparse/patches/03-buildflags.patch | 33 +- .../pkgs/suitesparse/patches/04-cygwin.patch | 297 +++++++++--------- 4 files changed, 199 insertions(+), 195 deletions(-) diff --git a/build/pkgs/suitesparse/patches/01-no_cmake_project.patch b/build/pkgs/suitesparse/patches/01-no_cmake_project.patch index ccde30de327..8b2b1a2666c 100644 --- a/build/pkgs/suitesparse/patches/01-no_cmake_project.patch +++ b/build/pkgs/suitesparse/patches/01-no_cmake_project.patch @@ -1,24 +1,24 @@ diff --git a/Makefile b/Makefile -index 74941a6..70e7b68 100644 +index abd00d0f..10c31390 100644 --- a/Makefile +++ b/Makefile -@@ -12,7 +12,6 @@ include SuiteSparse_config/SuiteSparse_config.mk - # Compile the default rules for each package +@@ -16,7 +16,6 @@ include SuiteSparse_config/SuiteSparse_config.mk + # installs all libraries SuiteSparse/lib. go: metis ( cd SuiteSparse_config && $(MAKE) ) -- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) +- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) ( cd AMD && $(MAKE) ) ( cd BTF && $(MAKE) ) ( cd CAMD && $(MAKE) ) -@@ -30,7 +29,6 @@ ifneq ($(GPU_CONFIG),) +@@ -34,7 +33,6 @@ ifneq ($(GPU_CONFIG),) ( cd GPUQREngine && $(MAKE) ) endif ( cd SPQR && $(MAKE) ) - ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) + ( cd SLIP_LU && $(MAKE) ) # ( cd PIRO_BAND && $(MAKE) ) # ( cd SKYLINE_SVD && $(MAKE) ) - -@@ -38,7 +36,6 @@ endif +@@ -46,7 +44,6 @@ endif # (note that CSparse is not installed; CXSparse is installed instead) install: metisinstall ( cd SuiteSparse_config && $(MAKE) install ) @@ -26,15 +26,15 @@ index 74941a6..70e7b68 100644 ( cd AMD && $(MAKE) install ) ( cd BTF && $(MAKE) install ) ( cd CAMD && $(MAKE) install ) -@@ -55,7 +52,6 @@ ifneq (,$(GPU_CONFIG)) +@@ -63,7 +60,6 @@ ifneq (,$(GPU_CONFIG)) ( cd GPUQREngine && $(MAKE) install ) endif ( cd SPQR && $(MAKE) install ) - ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) # ( cd PIRO_BAND && $(MAKE) install ) # ( cd SKYLINE_SVD && $(MAKE) install ) - $(CP) README.txt $(INSTALL_DOC)/SuiteSparse_README.txt -@@ -116,7 +112,6 @@ endif + ( cd SLIP_LU && $(MAKE) install ) +@@ -126,7 +122,6 @@ endif # the static library library: metis ( cd SuiteSparse_config && $(MAKE) ) @@ -42,15 +42,15 @@ index 74941a6..70e7b68 100644 ( cd AMD && $(MAKE) library ) ( cd BTF && $(MAKE) library ) ( cd CAMD && $(MAKE) library ) -@@ -134,7 +129,6 @@ ifneq (,$(GPU_CONFIG)) +@@ -144,7 +139,6 @@ ifneq (,$(GPU_CONFIG)) ( cd GPUQREngine && $(MAKE) library ) endif ( cd SPQR && $(MAKE) library ) - ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' library ) + ( cd SLIP_LU && $(MAKE) library ) # ( cd PIRO_BAND && $(MAKE) library ) # ( cd SKYLINE_SVD && $(MAKE) library ) - -@@ -143,7 +137,6 @@ endif +@@ -154,7 +148,6 @@ endif # both the dynamic and static libraries. static: metis ( cd SuiteSparse_config && $(MAKE) static ) @@ -58,11 +58,33 @@ index 74941a6..70e7b68 100644 ( cd AMD && $(MAKE) static ) ( cd BTF && $(MAKE) static ) ( cd CAMD && $(MAKE) static ) -@@ -161,7 +154,6 @@ ifneq (,$(GPU_CONFIG)) +@@ -172,7 +165,6 @@ ifneq (,$(GPU_CONFIG)) ( cd GPUQREngine && $(MAKE) static ) endif ( cd SPQR && $(MAKE) static ) - ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' static ) + ( cd SLIP_LU && $(MAKE) static ) # ( cd PIRO_BAND && $(MAKE) static ) # ( cd SKYLINE_SVD && $(MAKE) static ) +@@ -291,21 +283,3 @@ else + @echo 'Using pre-installed METIS 5.1.0 library at ' '[$(MY_METIS_LIB)]' + endif +-# just compile GraphBLAS +-gb: +- echo $(CMAKE_OPTIONS) +- ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) +- +-# just install GraphBLAS +-gbinstall: +- echo $(CMAKE_OPTIONS) +- ( cd GraphBLAS && $(MAKE) JOBS=$(JOBS) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) +- +-# just compile Mongoose +-mon: +- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' ) +- +-# just install Mongoose +-moninstall: +- ( cd Mongoose && $(MAKE) CMAKE_OPTIONS='$(CMAKE_OPTIONS)' install ) +- diff --git a/build/pkgs/suitesparse/patches/02-darwin_blas.patch b/build/pkgs/suitesparse/patches/02-darwin_blas.patch index bfd0e0bc548..546bfc80642 100644 --- a/build/pkgs/suitesparse/patches/02-darwin_blas.patch +++ b/build/pkgs/suitesparse/patches/02-darwin_blas.patch @@ -1,15 +1,15 @@ diff --git a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk -index 0a09188..90b2e57 100644 +index 2fe1ab09..352ff593 100644 --- a/SuiteSparse_config/SuiteSparse_config.mk +++ b/SuiteSparse_config/SuiteSparse_config.mk -@@ -370,8 +370,8 @@ SUITESPARSE_VERSION = 5.6.0 +@@ -370,8 +370,8 @@ SUITESPARSE_VERSION = 5.10.1 # command line in the Terminal, before doing 'make': # xcode-select --install CF += -fno-common -- BLAS = -framework Accelerate -- LAPACK = -framework Accelerate -+ #BLAS = -framework Accelerate -+ #LAPACK = -framework Accelerate +- BLAS ?= -framework Accelerate +- LAPACK ?= -framework Accelerate ++ #BLAS ?= -framework Accelerate ++ #LAPACK ?= -framework Accelerate # OpenMP is not yet supported by default in clang CFOPENMP = - endif + LDLIBS += -rpath $(INSTALL_LIB) diff --git a/build/pkgs/suitesparse/patches/03-buildflags.patch b/build/pkgs/suitesparse/patches/03-buildflags.patch index 20da6535d82..8a2c312be39 100644 --- a/build/pkgs/suitesparse/patches/03-buildflags.patch +++ b/build/pkgs/suitesparse/patches/03-buildflags.patch @@ -1,26 +1,8 @@ -Inspired by, but not identical to the following patch from debian: -https://sources.debian.org/patches/suitesparse/1:5.6.0+dfsg-2/buildflags.patch/ - -This prepends $(DESTDIR) directly to all of $(INSTALL_LIB), $(INSTALL_INCLUDE), -and $(INSTALL_DOC), as these variables only affect where built files are copied -to, and do not affect the contents of the files. - -However, we also remove some additional LDFLAGS added by this Makefile (for - -L$SAGE_LOCAL and -Wl,-rpath=$SAGE_LOCAL/lib) since we already add those -in the standard SPKG built environment. -Note that -L$(INSTALL_LIB) cannot be removed from LDFLAGS unless the package -is broken into components. It is necessary as the components are progressively -installed in INSTALL_LIB one after the other. -Because we don't install directly in the final location, install_name also becomes -wrong on OS X which means the libraries cannot be loaded. So we have to provide -a correct install_name as well. - -For https://trac.sagemath.org/ticket/28832 diff --git a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk -index 0a09188..fd1c0b0 100644 +index 352ff593..61f0076e 100644 --- a/SuiteSparse_config/SuiteSparse_config.mk +++ b/SuiteSparse_config/SuiteSparse_config.mk -@@ -55,9 +55,9 @@ SUITESPARSE_VERSION = 5.6.0 +@@ -72,9 +72,9 @@ SUITESPARSE_VERSION = 5.10.1 # which puts the libraries in /yada/mylibs, include files in /yoda/myinc, # and documentation in /solo/mydox. INSTALL ?= $(SUITESPARSE) @@ -33,7 +15,7 @@ index 0a09188..fd1c0b0 100644 #--------------------------------------------------------------------------- # parallel make -@@ -358,7 +358,7 @@ SUITESPARSE_VERSION = 5.6.0 +@@ -358,7 +358,7 @@ SUITESPARSE_VERSION = 5.10.1 ifeq ($(UNAME),Linux) # add the realtime library, librt, and SuiteSparse/lib @@ -42,13 +24,12 @@ index 0a09188..fd1c0b0 100644 endif #--------------------------------------------------------------------------- -@@ -463,7 +463,8 @@ else +@@ -464,7 +464,7 @@ else SO_TARGET = $(LIBRARY).$(VERSION).dylib SO_OPTS += -dynamiclib -compatibility_version $(SO_VERSION) \ -current_version $(VERSION) \ -- -shared -undefined dynamic_lookup -+ -shared -undefined dynamic_lookup \ -+ -install_name $(INSTALL)/lib/$(SO_MAIN) +- -Wl,-install_name -Wl,@rpath/$(SO_MAIN) \ ++ -Wl,-install_name -Wl,$(INSTALL)/lib/$(SO_MAIN) \ + -shared -undefined dynamic_lookup # When a Mac *.dylib file is moved, this command is required # to change its internal name to match its location in the filesystem: - SO_INSTALL_NAME = install_name_tool -id diff --git a/build/pkgs/suitesparse/patches/04-cygwin.patch b/build/pkgs/suitesparse/patches/04-cygwin.patch index 803a395e86d..82662f16d89 100644 --- a/build/pkgs/suitesparse/patches/04-cygwin.patch +++ b/build/pkgs/suitesparse/patches/04-cygwin.patch @@ -1,21 +1,7 @@ -Patch to support installing correctly on Cygwin. - -This includes: - -* Installing DLLs under bin/ with a cyg- prefix instead of lib- -* Creating and installing import libs (.dll.a files) in lib/ - -For the time being versioning of import libs is not used, but it is -for DLLs. - -This unfortunately requires a patch bomb since all of SuiteSparse's -libraries have their own Makefiles even though they are near copies -of each other with minor differences. - -See https://trac.sagemath.org/ticket/28829 -diff -ruN a/AMD/Lib/Makefile b/AMD/Lib/Makefile ---- a/AMD/Lib/Makefile 2019-12-02 16:05:04.856283600 +0100 -+++ b/AMD/Lib/Makefile 2019-12-02 18:12:09.753790100 +0100 +diff --git a/AMD/Lib/Makefile b/AMD/Lib/Makefile +index 3b92f6a0..582bef2a 100644 +--- a/AMD/Lib/Makefile ++++ b/AMD/Lib/Makefile @@ -2,7 +2,7 @@ # AMD Lib/Makefile #------------------------------------------------------------------------------- @@ -25,7 +11,7 @@ diff -ruN a/AMD/Lib/Makefile b/AMD/Lib/Makefile VERSION = 2.4.6 SO_VERSION = 2 -@@ -81,19 +81,22 @@ +@@ -81,19 +81,22 @@ libamdf77.a: $(AMDF77) #------------------------------------------------------------------------------- # install AMD @@ -51,9 +37,10 @@ diff -ruN a/AMD/Lib/Makefile b/AMD/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/amd.h chmod 644 $(INSTALL_DOC)/AMD_UserGuide.pdf chmod 644 $(INSTALL_DOC)/AMD_README.txt -diff -ruN a/BTF/Lib/Makefile b/BTF/Lib/Makefile ---- a/BTF/Lib/Makefile 2019-12-02 16:04:51.542288600 +0100 -+++ b/BTF/Lib/Makefile 2019-12-02 18:12:16.908303300 +0100 +diff --git a/BTF/Lib/Makefile b/BTF/Lib/Makefile +index 85b7a264..6e4e2ec4 100644 +--- a/BTF/Lib/Makefile ++++ b/BTF/Lib/Makefile @@ -2,7 +2,7 @@ # BTF Lib/Makefile #------------------------------------------------------------------------------- @@ -63,7 +50,7 @@ diff -ruN a/BTF/Lib/Makefile b/BTF/Lib/Makefile VERSION = 1.2.6 SO_VERSION = 1 -@@ -66,18 +66,21 @@ +@@ -66,18 +66,21 @@ btf_l_strongcomp.o: ../Source/btf_strongcomp.c #------------------------------------------------------------------------------- # install BTF @@ -88,9 +75,10 @@ diff -ruN a/BTF/Lib/Makefile b/BTF/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/btf.h chmod 644 $(INSTALL_DOC)/BTF_README.txt -diff -ruN a/CAMD/Lib/Makefile b/CAMD/Lib/Makefile ---- a/CAMD/Lib/Makefile 2019-12-02 16:04:50.051276500 +0100 -+++ b/CAMD/Lib/Makefile 2019-12-02 18:12:07.722810200 +0100 +diff --git a/CAMD/Lib/Makefile b/CAMD/Lib/Makefile +index b3ae159e..27cb072a 100644 +--- a/CAMD/Lib/Makefile ++++ b/CAMD/Lib/Makefile @@ -2,7 +2,7 @@ # CAMD Lib/Makefile #------------------------------------------------------------------------------- @@ -100,7 +88,7 @@ diff -ruN a/CAMD/Lib/Makefile b/CAMD/Lib/Makefile VERSION = 2.4.6 SO_VERSION = 2 -@@ -62,19 +62,22 @@ +@@ -62,19 +62,22 @@ $(AR_TARGET): $(OBJ) #------------------------------------------------------------------------------- # install CAMD @@ -126,9 +114,10 @@ diff -ruN a/CAMD/Lib/Makefile b/CAMD/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/camd.h chmod 644 $(INSTALL_DOC)/CAMD_UserGuide.pdf chmod 644 $(INSTALL_DOC)/CAMD_README.txt -diff -ruN a/CCOLAMD/Lib/Makefile b/CCOLAMD/Lib/Makefile ---- a/CCOLAMD/Lib/Makefile 2019-12-02 16:04:49.279277800 +0100 -+++ b/CCOLAMD/Lib/Makefile 2019-12-02 18:12:22.610632700 +0100 +diff --git a/CCOLAMD/Lib/Makefile b/CCOLAMD/Lib/Makefile +index 52c52eb9..18190be0 100644 +--- a/CCOLAMD/Lib/Makefile ++++ b/CCOLAMD/Lib/Makefile @@ -2,7 +2,7 @@ # CCOLAMD Lib/Makefile #------------------------------------------------------------------------------- @@ -138,7 +127,7 @@ diff -ruN a/CCOLAMD/Lib/Makefile b/CCOLAMD/Lib/Makefile VERSION = 2.9.6 SO_VERSION = 2 -@@ -49,18 +49,21 @@ +@@ -49,18 +49,21 @@ distclean: clean - $(RM) -r $(PURGE) # install CCOLAMD @@ -163,19 +152,20 @@ diff -ruN a/CCOLAMD/Lib/Makefile b/CCOLAMD/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/ccolamd.h chmod 644 $(INSTALL_DOC)/CCOLAMD_README.txt -diff -ruN a/CHOLMOD/Lib/Makefile b/CHOLMOD/Lib/Makefile ---- a/CHOLMOD/Lib/Makefile 2019-12-02 16:04:51.453293800 +0100 -+++ b/CHOLMOD/Lib/Makefile 2019-12-02 18:12:16.627302900 +0100 +diff --git a/CHOLMOD/Lib/Makefile b/CHOLMOD/Lib/Makefile +index bc3eab69..d240b4cd 100644 +--- a/CHOLMOD/Lib/Makefile ++++ b/CHOLMOD/Lib/Makefile @@ -2,7 +2,7 @@ # CHOLMOD/Lib/Makefile: for compiling the CHOLMOD library #=============================================================================== -LIBRARY = libcholmod +LIBRARY = cholmod - VERSION = 3.0.13 + VERSION = 3.0.14 SO_VERSION = 3 -@@ -535,20 +535,23 @@ +@@ -535,20 +535,23 @@ cholmod_l_gpu.o: ../GPU/cholmod_gpu.c #------------------------------------------------------------------------------- # install CHOLMOD @@ -188,7 +178,7 @@ diff -ruN a/CHOLMOD/Lib/Makefile b/CHOLMOD/Lib/Makefile + @mkdir -p $(dir $(INSTALL_SO)) @mkdir -p $(INSTALL_INCLUDE) @mkdir -p $(INSTALL_DOC) - $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) + $(CXX) $(SO_OPTS) $^ -o $@ $(LDLIBS) +ifneq ($(UNAME),Cygwin) ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) @@ -196,16 +186,16 @@ diff -ruN a/CHOLMOD/Lib/Makefile b/CHOLMOD/Lib/Makefile +endif $(CP) ../Include/cholmod*.h $(INSTALL_INCLUDE) $(RM) $(INSTALL_INCLUDE)/cholmod_internal.h -- $(CP) ../Doc/CHOLMOD_UserGuide.pdf $(INSTALL_DOC) + $(CP) ../Doc/CHOLMOD_UserGuide.pdf $(INSTALL_DOC) $(CP) ../README.txt $(INSTALL_DOC)/CHOLMOD_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+ $(CP) ../Doc/CHOLMOD_UserGuide.pdf $(INSTALL_DOC) chmod 644 $(INSTALL_INCLUDE)/cholmod*.h chmod 644 $(INSTALL_DOC)/CHOLMOD_UserGuide.pdf chmod 644 $(INSTALL_DOC)/CHOLMOD_README.txt -diff -ruN a/COLAMD/Lib/Makefile b/COLAMD/Lib/Makefile ---- a/COLAMD/Lib/Makefile 2019-12-02 16:04:58.545276600 +0100 -+++ b/COLAMD/Lib/Makefile 2019-12-02 18:12:17.545302000 +0100 +diff --git a/COLAMD/Lib/Makefile b/COLAMD/Lib/Makefile +index 2d0c5aa6..26720b62 100644 +--- a/COLAMD/Lib/Makefile ++++ b/COLAMD/Lib/Makefile @@ -2,7 +2,7 @@ # COLAMD Lib/Makefile #------------------------------------------------------------------------------- @@ -215,7 +205,7 @@ diff -ruN a/COLAMD/Lib/Makefile b/COLAMD/Lib/Makefile VERSION = 2.9.6 SO_VERSION = 2 -@@ -49,18 +49,21 @@ +@@ -49,18 +49,21 @@ distclean: clean - $(RM) -r $(PURGE) # install COLAMD @@ -240,9 +230,10 @@ diff -ruN a/COLAMD/Lib/Makefile b/COLAMD/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/colamd.h chmod 644 $(INSTALL_DOC)/COLAMD_README.txt -diff -ruN a/CSparse/Lib/Makefile b/CSparse/Lib/Makefile ---- a/CSparse/Lib/Makefile 2019-12-02 16:04:58.059294300 +0100 -+++ b/CSparse/Lib/Makefile 2019-12-02 18:12:09.158797700 +0100 +diff --git a/CSparse/Lib/Makefile b/CSparse/Lib/Makefile +index e3f32cec..468f7a47 100644 +--- a/CSparse/Lib/Makefile ++++ b/CSparse/Lib/Makefile @@ -14,7 +14,7 @@ # install' in this Makefile only installs a static compiled library in # CSparse/Lib. It does not install it for system-wide usage. @@ -252,9 +243,10 @@ diff -ruN a/CSparse/Lib/Makefile b/CSparse/Lib/Makefile CF = $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -O I = -I../Include -diff -ruN a/CXSparse/Lib/Makefile b/CXSparse/Lib/Makefile ---- a/CXSparse/Lib/Makefile 2019-12-02 16:04:49.462296500 +0100 -+++ b/CXSparse/Lib/Makefile 2019-12-02 18:12:23.167631600 +0100 +diff --git a/CXSparse/Lib/Makefile b/CXSparse/Lib/Makefile +index a21e3607..270b8027 100644 +--- a/CXSparse/Lib/Makefile ++++ b/CXSparse/Lib/Makefile @@ -2,7 +2,7 @@ # CXSparse Lib/Makefile #------------------------------------------------------------------------------- @@ -264,7 +256,7 @@ diff -ruN a/CXSparse/Lib/Makefile b/CXSparse/Lib/Makefile VERSION = 3.2.0 SO_VERSION = 3 -@@ -113,18 +113,21 @@ +@@ -113,18 +113,21 @@ distclean: clean - $(RM) -r $(PURGE) # install CXSparse @@ -289,9 +281,10 @@ diff -ruN a/CXSparse/Lib/Makefile b/CXSparse/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/cs.h chmod 644 $(INSTALL_DOC)/CXSPARSE_README.txt -diff -ruN a/CXSparse_newfiles/Lib/Makefile b/CXSparse_newfiles/Lib/Makefile ---- a/CXSparse_newfiles/Lib/Makefile 2019-12-02 16:05:04.505277300 +0100 -+++ b/CXSparse_newfiles/Lib/Makefile 2019-12-02 18:12:08.029790200 +0100 +diff --git a/CXSparse_newfiles/Lib/Makefile b/CXSparse_newfiles/Lib/Makefile +index a21e3607..270b8027 100644 +--- a/CXSparse_newfiles/Lib/Makefile ++++ b/CXSparse_newfiles/Lib/Makefile @@ -2,7 +2,7 @@ # CXSparse Lib/Makefile #------------------------------------------------------------------------------- @@ -301,7 +294,7 @@ diff -ruN a/CXSparse_newfiles/Lib/Makefile b/CXSparse_newfiles/Lib/Makefile VERSION = 3.2.0 SO_VERSION = 3 -@@ -113,18 +113,21 @@ +@@ -113,18 +113,21 @@ distclean: clean - $(RM) -r $(PURGE) # install CXSparse @@ -326,9 +319,10 @@ diff -ruN a/CXSparse_newfiles/Lib/Makefile b/CXSparse_newfiles/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/cs.h chmod 644 $(INSTALL_DOC)/CXSPARSE_README.txt -diff -ruN a/GPUQREngine/Lib/Makefile b/GPUQREngine/Lib/Makefile ---- a/GPUQREngine/Lib/Makefile 2019-12-02 16:04:50.027276700 +0100 -+++ b/GPUQREngine/Lib/Makefile 2019-12-02 18:12:07.617802700 +0100 +diff --git a/GPUQREngine/Lib/Makefile b/GPUQREngine/Lib/Makefile +index adf46039..7bb34807 100644 +--- a/GPUQREngine/Lib/Makefile ++++ b/GPUQREngine/Lib/Makefile @@ -2,7 +2,7 @@ # GPUQREngine/Lib/Makefile: for compiling the GPUQREngine library #------------------------------------------------------------------------------- @@ -338,7 +332,7 @@ diff -ruN a/GPUQREngine/Lib/Makefile b/GPUQREngine/Lib/Makefile VERSION = 1.0.5 SO_VERSION = 1 -@@ -129,17 +129,20 @@ +@@ -129,17 +129,20 @@ $(AR_TARGET): $(OBJS) #------------------------------------------------------------------------------- # install GPUQREngine. Note that the include files are not installed. @@ -362,9 +356,10 @@ diff -ruN a/GPUQREngine/Lib/Makefile b/GPUQREngine/Lib/Makefile chmod 644 $(INSTALL_DOC)/GPUQRENGINE_README.txt # uninstall GPUQREngine -diff -ruN a/KLU/Lib/Makefile b/KLU/Lib/Makefile ---- a/KLU/Lib/Makefile 2019-12-02 16:04:51.621283700 +0100 -+++ b/KLU/Lib/Makefile 2019-12-02 18:12:09.148789600 +0100 +diff --git a/KLU/Lib/Makefile b/KLU/Lib/Makefile +index 2dc62cb4..35e2ed3a 100644 +--- a/KLU/Lib/Makefile ++++ b/KLU/Lib/Makefile @@ -2,7 +2,7 @@ # KLU Lib/Makefile #------------------------------------------------------------------------------- @@ -374,7 +369,7 @@ diff -ruN a/KLU/Lib/Makefile b/KLU/Lib/Makefile VERSION = 1.3.8 SO_VERSION = 1 -@@ -263,19 +263,22 @@ +@@ -263,19 +263,22 @@ klu_l_memory.o: ../Source/klu_memory.c #------------------------------------------------------------------------------- # install KLU @@ -400,9 +395,10 @@ diff -ruN a/KLU/Lib/Makefile b/KLU/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/klu.h chmod 644 $(INSTALL_DOC)/KLU_UserGuide.pdf chmod 644 $(INSTALL_DOC)/KLU_README.txt -diff -ruN a/LDL/Lib/Makefile b/LDL/Lib/Makefile ---- a/LDL/Lib/Makefile 2019-12-02 16:04:51.803294200 +0100 -+++ b/LDL/Lib/Makefile 2019-12-02 18:12:17.500303700 +0100 +diff --git a/LDL/Lib/Makefile b/LDL/Lib/Makefile +index 604ffa27..da5ecd53 100644 +--- a/LDL/Lib/Makefile ++++ b/LDL/Lib/Makefile @@ -2,7 +2,7 @@ # LDL Lib/Makefile #------------------------------------------------------------------------------- @@ -412,7 +408,7 @@ diff -ruN a/LDL/Lib/Makefile b/LDL/Lib/Makefile VERSION = 2.2.6 SO_VERSION = 2 -@@ -46,19 +46,22 @@ +@@ -46,19 +46,22 @@ clean: - $(RM) -r $(CLEAN) # install LDL @@ -438,9 +434,10 @@ diff -ruN a/LDL/Lib/Makefile b/LDL/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/ldl.h chmod 644 $(INSTALL_DOC)/ldl_userguide.pdf chmod 644 $(INSTALL_DOC)/LDL_README.txt -diff -ruN a/RBio/Lib/Makefile b/RBio/Lib/Makefile ---- a/RBio/Lib/Makefile 2019-12-02 16:05:04.771295500 +0100 -+++ b/RBio/Lib/Makefile 2019-12-02 18:12:09.016789800 +0100 +diff --git a/RBio/Lib/Makefile b/RBio/Lib/Makefile +index 056715de..4ba42157 100644 +--- a/RBio/Lib/Makefile ++++ b/RBio/Lib/Makefile @@ -2,7 +2,7 @@ # RBio/Lib/Makefile: for compiling the RBio library #=============================================================================== @@ -450,7 +447,7 @@ diff -ruN a/RBio/Lib/Makefile b/RBio/Lib/Makefile VERSION = 2.2.6 SO_VERSION = 2 -@@ -60,18 +60,21 @@ +@@ -60,18 +60,21 @@ RBio_i.o: ../Source/RBio.c #------------------------------------------------------------------------------- # install RBio @@ -475,9 +472,10 @@ diff -ruN a/RBio/Lib/Makefile b/RBio/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/RBio.h chmod 644 $(INSTALL_DOC)/RBIO_README.txt -diff -ruN a/SPQR/Lib/Makefile b/SPQR/Lib/Makefile ---- a/SPQR/Lib/Makefile 2019-12-02 16:04:50.190277100 +0100 -+++ b/SPQR/Lib/Makefile 2019-12-02 18:12:07.955789500 +0100 +diff --git a/SPQR/Lib/Makefile b/SPQR/Lib/Makefile +index 06c142ae..10e32007 100644 +--- a/SPQR/Lib/Makefile ++++ b/SPQR/Lib/Makefile @@ -2,7 +2,7 @@ # SuiteSparseQR/Lib/Makefile #=============================================================================== @@ -487,7 +485,7 @@ diff -ruN a/SPQR/Lib/Makefile b/SPQR/Lib/Makefile VERSION = 2.0.9 SO_VERSION = 2 -@@ -242,22 +242,25 @@ +@@ -242,22 +242,25 @@ spqrgpu_computeFrontStaging.o: ../SPQRGPU/spqrgpu_computeFrontStaging.cpp #------------------------------------------------------------------------------- # install SPQR @@ -516,19 +514,57 @@ diff -ruN a/SPQR/Lib/Makefile b/SPQR/Lib/Makefile chmod 644 $(INSTALL_INCLUDE)/SuiteSparseQR.hpp chmod 644 $(INSTALL_INCLUDE)/SuiteSparseQR_C.h chmod 644 $(INSTALL_INCLUDE)/SuiteSparseQR_definitions.h -diff -ruN a/SuiteSparse_config/Makefile b/SuiteSparse_config/Makefile ---- a/SuiteSparse_config/Makefile 2019-12-02 16:05:03.939278100 +0100 -+++ b/SuiteSparse_config/Makefile 2019-12-02 18:12:16.275303600 +0100 -@@ -6,7 +6,7 @@ +diff --git a/SuiteSparse_GPURuntime/Lib/Makefile b/SuiteSparse_GPURuntime/Lib/Makefile +index 31f536a5..2f6c54d0 100644 +--- a/SuiteSparse_GPURuntime/Lib/Makefile ++++ b/SuiteSparse_GPURuntime/Lib/Makefile +@@ -2,7 +2,7 @@ + # SuiteSparse_GPURuntime/Lib/Makfile + #------------------------------------------------------------------------------- + +-LIBRARY = libSuiteSparse_GPURuntime ++LIBRARY = SuiteSparse_GPURuntime + VERSION = 1.0.5 + SO_VERSION = 1 + +@@ -70,17 +70,20 @@ SuiteSparseGPU_Workspace_transfer.o: ../Source/SuiteSparseGPU_Workspace_transfer + #------------------------------------------------------------------------------- + + # install SuiteSparse_GPURuntime (just the library, not the include files) +-install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) ++install: $(AR_TARGET) $(INSTALL_SO) + +-$(INSTALL_LIB)/$(SO_TARGET): $(OBJS) ++$(INSTALL_SO): $(OBJS) + @mkdir -p $(INSTALL_LIB) ++ @mkdir -p $(dir $(INSTALL_SO)) + @mkdir -p $(INSTALL_INCLUDE) + @mkdir -p $(INSTALL_DOC) + $(CXX) $(SO_OPTS) $^ -o $@ $(LDLIBS) ++ifneq ($(UNAME),Cygwin) + ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) + ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) +- $(CP) ../README.txt $(INSTALL_DOC)/GPURUNTIME_README.txt + chmod 755 $(INSTALL_LIB)/$(SO_TARGET) ++endif ++ $(CP) ../README.txt $(INSTALL_DOC)/GPURUNTIME_README.txt + chmod 644 $(INSTALL_DOC)/GPURUNTIME_README.txt + + # uninstall SuiteSparse_GPURuntime +diff --git a/SuiteSparse_config/Makefile b/SuiteSparse_config/Makefile +index 6a5814d4..e2acc53e 100644 +--- a/SuiteSparse_config/Makefile ++++ b/SuiteSparse_config/Makefile +@@ -6,7 +6,7 @@ SUITESPARSE ?= $(realpath $(CURDIR)/..) export SUITESPARSE # version of SuiteSparse_config is also version of SuiteSparse meta-package -LIBRARY = libsuitesparseconfig +LIBRARY = suitesparseconfig - VERSION = 5.5.0 + VERSION = 5.10.1 SO_VERSION = 5 -@@ -44,19 +44,23 @@ +@@ -44,19 +44,23 @@ clean: - $(RM) -r $(CLEAN) # install SuiteSparse_config @@ -556,26 +592,13 @@ diff -ruN a/SuiteSparse_config/Makefile b/SuiteSparse_config/Makefile chmod 644 $(INSTALL_INCLUDE)/SuiteSparse_config.h chmod 644 $(INSTALL_DOC)/SUITESPARSECONFIG_README.txt -diff -ruN a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk ---- a/SuiteSparse_config/SuiteSparse_config.mk 2019-12-02 16:05:03.944279100 +0100 -+++ b/SuiteSparse_config/SuiteSparse_config.mk 2019-12-02 18:12:16.277302200 +0100 -@@ -153,6 +153,11 @@ - # It places its shared *.so libraries in SuiteSparse/lib. - # Linux also requires the -lrt library (see below) - LDLIBS ?= -lm -+ # Note: Because suitesparse doesn't really do install staging properly -+ # (it just outputs final build artifacts directly to their install paths) -+ # we must add that path to our linker flags in order to link properly -+ # against built libraries; it might be better to fix the whole build -+ # system though). - LDFLAGS += -L$(INSTALL_LIB) - - # NOTE: Use of the Intel MKL BLAS is strongly recommended. The OpenBLAS can -@@ -340,16 +346,15 @@ - #--------------------------------------------------------------------------- - +diff --git a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk +index 61f0076e..5caa89dd 100644 +--- a/SuiteSparse_config/SuiteSparse_config.mk ++++ b/SuiteSparse_config/SuiteSparse_config.mk +@@ -16,14 +16,14 @@ SUITESPARSE_VERSION = 5.10.1 # To disable these auto configurations, use 'make UNAME=custom' -- + ifndef UNAME - ifeq ($(OS),Windows_NT) - # Cygwin Make on Windows has an $(OS) variable, but not uname. @@ -595,8 +618,20 @@ diff -ruN a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteS + UNAME := Cygwin endif - #--------------------------------------------------------------------------- -@@ -446,21 +451,23 @@ + #=============================================================================== +@@ -170,6 +170,11 @@ SUITESPARSE_VERSION = 5.10.1 + # It places its shared *.so libraries in SuiteSparse/lib. + # Linux also requires the -lrt library (see below) + LDLIBS ?= -lm ++ # Note: Because suitesparse doesn't really do install staging properly ++ # (it just outputs final build artifacts directly to their install paths) ++ # we must add that path to our linker flags in order to link properly ++ # against built libraries; it might be better to fix the whole build ++ # system though). + LDFLAGS += -L$(INSTALL_LIB) + + # NOTE: Use of the Intel MKL BLAS is strongly recommended. The OpenBLAS can +@@ -447,21 +452,23 @@ SUITESPARSE_VERSION = 5.10.1 SO_OPTS = $(LDFLAGS) @@ -630,8 +665,8 @@ diff -ruN a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteS + SO_TARGET = lib$(LIBRARY).$(VERSION).dylib SO_OPTS += -dynamiclib -compatibility_version $(SO_VERSION) \ -current_version $(VERSION) \ - -shared -undefined dynamic_lookup -@@ -469,9 +476,9 @@ + -Wl,-install_name -Wl,$(INSTALL)/lib/$(SO_MAIN) \ +@@ -471,9 +478,9 @@ else SO_INSTALL_NAME = install_name_tool -id else # Linux and other variants of Unix @@ -644,10 +679,11 @@ diff -ruN a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteS SO_OPTS += -shared -Wl,-soname -Wl,$(SO_MAIN) -Wl,--no-undefined # Linux/Unix *.so files can be moved without modification: SO_INSTALL_NAME = echo -diff -ruN a/SuiteSparse_config/xerbla/Makefile b/SuiteSparse_config/xerbla/Makefile ---- a/SuiteSparse_config/xerbla/Makefile 2019-12-02 16:05:03.949290500 +0100 -+++ b/SuiteSparse_config/xerbla/Makefile 2019-12-02 18:12:16.284303200 +0100 -@@ -16,9 +16,9 @@ +diff --git a/SuiteSparse_config/xerbla/Makefile b/SuiteSparse_config/xerbla/Makefile +index db68a2ea..c59820c2 100644 +--- a/SuiteSparse_config/xerbla/Makefile ++++ b/SuiteSparse_config/xerbla/Makefile +@@ -16,9 +16,9 @@ library: all: library ifeq ($(USE_FORTRAN),0) @@ -659,7 +695,7 @@ diff -ruN a/SuiteSparse_config/xerbla/Makefile b/SuiteSparse_config/xerbla/Makef endif include ../SuiteSparse_config.mk -@@ -44,19 +44,22 @@ +@@ -44,19 +44,22 @@ $(AR_TARGET): $(DEPENDS) - $(RM) xerbla.o # install libcerbla / libxerbla @@ -685,45 +721,10 @@ diff -ruN a/SuiteSparse_config/xerbla/Makefile b/SuiteSparse_config/xerbla/Makef chmod 644 $(INSTALL_INCLUDE)/xerbla.h # uninstall libcerbla / libxerbla -diff -ruN a/SuiteSparse_GPURuntime/Lib/Makefile b/SuiteSparse_GPURuntime/Lib/Makefile ---- a/SuiteSparse_GPURuntime/Lib/Makefile 2019-12-02 16:04:57.931276800 +0100 -+++ b/SuiteSparse_GPURuntime/Lib/Makefile 2019-12-02 18:12:08.912790400 +0100 -@@ -2,7 +2,7 @@ - # SuiteSparse_GPURuntime/Lib/Makfile - #------------------------------------------------------------------------------- - --LIBRARY = libSuiteSparse_GPURuntime -+LIBRARY = SuiteSparse_GPURuntime - VERSION = 1.0.5 - SO_VERSION = 1 - -@@ -70,17 +70,20 @@ - #------------------------------------------------------------------------------- - - # install SuiteSparse_GPURuntime (just the library, not the include files) --install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) -+install: $(AR_TARGET) $(INSTALL_SO) - --$(INSTALL_LIB)/$(SO_TARGET): $(OBJS) -+$(INSTALL_SO): $(OBJS) - @mkdir -p $(INSTALL_LIB) -+ @mkdir -p $(dir $(INSTALL_SO)) - @mkdir -p $(INSTALL_INCLUDE) - @mkdir -p $(INSTALL_DOC) - $(CXX) $(SO_OPTS) $^ -o $@ $(LDLIBS) -+ifneq ($(UNAME),Cygwin) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) - ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) -- $(CP) ../README.txt $(INSTALL_DOC)/GPURUNTIME_README.txt - chmod 755 $(INSTALL_LIB)/$(SO_TARGET) -+endif -+ $(CP) ../README.txt $(INSTALL_DOC)/GPURUNTIME_README.txt - chmod 644 $(INSTALL_DOC)/GPURUNTIME_README.txt - - # uninstall SuiteSparse_GPURuntime -diff -ruN a/UMFPACK/Lib/Makefile b/UMFPACK/Lib/Makefile ---- a/UMFPACK/Lib/Makefile 2019-12-02 16:05:03.957276500 +0100 -+++ b/UMFPACK/Lib/Makefile 2019-12-02 18:12:17.206308800 +0100 +diff --git a/UMFPACK/Lib/Makefile b/UMFPACK/Lib/Makefile +index 78436de1..0f291068 100644 +--- a/UMFPACK/Lib/Makefile ++++ b/UMFPACK/Lib/Makefile @@ -2,7 +2,7 @@ # UMFPACK Makefile for compiling on Unix systems #------------------------------------------------------------------------------- @@ -733,7 +734,7 @@ diff -ruN a/UMFPACK/Lib/Makefile b/UMFPACK/Lib/Makefile VERSION = 5.7.9 SO_VERSION = 5 -@@ -288,20 +288,23 @@ +@@ -288,20 +288,23 @@ clean: #------------------------------------------------------------------------------- # install UMFPACK From a01dcd2d235a7b20406ba959d776616076574758 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 12:50:46 -0700 Subject: [PATCH 088/336] PolymakeExpect: If startup fails, only mention deprecation once. Update polymake install info, fix doctest --- src/sage/interfaces/polymake.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 3b83652a583..6a60b34592a 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -187,10 +187,7 @@ def version(self): sage: Polymake(command='foobar').version() Traceback (most recent call last): ... - RuntimeError: unable to start polymake because the command 'foobar' failed: - The command was not found or was not executable: foobar. - Please install the optional polymake package for sage (but read its SPKG.txt first!) - or install polymake system-wide + RuntimeError: runtime error with deprecated pexpect-based interface to polymake; please install jupymake """ return self.get('$Polymake::Version') @@ -358,11 +355,12 @@ def _install_hints(self): TESTS:: sage: print(polymake._install_hints()) - Please install the optional polymake package for sage (but read its SPKG.txt first!) + Please install the optional polymake package for sage or install polymake system-wide + (use the shell command 'sage --info polymake' for more information) """ - return "Please install the optional polymake package for sage (but read its SPKG.txt first!)"+os.linesep+"or install polymake system-wide" + return "Please install the optional polymake package for sage" + os.linesep + "or install polymake system-wide" + os.linesep + "(use the shell command 'sage --info polymake' for more information)" def _start(self): """ @@ -1848,14 +1846,13 @@ def _start(self, alt_message=None): """ from sage.misc.superseded import deprecation - deprecation(27745, "the pexpect-based interface to polymake is deprecated. Install package jupymake so that Sage can use the more robust jupymake-based interface to polymake") - if not self.is_running(): try: self._change_prompt("polytope > ") Expect._start(self, alt_message=None) except RuntimeError: - raise RuntimeError("runtime error with deprecated pexpect-base interface to polymake; please install jupymake") + raise RuntimeError("runtime error with deprecated pexpect-based interface to polymake; please install jupymake") + deprecation(27745, "the pexpect-based interface to polymake is deprecated. Install package jupymake so that Sage can use the more robust jupymake-based interface to polymake") PolymakeAbstract._start(self) self.eval('use File::Slurp;') From 9b2558372686609cc7f57ccfb0acba428f76adf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Sun, 20 Jun 2021 13:22:34 +1200 Subject: [PATCH 089/336] update cygwin patch to include SLIP_LU --- .../pkgs/suitesparse/patches/04-cygwin.patch | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/build/pkgs/suitesparse/patches/04-cygwin.patch b/build/pkgs/suitesparse/patches/04-cygwin.patch index 82662f16d89..f4118f86670 100644 --- a/build/pkgs/suitesparse/patches/04-cygwin.patch +++ b/build/pkgs/suitesparse/patches/04-cygwin.patch @@ -472,6 +472,45 @@ index 056715de..4ba42157 100644 chmod 644 $(INSTALL_INCLUDE)/RBio.h chmod 644 $(INSTALL_DOC)/RBIO_README.txt +diff --git a/SLIP_LU/Lib/Makefile b/SLIP_LU/Lib/Makefile +index 8b2221f1..b2153824 100644 +--- a/SLIP_LU/Lib/Makefile ++++ b/SLIP_LU/Lib/Makefile +@@ -8,7 +8,7 @@ + # To run a demo using the library + # cd ../Demo ; make + +-LIBRARY = libsliplu ++LIBRARY = sliplu + VERSION = 1.0.2 + SO_VERSION = 1 + +@@ -63,19 +63,22 @@ $(AR_TARGET): $(OBJ) + #------------------------------------------------------------------------------- + + # install SLIP_LU +-install: $(AR_TARGET) $(INSTALL_LIB)/$(SO_TARGET) ++install: $(AR_TARGET) $(INSTALL_SO) + +-$(INSTALL_LIB)/$(SO_TARGET): $(OBJ) ++$(INSTALL_SO): $(OBJ) + @mkdir -p $(INSTALL_LIB) ++ @mkdir -p $(dir $(INSTALL_SO)) + @mkdir -p $(INSTALL_INCLUDE) + @mkdir -p $(INSTALL_DOC) + $(CC) $(SO_OPTS) $^ -o $@ $(LDLIBS) ++ifneq ($(UNAME),Cygwin) + ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_PLAIN) ) + ( cd $(INSTALL_LIB) ; ln -sf $(SO_TARGET) $(SO_MAIN) ) ++ chmod 755 $(INSTALL_LIB)/$(SO_TARGET) ++endif + $(CP) ../Include/SLIP_LU.h $(INSTALL_INCLUDE) + $(CP) ../Doc/SLIP_LU_UserGuide.pdf $(INSTALL_DOC) + $(CP) ../README.md $(INSTALL_DOC)/SLIP_LU_README.md +- chmod 755 $(INSTALL_LIB)/$(SO_TARGET) + chmod 644 $(INSTALL_INCLUDE)/SLIP_LU.h + chmod 644 $(INSTALL_DOC)/SLIP_LU_UserGuide.pdf + chmod 644 $(INSTALL_DOC)/SLIP_LU_README.md diff --git a/SPQR/Lib/Makefile b/SPQR/Lib/Makefile index 06c142ae..10e32007 100644 --- a/SPQR/Lib/Makefile From 0b51340f704eef2992641b1c3dbcf76f36a76eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Sun, 20 Jun 2021 13:23:12 +1200 Subject: [PATCH 090/336] Make sure C99 is used --- build/pkgs/suitesparse/patches/05-c99.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 build/pkgs/suitesparse/patches/05-c99.patch diff --git a/build/pkgs/suitesparse/patches/05-c99.patch b/build/pkgs/suitesparse/patches/05-c99.patch new file mode 100644 index 00000000000..c50ee322107 --- /dev/null +++ b/build/pkgs/suitesparse/patches/05-c99.patch @@ -0,0 +1,13 @@ +diff --git a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk +index 5caa89dd..1a1658f5 100644 +--- a/SuiteSparse_config/SuiteSparse_config.mk ++++ b/SuiteSparse_config/SuiteSparse_config.mk +@@ -154,7 +154,7 @@ SUITESPARSE_VERSION = 5.10.1 + + # The CF macro is used by SuiteSparse Makefiles as a combination of + # CFLAGS, CPPFLAGS, TARGET_ARCH, and system-dependent settings. +- CF ?= $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $(OPTIMIZATION) -fexceptions -fPIC ++ CF ?= -std=c99 $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $(OPTIMIZATION) -fexceptions -fPIC + + #--------------------------------------------------------------------------- + # code formatting (for Tcov on Linux only) From 8ba174cc014d892ab8defbb9453e847067af5b13 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 19 Jun 2021 20:23:42 -0700 Subject: [PATCH 091/336] Eliminate direct use of Chart._domain --- src/sage/manifolds/chart.py | 40 +++++++++---------- src/sage/manifolds/chart_func.py | 2 +- src/sage/manifolds/differentiable/chart.py | 20 +++++----- src/sage/manifolds/differentiable/diff_map.py | 4 +- .../differentiable/scalarfield_algebra.py | 2 +- src/sage/manifolds/manifold.py | 4 +- src/sage/manifolds/point.py | 2 +- src/sage/manifolds/scalarfield.py | 16 ++++---- src/sage/manifolds/scalarfield_algebra.py | 2 +- 9 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 6fb017d0082..6e26408b720 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -320,7 +320,7 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): # The chart is added to the domain's atlas, as well as to all the # atlases of the domain's supersets; moreover the first defined chart # is considered as the default chart - for sd in self._domain.open_supersets(): + for sd in domain.open_supersets(): # the chart is added in the top charts only if its coordinates have # not been used: for chart in sd._atlas: @@ -332,7 +332,7 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): if sd._def_chart is None: sd._def_chart = self # The chart is added to the list of the domain's covering charts: - self._domain._covering_charts.append(self) + domain._covering_charts.append(self) # Initialization of the set of charts that are restrictions of the # current chart to subsets of the chart domain: self._subcharts = set([self]) @@ -346,7 +346,7 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): # The null and one functions of the coordinates: # Expression in self of the zero and one scalar fields of open sets # containing the domain of self: - for dom in self._domain.open_supersets(): + for dom in domain.open_supersets(): dom._zero_scalar_field._express[self] = self.function_ring().zero() dom._one_scalar_field._express[self] = self.function_ring().one() @@ -386,10 +386,10 @@ def _init_coordinates(self, coord_list): for prop in coord_properties[1:]: prop1 = prop.strip() if prop1[0:6] == 'period': - if self._manifold.base_field_type() in ['real', 'complex']: + if domain.base_field_type() in ['real', 'complex']: period = SR(prop1[7:]) else: - period = self._manifold.base_field()(prop1[7:]) + period = domain.base_field()(prop1[7:]) self._periods[coord_index + self._sindex] = period else: # prop1 is the coordinate's LaTeX symbol @@ -411,7 +411,7 @@ def _repr_(self): Chart (M, (x, y)) """ - return 'Chart ({}, {})'.format(self._domain._name, self._xx) + return 'Chart ({}, {})'.format(self.domain()._name, self._xx) def _latex_(self): r""" @@ -430,7 +430,7 @@ def _latex_(self): \left(M,({\zeta_1}, {\zeta2})\right) """ - description = r'\left(' + latex(self._domain).strip() + ',(' + description = r'\left(' + latex(self.domain()).strip() + ',(' n = len(self._xx) for i in range(n-1): description += latex(self._xx[i]).strip() + ', ' @@ -712,10 +712,10 @@ def restrict(self, subset, restrictions=None): Chart (B, (z1, z2)) """ - if subset == self._domain: + if subset == self.domain(): return self if subset not in self._dom_restrict: - if not subset.is_subset(self._domain): + if not subset.is_subset(self.domain()): raise ValueError("the specified subset is not a subset " + "of the domain of definition of the chart") coordinates = "" @@ -784,7 +784,7 @@ def valid_coordinates(self, *coordinates, **kwds): False """ - if len(coordinates) != self._domain._dim: + if len(coordinates) != self.domain()._dim: return False if 'parameters' in kwds: parameters = kwds['parameters'] @@ -966,8 +966,8 @@ def transition_map(self, other, transformations, intersection_name=None, [Chart (R^2, (x, y)), Chart (U, (r, phi)), Chart (U, (x, y))] """ - dom1 = self._domain - dom2 = other._domain + dom1 = self.domain() + dom2 = other.domain() dom = dom1.intersection(dom2, name=intersection_name) if dom is dom1: chart1 = self @@ -2028,10 +2028,10 @@ def restrict(self, subset, restrictions=None): True """ - if subset == self._domain: + if subset == self.domain(): return self if subset not in self._dom_restrict: - if not subset.is_subset(self._domain): + if not subset.is_subset(self.domain()): raise ValueError("the specified subset is not a subset " + "of the domain of definition of the chart") coordinates = "" @@ -2723,10 +2723,10 @@ def _plot_xx_list(xx_list, rem_coords, ranges, steps, number_values): transf = None # to be the MultiCoordFunction object relating self # to the ambient chart if mapping is None: - if not self._domain.is_subset(chart._domain): + if not self.domain().is_subset(chart.domain()): raise ValueError("the domain of {} is not ".format(self) + "included in that of {}".format(chart)) - coord_changes = chart._domain._coord_changes + coord_changes = chart.domain()._coord_changes for chart_pair in coord_changes: if chart_pair == (self, chart): transf = coord_changes[chart_pair]._transf @@ -2741,10 +2741,10 @@ def _plot_xx_list(xx_list, rem_coords, ranges, steps, number_values): if not isinstance(mapping, ContinuousMap): raise TypeError("the argument 'mapping' must be a " "continuous manifold map") - if not self._domain.is_subset(mapping._domain): + if not self.domain().is_subset(mapping.domain()): raise ValueError("the domain of {} is not ".format(self) + "included in that of {}".format(mapping)) - if not chart._domain.is_subset(mapping._codomain): + if not chart.domain().is_subset(mapping._codomain): raise ValueError("the domain of {} is not ".format(chart) + "included in the codomain of {}".format( mapping)) @@ -3004,8 +3004,8 @@ def __init__(self, chart1, chart2, *transformations): self._inverse = None # If the two charts are on the same open subset, the coordinate change # is added to the subset (and supersets) dictionary: - if chart1._domain == chart2._domain: - domain = chart1._domain + if chart1.domain() == chart2.domain(): + domain = chart1.domain() for sdom in domain.open_supersets(): sdom._coord_changes[(chart1, chart2)] = self diff --git a/src/sage/manifolds/chart_func.py b/src/sage/manifolds/chart_func.py index d38eab686ae..b627a651802 100644 --- a/src/sage/manifolds/chart_func.py +++ b/src/sage/manifolds/chart_func.py @@ -1066,7 +1066,7 @@ def derivative(self, coord): # NB: for efficiency, we access directly to the "private" attributes # of other classes. A more conventional OOP writing would be # coordsi = coord - self._chart.domain().start_index() - coordsi = coord - self._chart._domain._sindex + coordsi = coord - self._chart.domain()._sindex if coordsi < 0 or coordsi >= self._nc: raise ValueError("coordinate index out of range") return self._der[coordsi] diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index e19eb818a68..250f8b592b9 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -395,8 +395,8 @@ def transition_map(self, other, transformations, intersection_name=None, [Chart (R^2, (x, y)), Chart (U, (r, phi)), Chart (U, (x, y))] """ - dom1 = self._domain - dom2 = other._domain + dom1 = self.domain() + dom2 = other.domain() dom = dom1.intersection(dom2, name=intersection_name) if dom is dom1: chart1 = self @@ -552,7 +552,7 @@ def restrict(self, subset, restrictions=None): Chart (B, (z1, z2)) """ - if subset == self._domain: + if subset == self.domain(): return self if subset not in self._dom_restrict: resu = Chart.restrict(self, subset, restrictions=restrictions) @@ -562,8 +562,8 @@ def restrict(self, subset, restrictions=None): sframe._subframes.add(resu._frame) sframe._restrictions[subset] = resu._frame # The subchart frame is not a "top frame" in the supersets - # (including self._domain): - for dom in self._domain.open_supersets(): + # (including self.domain()): + for dom in self.domain().open_supersets(): if resu._frame in dom._top_frames: # it was added by the Chart constructor invoked in # Chart.restrict above @@ -1035,7 +1035,7 @@ def restrict(self, subset, restrictions=None): True """ - if subset == self._domain: + if subset == self.domain(): return self if subset not in self._dom_restrict: resu = RealChart.restrict(self, subset, restrictions=restrictions) @@ -1045,8 +1045,8 @@ def restrict(self, subset, restrictions=None): sframe._subframes.add(resu._frame) sframe._restrictions[subset] = resu._frame # The subchart frame is not a "top frame" in the supersets - # (including self._domain): - for dom in self._domain.open_supersets(): + # (including self.domain()): + for dom in self.domain().open_supersets(): if resu._frame in dom._top_frames: # it was added by the Chart constructor invoked in # Chart.restrict above @@ -1129,8 +1129,8 @@ def __init__(self, chart1, chart2, *transformations): self._jacobian = self._transf.jacobian() # If the two charts are on the same open subset, the Jacobian matrix is # added to the dictionary of changes of frame: - if chart1._domain == chart2._domain: - domain = chart1._domain + if chart1.domain() == chart2.domain(): + domain = chart1.domain() frame1 = chart1._frame frame2 = chart2._frame vf_module = domain.vector_field_module() diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index 8caab15c093..f111244de1c 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -1071,9 +1071,9 @@ def paral_comp(tcomp,chart1,chart2,coord2_1,jacob, coord1 = chart1._xx ff = tensor._express[chart2] resu_fc.append( chart1.function(ff(*(phi(*coord1)))) ) - dom_resu = resu_fc[0].parent()._chart._domain + dom_resu = resu_fc[0].parent()._chart.domain() for fc in resu_fc[1:]: - dom_resu = dom_resu.union(fc.parent()._chart._domain) + dom_resu = dom_resu.union(fc.parent()._chart.domain()) resu = dom_resu.scalar_field(name=resu_name, latex_name=resu_latex_name) for fc in resu_fc: diff --git a/src/sage/manifolds/differentiable/scalarfield_algebra.py b/src/sage/manifolds/differentiable/scalarfield_algebra.py index c0a0a3b59fa..c9ed7f6160b 100644 --- a/src/sage/manifolds/differentiable/scalarfield_algebra.py +++ b/src/sage/manifolds/differentiable/scalarfield_algebra.py @@ -426,7 +426,7 @@ def _coerce_map_from_(self, other): elif isinstance(other, DiffScalarFieldAlgebra): return self._domain.is_subset(other._domain) elif isinstance(other, ChartFunctionRing): - return self._domain.is_subset(other._chart._domain) + return self._domain.is_subset(other._chart.domain()) else: return False diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 3dccc5ea6cb..07ae0f6a092 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -1727,7 +1727,7 @@ def orientation(self): # Try the default chart: def_chart = self._def_chart if def_chart is not None: - if def_chart._domain is self: + if def_chart.domain() is self: self._orientation = [self._def_chart] # Still no orientation? Choose arbitrary chart: if not self._orientation: @@ -1967,7 +1967,7 @@ def scalar_field(self, coord_expression=None, chart=None, name=None, if isinstance(coord_expression, dict): # check validity of entry for chart in coord_expression: - if not chart._domain.is_subset(self): + if not chart.domain().is_subset(self): raise ValueError("the {} is not defined ".format(chart) + "on some subset of the " + str(self)) alg = self.scalar_field_algebra() diff --git a/src/sage/manifolds/point.py b/src/sage/manifolds/point.py index e01fbbb99fa..3f0d6fb9e13 100644 --- a/src/sage/manifolds/point.py +++ b/src/sage/manifolds/point.py @@ -371,7 +371,7 @@ def coordinates(self, chart=None, old_chart=None): chart = dom._def_chart def_chart = chart else: - dom = chart._domain + dom = chart.domain() def_chart = dom._def_chart if self not in dom: raise ValueError("the point does not belong to the domain " + diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index 764acedfdec..d38b2fb6ede 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -2027,7 +2027,7 @@ def add_expr_by_continuation(self, chart, subdomain): if self.is_immutable(): raise ValueError("the expressions of an immutable element " "cannot be changed") - if not chart._domain.is_subset(self._domain): + if not chart.domain().is_subset(self._domain): raise ValueError("the chart is not defined on a subset of " + "the scalar field domain") schart = chart.restrict(subdomain) @@ -2074,7 +2074,7 @@ def set_restriction(self, rst): self._restrictions[rst._domain].set_name(name=self._name, latex_name=self._latex_name) for chart, expr in rst._express.items(): - intersection = chart._domain.intersection(rst._domain) + intersection = chart.domain().intersection(rst._domain) self._express[chart.restrict(intersection)] = expr self._is_zero = False # a priori @@ -2150,13 +2150,13 @@ def _display_expression(self, chart, result): coords = chart[:] if len(coords) == 1: coords = coords[0] - if chart._domain == self._domain: + if chart.domain() == self._domain: if self._name is not None: result._txt += " " result._latex += " & " else: - result._txt += "on " + chart._domain._name + ": " - result._latex += r"\mbox{on}\ " + latex(chart._domain) + \ + result._txt += "on " + chart.domain()._name + ": " + result._latex += r"\mbox{on}\ " + latex(chart.domain()) + \ r": & " result._txt += repr(coords) + " |--> " + repr(expression) + "\n" result._latex += latex(coords) + r"& \longmapsto & " + \ @@ -2198,13 +2198,13 @@ def _display_expression(self, chart, result): if max_dom is None: try: self.coord_function(sch) - max_dom = sch._domain + max_dom = sch.domain() except (TypeError, ValueError): pass - elif max_dom.is_subset(sch._domain): + elif max_dom.is_subset(sch.domain()): try: self.coord_function(sch) - max_dom = sch._domain + max_dom = sch.domain() except (TypeError, ValueError): pass if max_dom is not None: diff --git a/src/sage/manifolds/scalarfield_algebra.py b/src/sage/manifolds/scalarfield_algebra.py index 3ee4b383f9d..6f51f4b40f6 100644 --- a/src/sage/manifolds/scalarfield_algebra.py +++ b/src/sage/manifolds/scalarfield_algebra.py @@ -527,7 +527,7 @@ def _coerce_map_from_(self, other): elif isinstance(other, ScalarFieldAlgebra): return self._domain.is_subset(other._domain) elif isinstance(other, ChartFunctionRing): - return self._domain.is_subset(other._chart._domain) + return self._domain.is_subset(other._chart.domain()) else: return False From 7fa53817e5aa1d3e13e6cf32dec056541ce6af9f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 19 Jun 2021 23:17:15 -0700 Subject: [PATCH 092/336] build/pkgs/suitesparse/dependencies: Add mpfr, $(MP_LIBRARY) for SLIP_LU --- build/pkgs/suitesparse/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/suitesparse/dependencies b/build/pkgs/suitesparse/dependencies index 7580ea8e60f..c9663df28f5 100644 --- a/build/pkgs/suitesparse/dependencies +++ b/build/pkgs/suitesparse/dependencies @@ -1 +1 @@ -$(BLAS) gfortran +$(BLAS) gfortran mpfr $(MP_LIBRARY) From aa028b7cfb07ca219ba6d28d9f17ce53f1d7dd74 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 08:35:59 -0700 Subject: [PATCH 093/336] src/sage/game_theory/parser.py: Relax lrslib output doctests --- src/sage/game_theory/parser.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index 00cc8efa5ee..f81e928a00e 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -155,8 +155,8 @@ def format_lrs(self): temporary files, calls lrs and stores the output in `lrs_output` (here slicing to get rid of some system parameters that get returned):: - sage: lrs_output[5:16] # optional - lrslib - ['\n', + sage: lrs_output[:20] # optional - lrslib + [..., '***** 4 4 rational\n', '2 0 1 2 \n', '1 1/2 1/2 -2 \n', @@ -166,7 +166,8 @@ def format_lrs(self): '\n', '\n', '*Number of equilibria found: 2\n', - '*Player 1: vertices=3 bases=3 pivots=5\n'] + '*Player 1: vertices=3 bases=3 pivots=5\n', + ...] The above is pretty messy, here is the output when we put it through the parser:: @@ -195,8 +196,8 @@ def format_lrs(self): sage: g2_file.close() sage: process = Popen(['lrsnash', g1_name, g2_name], stdout=PIPE, stderr=PIPE) # optional - lrslib sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # optional - lrslib - sage: print(lrs_output[5:20]) # optional - lrslib - ['\n', + sage: print(lrs_output[:25]) # optional - lrslib + [..., '***** 5 5 rational\n', '2 1/7 0 6/7 23/7 \n', '2 0 1/6 5/6 10/3 \n', @@ -210,7 +211,8 @@ def format_lrs(self): '\n', '\n', '*Number of equilibria found: 4\n', - '*Player 1: vertices=6 bases=7 pivots=10\n'] + '*Player 1: vertices=6 bases=7 pivots=10\n', + ...] sage: nasheq = Parser(lrs_output).format_lrs() # optional - lrslib sage: sorted(nasheq) # optional - lrslib From f351eb8df3aa9a2565c896ca8644a90c922445d9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 10:34:50 -0700 Subject: [PATCH 094/336] src/sage/sets/set.py: Split out mix-in classes Set_base, Set_boolean_operators, Set_add_sub_operators from Set_object; pycodestyle fixes --- src/sage/sets/set.py | 409 +++++++++++++++++++++++-------------------- 1 file changed, 218 insertions(+), 191 deletions(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 2777332e405..f58ce6d37b4 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -203,8 +203,206 @@ def Set(X=None): return Set_object_enumerated(X) +class Set_base(): + r""" + Abstract base class for sets, not necessarily parents. + """ + + def union(self, X): + """ + Return the union of ``self`` and ``X``. + + EXAMPLES:: + + sage: Set(QQ).union(Set(ZZ)) + Set-theoretic union of Set of elements of Rational Field and Set of elements of Integer Ring + sage: Set(QQ) + Set(ZZ) + Set-theoretic union of Set of elements of Rational Field and Set of elements of Integer Ring + sage: X = Set(QQ).union(Set(GF(3))); X + Set-theoretic union of Set of elements of Rational Field and {0, 1, 2} + sage: 2/3 in X + True + sage: GF(3)(2) in X + True + sage: GF(5)(2) in X + False + sage: sorted(Set(GF(7)) + Set(GF(3)), key=int) + [0, 0, 1, 1, 2, 2, 3, 4, 5, 6] + """ + if isinstance(X, Set_generic): + if self is X: + return self + return Set_object_union(self, X) + raise TypeError("X (=%s) must be a Set" % X) + + def intersection(self, X): + r""" + Return the intersection of ``self`` and ``X``. + + EXAMPLES:: + + sage: X = Set(ZZ).intersection(Primes()) + sage: 4 in X + False + sage: 3 in X + True + + sage: 2/1 in X + True + + sage: X = Set(GF(9,'b')).intersection(Set(GF(27,'c'))) + sage: X + {} + + sage: X = Set(GF(9,'b')).intersection(Set(GF(27,'b'))) + sage: X + {} + """ + if isinstance(X, Set_generic): + if self is X: + return self + return Set_object_intersection(self, X) + raise TypeError("X (=%s) must be a Set" % X) + + def difference(self, X): + r""" + Return the set difference ``self - X``. + + EXAMPLES:: + + sage: X = Set(ZZ).difference(Primes()) + sage: 4 in X + True + sage: 3 in X + False + + sage: 4/1 in X + True + + sage: X = Set(GF(9,'b')).difference(Set(GF(27,'c'))) + sage: X + {0, 1, 2, b, b + 1, b + 2, 2*b, 2*b + 1, 2*b + 2} + + sage: X = Set(GF(9,'b')).difference(Set(GF(27,'b'))) + sage: X + {0, 1, 2, b, b + 1, b + 2, 2*b, 2*b + 1, 2*b + 2} + """ + if isinstance(X, Set_generic): + if self is X: + return Set([]) + return Set_object_difference(self, X) + raise TypeError("X (=%s) must be a Set" % X) + + def symmetric_difference(self, X): + r""" + Returns the symmetric difference of ``self`` and ``X``. + + EXAMPLES:: + + sage: X = Set([1,2,3]).symmetric_difference(Set([3,4])) + sage: X + {1, 2, 4} + """ + if isinstance(X, Set_generic): + if self is X: + return Set([]) + return Set_object_symmetric_difference(self, X) + raise TypeError("X (=%s) must be a Set" % X) + + +class Set_boolean_operators: + r""" + Mix-in class providing the Boolean operators `__or__`, `__and__`, `__xor__`. + + The operators delegate to the methods ``union``, ``intersection``, and + ``symmetric_difference``, which need to be implemented by the class. + """ + + def __or__(self, X): + """ + Return the union of ``self`` and ``X``. + + EXAMPLES:: + + sage: Set([2,3]) | Set([3,4]) + {2, 3, 4} + sage: Set(ZZ) | Set(QQ) + Set-theoretic union of Set of elements of Integer Ring and Set of elements of Rational Field + """ + return self.union(X) + + def __and__(self, X): + """ + Returns the intersection of ``self`` and ``X``. + + EXAMPLES:: + + sage: Set([2,3]) & Set([3,4]) + {3} + sage: Set(ZZ) & Set(QQ) + Set-theoretic intersection of Set of elements of Integer Ring and Set of elements of Rational Field + """ + return self.intersection(X) + + def __xor__(self, X): + """ + Returns the symmetric difference of ``self`` and ``X``. + + EXAMPLES:: + + sage: X = Set([1,2,3,4]) + sage: Y = Set([1,2]) + sage: X.symmetric_difference(Y) + {3, 4} + sage: X.__xor__(Y) + {3, 4} + """ + return self.symmetric_difference(X) + + +class Set_add_sub_operators: + r""" + Mix-in class providing the operators `__add__` and `__sub__`. + + The operators delegate to the methods ``union`` and ``intersection``, + which need to be implemented by the class. + """ + + def __add__(self, X): + """ + Return the union of ``self`` and ``X``. + + EXAMPLES:: + + sage: Set(RealField()) + Set(QQ^5) + Set-theoretic union of + Set of elements of Real Field with 53 bits of precision and + Set of elements of Vector space of dimension 5 over Rational Field + sage: Set(GF(3)) + Set(GF(2)) + {0, 1, 2, 0, 1} + sage: Set(GF(2)) + Set(GF(4,'a')) + {0, 1, a, a + 1} + sage: sorted(Set(GF(8,'b')) + Set(GF(4,'a')), key=str) + [0, 0, 1, 1, a, a + 1, b, b + 1, b^2, b^2 + 1, b^2 + b, b^2 + b + 1] + """ + return self.union(X) + + def __sub__(self, X): + """ + Return the difference of ``self`` and ``X``. + + EXAMPLES:: + + sage: X = Set(ZZ).difference(Primes()) + sage: Y = Set(ZZ) - Primes() + sage: X == Y + True + """ + return self.difference(X) + + @richcmp_method -class Set_object(Set_generic): +class Set_object(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_operators): r""" A set attached to an almost arbitrary object. @@ -231,6 +429,7 @@ class Set_object(Set_generic): sage: 1 == Set([0]), Set([0]) == 1 (False, False) """ + def __init__(self, X, category=None): """ Create a Set_object @@ -404,183 +603,6 @@ def __richcmp__(self, right, op): return NotImplemented return richcmp(self.__object, right.__object, op) - def union(self, X): - """ - Return the union of ``self`` and ``X``. - - EXAMPLES:: - - sage: Set(QQ).union(Set(ZZ)) - Set-theoretic union of Set of elements of Rational Field and Set of elements of Integer Ring - sage: Set(QQ) + Set(ZZ) - Set-theoretic union of Set of elements of Rational Field and Set of elements of Integer Ring - sage: X = Set(QQ).union(Set(GF(3))); X - Set-theoretic union of Set of elements of Rational Field and {0, 1, 2} - sage: 2/3 in X - True - sage: GF(3)(2) in X - True - sage: GF(5)(2) in X - False - sage: sorted(Set(GF(7)) + Set(GF(3)), key=int) - [0, 0, 1, 1, 2, 2, 3, 4, 5, 6] - """ - if isinstance(X, Set_generic): - if self is X: - return self - return Set_object_union(self, X) - raise TypeError("X (=%s) must be a Set"%X) - - def __add__(self, X): - """ - Return the union of ``self`` and ``X``. - - EXAMPLES:: - - sage: Set(RealField()) + Set(QQ^5) - Set-theoretic union of Set of elements of Real Field with 53 bits of precision and Set of elements of Vector space of dimension 5 over Rational Field - sage: Set(GF(3)) + Set(GF(2)) - {0, 1, 2, 0, 1} - sage: Set(GF(2)) + Set(GF(4,'a')) - {0, 1, a, a + 1} - sage: sorted(Set(GF(8,'b')) + Set(GF(4,'a')), key=str) - [0, 0, 1, 1, a, a + 1, b, b + 1, b^2, b^2 + 1, b^2 + b, b^2 + b + 1] - """ - return self.union(X) - - def __or__(self, X): - """ - Return the union of ``self`` and ``X``. - - EXAMPLES:: - - sage: Set([2,3]) | Set([3,4]) - {2, 3, 4} - sage: Set(ZZ) | Set(QQ) - Set-theoretic union of Set of elements of Integer Ring and Set of elements of Rational Field - """ - - return self.union(X) - - def intersection(self, X): - r""" - Return the intersection of ``self`` and ``X``. - - EXAMPLES:: - - sage: X = Set(ZZ).intersection(Primes()) - sage: 4 in X - False - sage: 3 in X - True - - sage: 2/1 in X - True - - sage: X = Set(GF(9,'b')).intersection(Set(GF(27,'c'))) - sage: X - {} - - sage: X = Set(GF(9,'b')).intersection(Set(GF(27,'b'))) - sage: X - {} - """ - if isinstance(X, Set_generic): - if self is X: - return self - return Set_object_intersection(self, X) - raise TypeError("X (=%s) must be a Set"%X) - - - def difference(self, X): - r""" - Return the set difference ``self - X``. - - EXAMPLES:: - - sage: X = Set(ZZ).difference(Primes()) - sage: 4 in X - True - sage: 3 in X - False - - sage: 4/1 in X - True - - sage: X = Set(GF(9,'b')).difference(Set(GF(27,'c'))) - sage: X - {0, 1, 2, b, b + 1, b + 2, 2*b, 2*b + 1, 2*b + 2} - - sage: X = Set(GF(9,'b')).difference(Set(GF(27,'b'))) - sage: X - {0, 1, 2, b, b + 1, b + 2, 2*b, 2*b + 1, 2*b + 2} - """ - if isinstance(X, Set_generic): - if self is X: - return Set([]) - return Set_object_difference(self, X) - raise TypeError("X (=%s) must be a Set"%X) - - def symmetric_difference(self, X): - r""" - Returns the symmetric difference of ``self`` and ``X``. - - EXAMPLES:: - - sage: X = Set([1,2,3]).symmetric_difference(Set([3,4])) - sage: X - {1, 2, 4} - """ - - if isinstance(X, Set_generic): - if self is X: - return Set([]) - return Set_object_symmetric_difference(self, X) - raise TypeError("X (=%s) must be a Set"%X) - - - def __sub__(self, X): - """ - Return the difference of ``self`` and ``X``. - - EXAMPLES:: - - sage: X = Set(ZZ).difference(Primes()) - sage: Y = Set(ZZ) - Primes() - sage: X == Y - True - """ - return self.difference(X) - - def __and__(self, X): - """ - Returns the intersection of ``self`` and ``X``. - - EXAMPLES:: - - sage: Set([2,3]) & Set([3,4]) - {3} - sage: Set(ZZ) & Set(QQ) - Set-theoretic intersection of Set of elements of Integer Ring and Set of elements of Rational Field - """ - - return self.intersection(X) - - def __xor__(self, X): - """ - Returns the symmetric difference of ``self`` and ``X``. - - EXAMPLES:: - - sage: X = Set([1,2,3,4]) - sage: Y = Set([1,2]) - sage: X.symmetric_difference(Y) - {3, 4} - sage: X.__xor__(Y) - {3, 4} - """ - return self.symmetric_difference(X) - def cardinality(self): """ Return the cardinality of this set, which is either an integer or @@ -611,7 +633,7 @@ def cardinality(self): except TypeError: pass - raise NotImplementedError("computation of cardinality of %s not yet implemented"%self.__object) + raise NotImplementedError("computation of cardinality of %s not yet implemented" % self.__object) def is_empty(self): """ @@ -687,22 +709,21 @@ def object(self): """ return self.__object - def subsets(self,size=None): + def subsets(self, size=None): """ Return the :class:`Subsets` object representing the subsets of a set. If size is specified, return the subsets of that size. EXAMPLES:: - sage: X = Set([1,2,3]) + sage: X = Set([1, 2, 3]) sage: list(X.subsets()) [{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}] sage: list(X.subsets(2)) [{1, 2}, {1, 3}, {2, 3}] - """ from sage.combinat.subset import Subsets - return Subsets(self,size) + return Subsets(self, size) def subsets_lattice(self): """ @@ -725,22 +746,23 @@ def subsets_lattice(self): from sage.graphs.graph import DiGraph from sage.rings.integer import Integer n = self.cardinality() - # list, contains at position 0 <= i < 2^n + # list, contains at position 0 <= i < 2^n # the i-th subset of self - subset_of_index = [Set([self[i] for i in range(n) if v&(1< Date: Sun, 20 Jun 2021 11:16:43 -0700 Subject: [PATCH 095/336] Polyhedron_base.*contains: Return False for non-iterables - do not raise an exception --- src/sage/geometry/polyhedron/base.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 918609a85cd..5ef8db32f1b 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8601,11 +8601,22 @@ def contains(self, point): True sage: full.contains([0]) False + + TESTS: + + Passing non-iterable objects does not cause an exception, see :trac:`32013`:: + + sage: None in Polyhedron(vertices=[(0,0)], rays=[(1,0)], base_ring=QQ) + False """ try: p = vector(point) except TypeError: # point not iterable or no common ring for elements - if len(point) > 0: + try: + l = len(point) + except TypeError: + return False + if l > 0: return False else: p = vector(self.base_ring(), []) @@ -8704,7 +8715,11 @@ def interior_contains(self, point): try: p = vector(point) except TypeError: # point not iterable or no common ring for elements - if len(point) > 0: + try: + l = len(point) + except TypeError: + return False + if l > 0: return False else: p = vector(self.base_ring(), []) @@ -8821,7 +8836,11 @@ def relative_interior_contains(self, point): try: p = vector(point) except TypeError: # point not iterable or no common ring for elements - if len(point) > 0: + try: + l = len(point) + except TypeError: + return False + if l > 0: return False else: p = vector(self.base_ring(), []) From 6237fbc13bd19d444e1d10f0ab3bbdc61acd21c8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 11:35:12 -0700 Subject: [PATCH 096/336] ConvexSet_base.is_finite, cardinality: New --- src/sage/geometry/convex_set.py | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 34da69b5cc9..0618fdfd841 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -38,6 +38,60 @@ def is_empty(self): """ return self.dim() < 0 + def is_finite(self): + r""" + Test whether ``self`` is a finite set. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: p = LatticePolytope([], lattice=ToricLattice(3).dual()); p + -1-d lattice polytope in 3-d lattice M + sage: p.is_finite() + True + sage: q = Polyhedron(ambient_dim=2); q + The empty polyhedron in ZZ^2 + sage: q.is_finite() + True + sage: r = Polyhedron(rays=[(1, 0)]); r + A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 ray + sage: r.is_finite() + False + """ + return self.dim() < 1 + + def cardinality(self): + """ + Return the cardinality of this set. + + OUTPUT: + + Either an integer or ``Infinity``. + + EXAMPLES:: + + sage: p = LatticePolytope([], lattice=ToricLattice(3).dual()); p + -1-d lattice polytope in 3-d lattice M + sage: p.cardinality() + 0 + sage: q = Polyhedron(ambient_dim=2); q + The empty polyhedron in ZZ^2 + sage: q.cardinality() + 0 + sage: r = Polyhedron(rays=[(1, 0)]); r + A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 ray + sage: r.cardinality() + +Infinity + """ + if self.dim() < 0: + return ZZ(0) + if self.dim() == 0: + return ZZ(1) + return infinity + def is_universe(self): r""" Test whether ``self`` is the whole ambient space. From 18270cef5f6521cce0437add8a18f27b707361a1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 11:40:53 -0700 Subject: [PATCH 097/336] Set_object._an_element_: Handle non-iterable objects by delegating to __object.an_element --- src/sage/sets/set.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index f58ce6d37b4..230a955ac68 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -532,7 +532,28 @@ def __iter__(self): """ return iter(self.__object) - an_element = EnumeratedSets.ParentMethods.__dict__['_an_element_from_iterator'] + _an_element_from_iterator = EnumeratedSets.ParentMethods.__dict__['_an_element_from_iterator'] + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: R = Set(RR) + sage: R.an_element() # indirect doctest + 1.00000000000000 + + sage: F = Set([1, 2, 3]) + sage: F.an_element() + 1 + """ + if self.__object is not self: + try: + return self.__object.an_element() + except (AttributeError, NotImplementedError): + pass + return self._an_element_from_iterator() def __contains__(self, x): """ From b8f5978dbac9b4558c8fa9d8ee0947cf035ec452 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 11:58:34 -0700 Subject: [PATCH 098/336] Set: Create a wrapper instance also for Elements that subclass Set_base --- src/sage/sets/set.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 230a955ac68..5388d8aaf62 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -192,7 +192,7 @@ def Set(X=None): else: return Set_object(X) - if isinstance(X, Element): + if isinstance(X, Element) and not isinstance(X, Set_base): raise TypeError("Element has no defined underlying set") try: @@ -309,6 +309,35 @@ def symmetric_difference(self, X): return Set_object_symmetric_difference(self, X) raise TypeError("X (=%s) must be a Set" % X) + def _test_as_set_object(self, tester=None, **options): + r""" + Run the test suite of ``Set(self)`` unless it is identical to ``self``. + + EXAMPLES: + + Nothing is tested for instances of :class`Set_generic` (constructed + with the :func:`Set` constructor):: + + sage: Set(ZZ)._test_as_set_object(verbose=True) + + Instances of other subclasses of :class:`Set_base` run this method:: + + sage: Polyhedron()._test_as_set_object(verbose=True) + Running the test suite of Set(self) + running ._test_an_element() . . . pass + ... + running ._test_some_elements() . . . pass + """ + if tester is None: + tester = self._tester(**options) + set_self = Set(self) + if set_self is not self: + from sage.misc.sage_unittest import TestSuite + tester.info("\n Running the test suite of Set(self)") + TestSuite(set_self).run(verbose=tester._verbose, + prefix=tester._prefix + " ") + tester.info(tester._prefix + " ", newline=False) + class Set_boolean_operators: r""" From b67092c535cc94c634674c2dcabb47d347dda616 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 12:27:18 -0700 Subject: [PATCH 099/336] Set_base.union, intersection, difference, symmetric_difference: Convert to Set if necessary --- src/sage/geometry/convex_set.py | 5 ++++- src/sage/sets/set.py | 38 +++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 0618fdfd841..3fd953a01e8 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -13,10 +13,13 @@ # **************************************************************************** from sage.structure.sage_object import SageObject +from sage.sets.set import Set_base from sage.categories.sets_cat import EmptySetError from sage.misc.abstract_method import abstract_method +from sage.rings.infinity import infinity +from sage.rings.integer_ring import ZZ -class ConvexSet_base(SageObject): +class ConvexSet_base(SageObject, Set_base): """ Abstract base class for convex sets. """ diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 5388d8aaf62..5a6ebb95c98 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -43,6 +43,7 @@ from sage.structure.element import Element from sage.structure.parent import Parent, Set_generic from sage.structure.richcmp import richcmp_method, richcmp, rich_to_bool +from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.categories.sets_cat import Sets from sage.categories.enumerated_sets import EnumeratedSets @@ -229,7 +230,7 @@ def union(self, X): sage: sorted(Set(GF(7)) + Set(GF(3)), key=int) [0, 0, 1, 1, 2, 2, 3, 4, 5, 6] """ - if isinstance(X, Set_generic): + if isinstance(X, (Set_generic, Set_base)): if self is X: return self return Set_object_union(self, X) @@ -258,7 +259,7 @@ def intersection(self, X): sage: X {} """ - if isinstance(X, Set_generic): + if isinstance(X, (Set_generic, Set_base)): if self is X: return self return Set_object_intersection(self, X) @@ -287,7 +288,7 @@ def difference(self, X): sage: X {0, 1, 2, b, b + 1, b + 2, 2*b, 2*b + 1, 2*b + 2} """ - if isinstance(X, Set_generic): + if isinstance(X, (Set_generic, Set_base)): if self is X: return Set([]) return Set_object_difference(self, X) @@ -303,7 +304,7 @@ def symmetric_difference(self, X): sage: X {1, 2, 4} """ - if isinstance(X, Set_generic): + if isinstance(X, (Set_generic, Set_base)): if self is X: return Set([]) return Set_object_symmetric_difference(self, X) @@ -1179,7 +1180,7 @@ def symmetric_difference(self, other): return Set_object_enumerated(self.set().symmetric_difference(other.set())) -class Set_object_binary(Set_object): +class Set_object_binary(Set_object, metaclass=ClasscallMetaclass): r""" An abstract common base class for sets defined by a binary operation (ex. :class:`Set_object_union`, :class:`Set_object_intersection`, @@ -1200,9 +1201,32 @@ class Set_object_binary(Set_object): sage: Y = Set(ZZ) sage: from sage.sets.set import Set_object_binary sage: S = Set_object_binary(X, Y, "union", "\\cup"); S - Set-theoretic union of Set of elements of Vector space of dimension 2 - over Rational Field and Set of elements of Integer Ring + Set-theoretic union of + Set of elements of Vector space of dimension 2 over Rational Field and + Set of elements of Integer Ring """ + + @staticmethod + def __classcall__(cls, X, Y, *args, **kwds): + r""" + Convert the operands to instances of :class:`Set_object` if necessary. + + TESTS:: + + sage: from sage.sets.set import Set_object_binary + sage: X = QQ^2 + sage: Y = ZZ + sage: Set_object_binary(X, Y, "union", "\\cup") + Set-theoretic union of + Set of elements of Vector space of dimension 2 over Rational Field and + Set of elements of Integer Ring + """ + if not isinstance(X, Set_object): + X = Set(X) + if not isinstance(Y, Set_object): + Y = Set(Y) + return type.__call__(cls, X, Y, *args, **kwds) + def __init__(self, X, Y, op, latex_op): r""" Initialization. From e1443896851ac805a89e8747e8c7412bbc7d9d10 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 12:37:11 -0700 Subject: [PATCH 100/336] ConvexSet_base.intersection: Remove abstract method; now inherits Set_base.intersection --- src/sage/geometry/convex_set.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 3fd953a01e8..efd6bfd3cd9 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -676,29 +676,6 @@ def _test_contains(self, tester=None, **options): tester.assertTrue(self.contains(point)) tester.assertTrue(point in self) - @abstract_method(optional=True) - def intersection(self, other): - r""" - Return the intersection of ``self`` and ``other``. - - INPUT: - - - ``other`` -- another convex set - - OUTPUT: - - The intersection. - - TESTS:: - - sage: from sage.geometry.convex_set import ConvexSet_base - sage: C = ConvexSet_base() - sage: C.intersection(C) - Traceback (most recent call last): - ... - TypeError: 'NotImplementedType' object is not callable - """ - class ConvexSet_closed(ConvexSet_base): r""" From b1d61ba69958bbdba7448193609a1def428d669a Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 21 Jun 2021 11:16:13 +0100 Subject: [PATCH 101/336] Added integrate_vector_N method A method was added to allow the user to use a Gauss-Legendre integrator while specifying the number of nodes desired. This is in constrast to the existing method integrate_vector which finds an appropriate number of nodes based on a desired error bound and a an error heuristic. As per comments by nbruin on ticket #30698, it is desirable to have this as a separate method rather than as an option within the existing method as the method is then set at call. It is my opinion that the name integrate_vector_N is not the best, and so improving it as this point in time would be useful. --- src/sage/numerical/gauss_legendre.pyx | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index fba5a931d2e..000d4339747 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -174,6 +174,52 @@ def estimate_error(results,prec,epsilon): e.append(D4.exp()) return max(e) +def integrate_vector_N(f, prec, N=3): + r""" + Integrate a one-argument vector-valued function numerically using Gauss-Legendre, + setting the number of nodes. + + This function uses the Gauss-Legendre quadrature scheme to approximate the + integral `\int_0^1 f(t) \, dt`. It is different from ``integrate_vector`` + by fixing the number of nodes to use, rather than aiming for a certain + error. + + INPUT: + + - ``f`` -- callable. Vector-valued integrand. + + - ``prec`` -- integer. Binary precision to be used. + + - ``N`` -- integer (default: 3). Number of nodes to use. + + OUTPUT: + + Vector approximating value of the integral. + + EXAMPLES:: + + sage: from sage.numerical.gauss_legendre import integrate_vector_N + sage: prec = 100 + sage: K = RealField(prec) + sage: V = VectorSpace(K,1) + sage: f = lambda t: V([t]) + sage: integrate_vector_N(f, prec, 4) + (0.50000000000000000000000000000) + + .. NOTE:: + + The nodes and weights are calculated in the real field with ``prec`` + bits of precision. If the the vector space in which ``f`` takes values + is over a field which is incomatible with this field (e.g. a finite + field) then a TypeError occurs. + + """ + nodelist = nodes(N, prec) + I = nodelist[0][1]*f(nodelist[0][0]) + for i in range(1,len(nodelist)): + I += nodelist[i][1]*f(nodelist[i][0]) + return I + def integrate_vector(f,prec,epsilon=None): r""" Integrate a one-argument vector-valued function numerically using Gauss-Legendre. From bc26d4f1dfef8d64ffb60db717a2db7a155771d6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Jun 2021 12:07:57 -0700 Subject: [PATCH 102/336] {LatticePolytopeClass, ConvexRationalPolyhedralCone._some_elements_: New --- src/sage/geometry/cone.py | 21 +++++++++++++++++++ src/sage/geometry/lattice_polytope.py | 30 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 78d0cb0444f..4e7655df09f 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -2006,6 +2006,27 @@ def _repr_(self): result += " face of %s" % self.ambient() return result + def _some_elements_(self): + r""" + Generate some points of ``self``. + + EXAMPLE:: + + sage: K = cones.nonnegative_orthant(3) + sage: K.some_elements() # indirect doctest + [(0, 0, 0), (1/2, 0, 0), (1/4, 1/2, 0), (1/8, 1/4, 1/2)] + """ + V = self.ambient_vector_space() + r_iter = iter(self._rays) + p = V(0) + yield p + for i in range(5): + try: + p = (p + next(r_iter)) / 2 + except StopIteration: + return + yield p + def _sort_faces(self, faces): r""" Return sorted (if necessary) ``faces`` as a tuple. diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 9d42d71f275..fa46804aacc 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -3794,6 +3794,36 @@ def points(self, *args, **kwds): else: return self._points + def _some_elements_(self): + r""" + Generate some points of ``self`` as a convex polytope. + + In contrast to :meth:`points`, these are not necessarily lattice points. + + EXAMPLE:: + + sage: o = lattice_polytope.cross_polytope(3) + sage: o.some_elements() # indirect doctest + [(1, 0, 0), + (1/2, 1/2, 0), + (1/4, 1/4, 1/2), + (-3/8, 1/8, 1/4), + (-3/16, -7/16, 1/8), + (-3/32, -7/32, -7/16)] + """ + if not self._vertices: + return + V = self.ambient_vector_space() + v_iter = iter(self._vertices) + p = V(next(v_iter)) + yield p + for i in range(5): + try: + p = (p + next(v_iter)) / 2 + except StopIteration: + return + yield p + def polar(self): r""" Return the polar polytope, if this polytope is reflexive. From cb93c99aeff620cae20a0734179e78bb49f5dfa1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Jun 2021 13:21:53 -0700 Subject: [PATCH 103/336] RealSet: Inherit from Set_base, Set_boolean_operators, Set_add_sub_operators --- src/sage/sets/real_set.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index f9dd8610491..e0126887a4d 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -89,6 +89,8 @@ class RealSet. from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.topological_spaces import TopologicalSpaces +from sage.categories.sets_cat import Sets +from sage.sets.set import Set_base, Set_boolean_operators, Set_add_sub_operators from sage.rings.all import ZZ from sage.rings.real_lazy import LazyFieldElement, RLF from sage.rings.infinity import infinity, minus_infinity @@ -749,7 +751,8 @@ def __rmul__(self, other): return self * other @richcmp_method -class RealSet(UniqueRepresentation, Parent): +class RealSet(UniqueRepresentation, Parent, Set_base, + Set_boolean_operators, Set_add_sub_operators): @staticmethod def __classcall__(cls, *args): @@ -1513,9 +1516,6 @@ def union(self, *other): intervals = self._intervals + other._intervals return RealSet(*intervals) - __or__ = union - __add__ = union - def intersection(self, *other): """ Return the intersection of the two sets @@ -1560,8 +1560,6 @@ def intersection(self, *other): intervals.append(i1.intersection(i2)) return RealSet(*intervals) - __and__ = intersection - def inf(self): """ Return the infimum @@ -1686,8 +1684,6 @@ def difference(self, *other): other = RealSet(*other) return self.intersection(other.complement()) - __sub__ = difference - def contains(self, x): """ Return whether `x` is contained in the set From 3a6f9bd8d6b9da7b5857b3879d76dedcb4f9d1a9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Jun 2021 13:22:32 -0700 Subject: [PATCH 104/336] RealSet.symmetric_difference: New --- src/sage/sets/real_set.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index e0126887a4d..3593c617d53 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1684,6 +1684,31 @@ def difference(self, *other): other = RealSet(*other) return self.intersection(other.complement()) + def symmetric_difference(self, *other): + r""" + Returns the symmetric difference of ``self`` and ``other``. + + INPUT: + + - ``other`` -- a :class:`RealSet` or data that defines one. + + OUTPUT: + + The set-theoretic symmetric difference of ``self`` and ``other`` + as a new :class:`RealSet`. + + EXAMPLES:: + + sage: s1 = RealSet(0,2); s1 + (0, 2) + sage: s2 = RealSet.unbounded_above_open(1); s2 + (1, +oo) + sage: s1.symmetric_difference(s2) + (0, 1] ∪ [2, +oo) + """ + other = RealSet(*other) + return self.difference(other).union(other.difference(self)) + def contains(self, x): """ Return whether `x` is contained in the set From 08c52c2535aeb4c2eadc43e22979df076a6d0760 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Jun 2021 13:53:36 -0700 Subject: [PATCH 105/336] Set_base._test_as_set_object: Skip _test_pickling --- src/sage/sets/set.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 5a6ebb95c98..809555d1b36 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -335,7 +335,8 @@ def _test_as_set_object(self, tester=None, **options): if set_self is not self: from sage.misc.sage_unittest import TestSuite tester.info("\n Running the test suite of Set(self)") - TestSuite(set_self).run(verbose=tester._verbose, + TestSuite(set_self).run(skip="_test_pickling", # see Trac #32025 + verbose=tester._verbose, prefix=tester._prefix + " ") tester.info(tester._prefix + " ", newline=False) From 3dd50e485860e39ce19973eba58fc9f0ed43ba0b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Jun 2021 13:38:43 -0700 Subject: [PATCH 106/336] sage.sets.set.Set_object: Add _sympy_ methods to subclasses --- src/sage/sets/set.py | 116 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 809555d1b36..4c11f89781d 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -38,6 +38,7 @@ from sage.misc.latex import latex from sage.misc.prandom import choice +from sage.misc.cachefunc import cached_method from sage.structure.category_object import CategoryObject from sage.structure.element import Element @@ -814,6 +815,22 @@ def subsets_lattice(self): L = FiniteLatticePoset(hasse_diagram=D) return L + @cached_method + def _sympy_(self): + """ + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + + EXAMPLES:: + + sage: X = Set(ZZ); X + Set of elements of Integer Ring + sage: X._sympy_() + Integers + """ + from sage.interfaces.sympy import sympy_init + sympy_init() + return self.__object._sympy_() + class Set_object_enumerated(Set_object): """ @@ -1180,6 +1197,23 @@ def symmetric_difference(self, other): return Set_object.symmetric_difference(self, other) return Set_object_enumerated(self.set().symmetric_difference(other.set())) + @cached_method + def _sympy_(self): + """ + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + + EXAMPLES:: + + sage: X = Set({1, 2, 3}); X + {1, 2, 3} + sage: X._sympy_() + Set(1, 2, 3) + """ + from sympy import Set + from sage.interfaces.sympy import sympy_init + sympy_init() + return Set(*[x._sympy_() for x in self]) + class Set_object_binary(Set_object, metaclass=ClasscallMetaclass): r""" @@ -1420,6 +1454,23 @@ def cardinality(self): """ return self._X.cardinality() + self._Y.cardinality() + @cached_method + def _sympy_(self): + """ + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + + EXAMPLES:: + + sage: X = Set(ZZ).union(Set([1/2])); X + Set-theoretic union of Set of elements of Integer Ring and {1/2} + sage: X._sympy_() + Union(Integers, Set(1/2)) + """ + from sympy import Union + from sage.interfaces.sympy import sympy_init + sympy_init() + return Union(self._X._sympy_(), self._Y._sympy_()) + class Set_object_intersection(Set_object_binary): """ @@ -1563,6 +1614,25 @@ def __contains__(self, x): """ return x in self._X and x in self._Y + @cached_method + def _sympy_(self): + """ + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + + EXAMPLES:: + + sage: X = Set(ZZ).intersection(RealSet([3/2, 11/2])); X + Set-theoretic intersection of + Set of elements of Integer Ring and + Set of elements of [3/2, 11/2] + sage: X._sympy_() + Range(2, 6, 1) + """ + from sympy import Intersection + from sage.interfaces.sympy import sympy_init + sympy_init() + return Intersection(self._X._sympy_(), self._Y._sympy_()) + class Set_object_difference(Set_object_binary): """ @@ -1701,6 +1771,32 @@ def __contains__(self, x): """ return x in self._X and x not in self._Y + @cached_method + def _sympy_(self): + """ + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + + EXAMPLES:: + + sage: X = Set(QQ).difference(Set(ZZ)); X + Set-theoretic difference of + Set of elements of Rational Field and + Set of elements of Integer Ring + sage: X._sympy_() + Complement(Rationals, Integers) + + sage: X = Set(ZZ).difference(Set(QQ)); X + Set-theoretic difference of + Set of elements of Integer Ring and + Set of elements of Rational Field + sage: X._sympy_() + EmptySet + """ + from sympy import Complement + from sage.interfaces.sympy import sympy_init + sympy_init() + return Complement(self._X._sympy_(), self._Y._sympy_()) + class Set_object_symmetric_difference(Set_object_binary): """ @@ -1840,3 +1936,23 @@ def __contains__(self, x): """ return ((x in self._X and x not in self._Y) or (x in self._Y and x not in self._X)) + + @cached_method + def _sympy_(self): + """ + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + + EXAMPLES:: + + sage: X = Set(ZZ).symmetric_difference(Set(srange(0, 3, 1/3))); X + Set-theoretic symmetric difference of + Set of elements of Integer Ring and + {0, 1, 2, 1/3, 2/3, 4/3, 5/3, 7/3, 8/3} + sage: X._sympy_() + Union(Complement(Integers, Set(0, 1, 2, 1/3, 2/3, 4/3, 5/3, 7/3, 8/3)), + Complement(Set(0, 1, 2, 1/3, 2/3, 4/3, 5/3, 7/3, 8/3), Integers)) + """ + from sympy import SymmetricDifference + from sage.interfaces.sympy import sympy_init + sympy_init() + return SymmetricDifference(self._X._sympy_(), self._Y._sympy_()) From 28d66228e635786a9f3c0ef75ac1aa894bb7f39f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 14:50:57 -0700 Subject: [PATCH 107/336] build/bin/sage-spkg: Remove dead code for old-style spkg --- build/bin/sage-spkg | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index c92f315f36f..2f44f09e904 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -295,12 +295,6 @@ export SAGE_SPKG_INST="$SAGE_INST_LOCAL/var/lib/sage/installed" export SAGE_SPKG_SCRIPTS="$SAGE_INST_LOCAL/var/lib/sage/scripts" export SAGE_SPKG_WHEELS="$SAGE_INST_LOCAL/var/lib/sage/wheels" -# USE_LOCAL_SCRIPTS is a flag that if non-empty will cause -# this script to try to install the package using local metadata -# i.e. use upstream tarballs (vs spkgs) and scripts located in build/pkgs/$PKG_BASE -# the value of this flag is set in the next codeblock -USE_LOCAL_SCRIPTS= - # PKG_SRC should look like "package-VERSION" or just "package". # If VERSION matches the version in build/pkgs or there is no version # specified, use the local scripts; otherwise we try to find a package @@ -315,7 +309,6 @@ if [ -z "$PKG_VER" ]; then else PKG_NAME="${PKG_BASE}-${PKG_VER}" fi -USE_LOCAL_SCRIPTS=yes PKG_BASE_VER=`echo $PKG_VER | sed 's/\.p[0-9][0-9]*$//'` PKG_NAME_UPSTREAM=`lookup_param tarball "$PKG_SCRIPTS/checksums.ini" | sed "s/VERSION/$PKG_BASE_VER/"` @@ -351,7 +344,7 @@ EOF fi fi -if [ $INFO -ne 0 -a "$USE_LOCAL_SCRIPTS" = yes ]; then +if [ $INFO -ne 0 ]; then exec sage-spkg-info $PKG_BASE fi @@ -395,20 +388,6 @@ if [ -n "$SAGE_INSTALL_FETCH_ONLY" ]; then exit 0 fi -################################################################## -# Handle --info -################################################################## - -if [ $INFO -ne 0 -a -z "$USE_LOCAL_SCRIPTS" ]; then - cat "$PKG_SCRIPTS/SPKG.txt" - sage-uncompress-spkg "$PKG_SRC" "$PKG_NAME/SPKG.txt" - if [ $? -ne 0 ]; then - error_msg "Error: no file SPKG.txt in $PKG_NAME" - exit 1 - fi - exit 0 -fi - ################################################################## # Setup directories ################################################################## @@ -467,8 +446,6 @@ fi # Extract the package ################################################################## -if [ "$USE_LOCAL_SCRIPTS" = yes ]; then - # New-style package echo "Setting up build directory for $PKG_NAME" cp -RLp "$PKG_SCRIPTS" "$PKG_NAME" cd "$PKG_NAME" || exit $? @@ -478,10 +455,6 @@ if [ "$USE_LOCAL_SCRIPTS" = yes ]; then error_msg "Error: failed to extract $PKG_SRC" exit 1 fi -else - echo >&2 "Error: Installing old-style SPKGs is no longer supported." - exit 1 -fi echo "Finished extraction" @@ -576,14 +549,7 @@ for script in $WRAPPED_SCRIPTS; do script="spkg-$script" if [ -f "$script.in" ]; then - if [ "$USE_LOCAL_SCRIPTS" = "yes" ]; then write_script_wrapper "$(pwd)/$script" "$script_dir" - else - if [ ! -x "$script" ]; then - echo >&2 "WARNING: $script is not executable, making it executable" - chmod +x "$script" - fi - fi fi done From 3b435d972a72feada67f65a8c96796952a91ac90 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 17:00:41 -0700 Subject: [PATCH 108/336] build/bin/sage-dist-helpers (sdh_store_and_pip_install_wheel): Record name of installed distribution name --- build/bin/sage-dist-helpers | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index 4fc586de5e8..b675e1d808b 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -266,6 +266,11 @@ sdh_store_and_pip_install_wheel() { fi $sudo sage-pip-install $root "$wheel" || \ sdh_die "Error installing ${wheel##*/}" + if [ -n "${SAGE_PKG_DIR}" ]; then + # Record name of installed distribution name for uninstallation. + wheel=${wheel##*/} + echo "${wheel%%-*}" >> ${SAGE_PKG_DIR}/spkg-piprm-requirements.txt + fi } sdh_cmake() { From 34bd4459deb61a8430733a3b88a96fd0dbede243 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 17:01:52 -0700 Subject: [PATCH 109/336] sdh_pip_uninstall (sdh_pip_uninstall): New helper functtion; use same flags in 'make SPKG-clean' for pip packages --- build/bin/sage-dist-helpers | 11 +++++++++++ build/make/Makefile.in | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index b675e1d808b..f849c65b5d9 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -273,6 +273,17 @@ sdh_store_and_pip_install_wheel() { fi } +sdh_pip_uninstall() { + # --disable-pip-version-check: Don't periodically check PyPI to determine whether a new version of pip is available + # --no-input: Disable prompting for input. + # --yes: Don't ask for confirmation of uninstall deletions + # See sage-pip-install for a discussion of the other flags. + python3 -m pip uninstall --isolated --disable-pip-version-check --yes --no-input "$@" + if [ $? -ne 0 ]; then + echo "Warning: pip exited with status $?" >&2 + fi +} + sdh_cmake() { echo "Configuring $PKG_NAME with cmake" cmake . -DCMAKE_INSTALL_PREFIX="${SAGE_INST_LOCAL}" \ diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 8ba89d78c74..88c745ff949 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -585,7 +585,7 @@ $(1)-no-deps: $(AM_V_at)sage-logger -p 'sage --pip install -r "$$(SAGE_ROOT)/build/pkgs/$(1)/requirements.txt"' '$$(SAGE_LOGS)/$(1).log' $(1)-clean: - -sage --pip uninstall -y -r '$$(SAGE_ROOT)/build/pkgs/$(1)/requirements.txt' + -sage --pip uninstall --isolated --yes --no-input -r '$$(SAGE_ROOT)/build/pkgs/$(1)/requirements.txt' .PHONY: $(1) $(1)-clean $(1)-build-deps $(1)-no-deps endef From 49af8aa45b40ae471e4748a7f343f8050df6383e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 17:02:49 -0700 Subject: [PATCH 110/336] build/bin/sage-spkg, build/sage_bootstrap/uninstall.py: Prepare/install/use the spkg-piprm script --- build/bin/sage-spkg | 15 +++++++++++++-- build/sage_bootstrap/uninstall.py | 9 +++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index 2f44f09e904..9415e4c9e03 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -532,9 +532,12 @@ __EOF__ } -WRAPPED_SCRIPTS="build install check preinst postinst prerm postrm" -INSTALLED_SCRIPTS="prerm postrm" +INSTALLED_SCRIPTS="prerm piprm postrm" +WRAPPED_SCRIPTS="build install check preinst postinst $INSTALLED_SCRIPTS" +# Prepare script for uninstallation of pacakges that use sdh_pip_install +# or sdh_store_and_pip_install_wheel. +echo 'sdh_pip_uninstall -r $SAGE_SPKG_SCRIPTS/$PKG_BASE/spkg-piprm-requirements.txt' > spkg-piprm.in for script in $WRAPPED_SCRIPTS; do # 'Installed' scripts are not run immediately out of the package build @@ -707,6 +710,14 @@ unset SAGE_DESTDIR_LOCAL # $SAGE_SPKG_SCRIPTS; they are not included in the package's manifest, but are # removed by sage-spkg-uninstall INSTALLED_SCRIPTS_DEST="$SAGE_SPKG_SCRIPTS/$PKG_BASE" + +if [ -f spkg-piprm-requirements.txt ]; then + INSTALLED_SCRIPTS="$INSTALLED_SCRIPTS piprm-requirements.txt" +else + # No packages to uninstall with pip, so remove the prepared uninstall script + rm -f spkg-piprm spkg-piprm.in +fi + for script in $INSTALLED_SCRIPTS; do script="spkg-$script" diff --git a/build/sage_bootstrap/uninstall.py b/build/sage_bootstrap/uninstall.py index 90bbb9927b4..4d7cd8558ef 100644 --- a/build/sage_bootstrap/uninstall.py +++ b/build/sage_bootstrap/uninstall.py @@ -204,6 +204,15 @@ def rmdir(dirname): # Remove file's directory if it is now empty rmdir(dirname) + # Run the package's piprm script, if it exists. + try: + run_spkg_script(spkg_name, spkg_scripts, 'piprm', + 'pip-uninstall') + except Exception: + print("Warning: Error running the pip-uninstall script for " + "'{0}'; uninstallation may have left behind some files".format( + spkg_name), file=sys.stderr) + # Run the package's postrm script, if it exists. # If an error occurs here print a warning, but complete the # uninstallation; otherwise we leave the package in a broken From c9e42ec1724006487fa17eeeb6a8c7e8ed7042de Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 28 Jun 2021 07:22:14 -0700 Subject: [PATCH 111/336] src/sage/libs/readline.pyx: Unused, remove; remove dependency of sagelib on readline --- build/pkgs/sagelib/dependencies | 2 +- src/sage/libs/readline.pyx | 311 -------------------------------- 2 files changed, 1 insertion(+), 312 deletions(-) delete mode 100644 src/sage/libs/readline.pyx diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies index 62aa32fca69..817640965dd 100644 --- a/build/pkgs/sagelib/dependencies +++ b/build/pkgs/sagelib/dependencies @@ -1,4 +1,4 @@ -FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy pycygwin pynac $(PYTHON) ratpoints readline rw sage_conf singular symmetrica zn_poly $(PCFILES) +FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy pycygwin pynac $(PYTHON) ratpoints rw sage_conf singular symmetrica zn_poly $(PCFILES) ---------- All lines of this file are ignored except the first. diff --git a/src/sage/libs/readline.pyx b/src/sage/libs/readline.pyx deleted file mode 100644 index 5d4e253fd78..00000000000 --- a/src/sage/libs/readline.pyx +++ /dev/null @@ -1,311 +0,0 @@ -# distutils: libraries = readline -""" -Readline - -This is the library behind the command line input, it takes keypresses -until you hit Enter and then returns it as a string to Python. We hook -into it so we can make it redraw the input area. - -EXAMPLES:: - - sage: from sage.libs.readline import * - sage: replace_line('foobar', 0) - sage: set_point(3) - sage: print('current line:', repr(copy_text(0, get_end()))) - current line: 'foobar' - sage: print('cursor position:', get_point()) - cursor position: 3 - -When printing with :class:`interleaved_output` the prompt and current -line is removed:: - - sage: with interleaved_output(): - ....: print('output') - ....: print('current line: ', - ....: repr(copy_text(0, get_end()))) - ....: print('cursor position:', get_point()) - output - current line: '' - cursor position: 0 - -After the interleaved output, the line and cursor is restored to the -old value:: - - sage: print('current line:', repr(copy_text(0, get_end()))) - current line: 'foobar' - sage: print('cursor position:', get_point()) - cursor position: 3 - -Finally, clear the current line for the remaining doctests:: - - sage: replace_line('', 1) -""" - -#***************************************************************************** -# Copyright (C) 2013 Volker Braun -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.cpython.string cimport str_to_bytes, bytes_to_str - - -cdef extern from 'readline/readline.h': - int rl_forced_update_display() - int rl_redisplay() - int rl_message(char* msg) - int rl_clear_message() - void rl_replace_line(char* line, int pos) - char* rl_copy_text(int begin, int end) - void rl_save_prompt() - void rl_restore_prompt() - int rl_read_key() - int rl_set_signals() - int rl_clear_signals() - int rl_crlf() - int rl_initialize() - int rl_catch_signals - int rl_catch_sigwinch - int rl_point - int rl_end - - -def print_status(): - """ - Print readline status for debug purposes - - EXAMPLES:: - - sage: from sage.libs.readline import print_status - sage: print_status() - catch_signals: 1 - catch_sigwinch: 1 - """ - print('catch_signals:', rl_catch_signals) - print('catch_sigwinch:', rl_catch_sigwinch) - -def set_signals(): - """ - Install the readline signal handlers - - Install Readline's signal handler for SIGINT, SIGQUIT, SIGTERM, - SIGALRM, SIGTSTP, SIGTTIN, SIGTTOU, and SIGWINCH, depending on the - values of rl_catch_signals and rl_catch_sigwinch. - - EXAMPLES:: - - sage: from sage.libs.readline import set_signals - sage: set_signals() - 0 - """ - return rl_set_signals() - - -def clear_signals(): - """ - Remove the readline signal handlers - - Remove all of the Readline signal handlers installed by - :func:`set_signals` - - EXAMPLES:: - - sage: from sage.libs.readline import clear_signals - sage: clear_signals() - 0 - """ - return rl_clear_signals() - -def get_point(): - """ - Get the cursor position - - OUTPUT: - - Integer - - EXAMPLES:: - - sage: from sage.libs.readline import get_point, set_point - sage: get_point() - 0 - sage: set_point(5) - sage: get_point() - 5 - sage: set_point(0) - """ - return rl_point - -def get_end(): - """ - Get the end position of the current input - - OUTPUT: - - Integer - - EXAMPLES:: - - sage: from sage.libs.readline import get_end - sage: get_end() - 0 - """ - return rl_end - -def set_point(point): - """ - Set the cursor position - - INPUT: - - - ``point`` -- integer. The new cursor position. - - EXAMPLES:: - - sage: from sage.libs.readline import get_point, set_point - sage: get_point() - 0 - sage: set_point(5) - sage: get_point() - 5 - sage: set_point(0) - """ - global rl_point - rl_point = point - -def forced_update_display(): - """ - Force the line to be updated and redisplayed, whether or not - Readline thinks the screen display is correct. - - EXAMPLES:: - - sage: from sage.libs.readline import forced_update_display - sage: forced_update_display() - 0 - """ - return rl_forced_update_display() - -def copy_text(pos_start, pos_end): - """ - Return a copy of the text between start and end in the current line. - - INPUT: - - - ``pos_start``, ``pos_end`` -- integer. Start and end position. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.libs.readline import copy_text, replace_line - sage: replace_line('foobar', 0) - sage: copy_text(1, 5) - 'ooba' - """ - return bytes_to_str(rl_copy_text(pos_start, pos_end)) - -def replace_line(text, clear_undo): - """ - Replace the contents of rl_line_buffer with text. - - The point and mark are preserved, if possible. - - INPUT: - - - ``text`` -- the new content of the line. - - - ``clear_undo`` -- integer. If non-zero, the undo list associated - with the current line is cleared. - - EXAMPLES:: - - sage: from sage.libs.readline import copy_text, replace_line - sage: replace_line('foobar', 0) - sage: copy_text(1, 5) - 'ooba' - """ - rl_replace_line(str_to_bytes(text), clear_undo) - -def initialize(): - """ - Initialize or re-initialize Readline’s internal state. It’s not - strictly necessary to call this; readline() calls it before - reading any input. - - EXAMPLES:: - - sage: from sage.libs.readline import initialize - sage: initialize() - 0 - """ - return rl_initialize() - - - -class interleaved_output: - - def __init__(self): - """ - Context manager for asynchronous output - - This allows you to show output while at the readline - prompt. When the block is left, the prompt is restored even if - it was clobbered by the output. - - EXAMPLES:: - - sage: from sage.libs.readline import interleaved_output - sage: with interleaved_output(): - ....: print('output') - output - """ - pass - - def __enter__(self): - """ - Called when entering the with block - - EXAMPLES:: - - sage: from sage.libs.readline import interleaved_output - sage: with interleaved_output(): - ....: print('output') - output - """ - self._saved_point = rl_point; - self._saved_line = rl_copy_text(0, rl_end) - rl_save_prompt() - rl_replace_line('', 0) - rl_redisplay() - rl_clear_signals() - - def __exit__(self, exc_type, exc_val, exc_tb): - """ - Called when entering the with block - - EXAMPLES:: - - sage: from sage.libs.readline import interleaved_output - sage: with interleaved_output(): - ....: print('output') - output - """ - rl_set_signals() - rl_replace_line(self._saved_line, 0) - global rl_point - rl_point = self._saved_point - rl_restore_prompt() - rl_forced_update_display() - return False - - - - - From 149aa32856236af9c913eca4a23e33f598ee06df Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 29 Jun 2021 10:05:33 +1000 Subject: [PATCH 112/336] Some cleanup of padic documentation. --- .../rings/padics/padic_ZZ_pX_FM_element.pyx | 246 ++++++++++-------- src/sage/rings/padics/padic_ZZ_pX_element.pyx | 89 ++++--- src/sage/rings/padics/padic_base_leaves.py | 4 +- src/sage/rings/padics/padic_ext_element.pyx | 54 ++-- src/sage/rings/padics/padic_generic.py | 243 ++++++++--------- 5 files changed, 319 insertions(+), 317 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index c3cf8f96770..b88ca008866 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" `p`-Adic ``ZZ_pX`` FM Element This file implements elements of Eisenstein and unramified extensions @@ -24,37 +24,37 @@ element contains the following data: identical among all elements with the same parent, holding common data. - + ``prime_pow.deg`` -- The degree of the extension + * ``prime_pow.deg`` -- the degree of the extension - + ``prime_pow.e`` -- The ramification index + * ``prime_pow.e`` -- the ramification index - + ``prime_pow.f`` -- The inertia degree + * ``prime_pow.f`` -- the inertia degree - + ``prime_pow.prec_cap`` -- the unramified precision cap. For - Eisenstein extensions this is the smallest power of p that is - zero. + * ``prime_pow.prec_cap`` -- the unramified precision cap: for + Eisenstein extensions this is the smallest power of `p` that is + zero - + ``prime_pow.ram_prec_cap`` -- the ramified precision cap. For + * ``prime_pow.ram_prec_cap`` -- the ramified precision cap: for Eisenstein extensions this will be the smallest power of `x` that - is indistinguishable from zero. + is indistinguishable from zero - + ``prime_pow.pow_ZZ_tmp``, prime_pow.pow_mpz_t_tmp``, + * ``prime_pow.pow_ZZ_tmp``, prime_pow.pow_mpz_t_tmp``, ``prime_pow.pow_Integer`` -- functions for accessing powers of `p`. The first two return pointers. See ``sage/rings/padics/pow_computer_ext`` for examples and important warnings. - + ``prime_pow.get_context``, ``prime_pow.get_context_capdiv``, + * ``prime_pow.get_context``, ``prime_pow.get_context_capdiv``, ``prime_pow.get_top_context`` -- obtain an ``ntl_ZZ_pContext_class`` corresponding to `p^n`. The capdiv version divides by ``prime_pow.e`` as appropriate. ``top_context`` corresponds to `p^{prec_cap}`. - + ``prime_pow.restore_context``, + * ``prime_pow.restore_context``, ``prime_pow.restore_context_capdiv``, - ``prime_pow.restore_top_context`` -- restores the given context. + ``prime_pow.restore_top_context`` -- restores the given context - + ``prime_pow.get_modulus``, ``get_modulus_capdiv``, + * ``prime_pow.get_modulus``, ``get_modulus_capdiv``, ``get_top_modulus`` -- Returns a ``ZZ_pX_Modulus_c*`` pointing to a polynomial modulus defined modulo `p^n` (appropriately divided by ``prime_pow.e`` in the capdiv case). @@ -155,7 +155,7 @@ from sage.rings.padics.pow_computer_ext cimport PowComputer_ZZ_pX_FM_Eis cdef class pAdicZZpXFMElement(pAdicZZpXElement): def __init__(self, parent, x, absprec=None, relprec=None, empty=False): - """ + r""" Creates an element of a fixed modulus, unramified or eisenstein extension of `\ZZ_p` or `\QQ_p`. @@ -174,7 +174,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): - ``relprec`` -- not used - ``empty`` -- whether to return after initializing to zero - (without setting anything). + (without setting anything) EXAMPLES:: @@ -320,7 +320,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 sage: W. = R.ext(f) sage: z = W(70/3); z # indirect doctest - 3*w^5 + w^7 + 2*w^9 + 2*w^10 + 4*w^11 + w^12 + 2*w^13 + 3*w^15 + 2*w^16 + 3*w^17 + w^18 + 3*w^19 + 3*w^20 + 2*w^21 + 2*w^22 + 3*w^23 + 4*w^24 + 3*w^5 + w^7 + 2*w^9 + 2*w^10 + 4*w^11 + w^12 + 2*w^13 + 3*w^15 + 2*w^16 + + 3*w^17 + w^18 + 3*w^19 + 3*w^20 + 2*w^21 + 2*w^22 + 3*w^23 + 4*w^24 sage: z * 3 4*w^5 + 3*w^7 + w^9 + 2*w^10 + 2*w^11 + w^13 + 3*w^16 + w^17 + w^18 + 4*w^20 + 4*w^21 + w^22 + 2*w^23 sage: W(70) @@ -353,7 +354,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 sage: W. = R.ext(f) sage: z = W(ntl.ZZ_pX([4,1,16],5^2)); z # indirect doctest - 4 + w + w^2 + 3*w^7 + w^9 + 2*w^11 + 4*w^13 + 3*w^14 + 2*w^15 + w^16 + 3*w^18 + 2*w^19 + 4*w^20 + 4*w^21 + 2*w^22 + 2*w^23 + 4*w^24 + 4 + w + w^2 + 3*w^7 + w^9 + 2*w^11 + 4*w^13 + 3*w^14 + 2*w^15 + w^16 + + 3*w^18 + 2*w^19 + 4*w^20 + 4*w^21 + 2*w^22 + 2*w^23 + 4*w^24 sage: z._ntl_rep() [4 1 16] """ @@ -373,7 +375,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 sage: W. = R.ext(f) sage: z = W(ntl.ZZX([4,1,16])); z # indirect doctest - 4 + w + w^2 + 3*w^7 + w^9 + 2*w^11 + 4*w^13 + 3*w^14 + 2*w^15 + w^16 + 3*w^18 + 2*w^19 + 4*w^20 + 4*w^21 + 2*w^22 + 2*w^23 + 4*w^24 + 4 + w + w^2 + 3*w^7 + w^9 + 2*w^11 + 4*w^13 + 3*w^14 + 2*w^15 + w^16 + + 3*w^18 + 2*w^19 + 4*w^20 + 4*w^21 + 2*w^22 + 2*w^23 + 4*w^24 sage: z._ntl_rep() [4 1 16] """ @@ -397,11 +400,12 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: z._is_inexact_zero() True """ - return ZZ_pX_IsZero(self.value) or (self.prime_pow.e * self.prime_pow.prec_cap != self.prime_pow.ram_prec_cap and self.valuation_c() >= self.prime_pow.ram_prec_cap) + return ZZ_pX_IsZero(self.value) or (self.prime_pow.e * self.prime_pow.prec_cap != self.prime_pow.ram_prec_cap + and self.valuation_c() >= self.prime_pow.ram_prec_cap) def __reduce__(self): """ - Pickles ``self``. + Pickle ``self``. EXAMPLES:: @@ -421,7 +425,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cdef pAdicZZpXFMElement _new_c(self): """ - Returns a new element with the same parent as ``self``. + Return a new element with the same parent as ``self``. EXAMPLES:: @@ -475,8 +479,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def __invert__(self): """ - Returns the inverse of ``self``, as long as ``self`` is a - unit. + Return the inverse of ``self``, as long as ``self`` is a unit. If ``self`` is not a unit, raises a ``ValueError``. @@ -488,7 +491,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: W. = R.ext(f) sage: z = (1 + w)^5 sage: y = ~z; y # indirect doctest - 1 + 4*w^5 + 4*w^6 + 3*w^7 + w^8 + 2*w^10 + w^11 + w^12 + 2*w^14 + 3*w^16 + 3*w^17 + 4*w^18 + 4*w^19 + 2*w^20 + 2*w^21 + 4*w^22 + 3*w^23 + 3*w^24 + 1 + 4*w^5 + 4*w^6 + 3*w^7 + w^8 + 2*w^10 + w^11 + w^12 + 2*w^14 + 3*w^16 + + 3*w^17 + 4*w^18 + 4*w^19 + 2*w^20 + 2*w^21 + 4*w^22 + 3*w^23 + 3*w^24 sage: y.parent() 5-adic Eisenstein Extension Ring in w defined by x^5 + 75*x^3 - 15*x^2 + 125*x - 5 sage: z = z - 1 @@ -510,8 +514,9 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cdef pAdicZZpXFMElement _lshift_c(self, long n): """ - Multiplies ``self`` by the uniformizer raised to the power - ``n``. If ``n`` is negative, right shifts by ``-n``. + Multiply ``self`` by the uniformizer raised to the power ``n``. + + If ``n`` is negative, right shifts by ``-n``. EXAMPLES:: @@ -541,8 +546,9 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def __lshift__(pAdicZZpXFMElement self, shift): """ - Multiplies ``self`` by the uniformizer raised to the power - ``n``. If ``n`` is negative, right shifts by ``-n``. + Multiply ``self`` by the uniformizer raised to the power ``n``. + + If ``n`` is negative, right shifts by ``-n``. EXAMPLES:: @@ -569,7 +575,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cdef pAdicZZpXFMElement _rshift_c(self, long n): """ - Divides ``self`` by the uniformizer raised to the power ``n``. + Divide ``self`` by the uniformizer raised to the power ``n``. + Throws away the non-positive part of the series expansion. The top digits will be garbage. If ``n`` is negative, left shifts by ``-n``. @@ -629,7 +636,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def __rshift__(pAdicZZpXFMElement self, shift): """ - Divides ``self`` by the uniformizer raised to the power ``n``. + Divide ``self`` by the uniformizer raised to the power ``n``. + Throws away the non-positive part of the series expansion. The top digits will be garbage. If ``n`` is negative, left shifts by ``-n``. @@ -642,11 +650,14 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: W. = R.ext(f) sage: z = (1 + w)^5 sage: z - 1 + w^5 + w^6 + 2*w^7 + 4*w^8 + 3*w^10 + w^12 + 4*w^13 + 4*w^14 + 4*w^15 + 4*w^16 + 4*w^17 + 4*w^20 + w^21 + 4*w^24 + 1 + w^5 + w^6 + 2*w^7 + 4*w^8 + 3*w^10 + w^12 + 4*w^13 + 4*w^14 + + 4*w^15 + 4*w^16 + 4*w^17 + 4*w^20 + w^21 + 4*w^24 sage: z >> (6) # indirect doctest - 1 + 2*w + 4*w^2 + 3*w^4 + w^6 + 4*w^7 + 4*w^8 + 4*w^9 + 4*w^10 + 4*w^11 + 4*w^14 + w^15 + 4*w^18 + 4*w^19 + 2*w^20 + 3*w^21 + 2*w^22 + 3*w^24 + 1 + 2*w + 4*w^2 + 3*w^4 + w^6 + 4*w^7 + 4*w^8 + 4*w^9 + 4*w^10 + 4*w^11 + + 4*w^14 + w^15 + 4*w^18 + 4*w^19 + 2*w^20 + 3*w^21 + 2*w^22 + 3*w^24 sage: z >> (-4) - w^4 + w^9 + w^10 + 2*w^11 + 4*w^12 + 3*w^14 + w^16 + 4*w^17 + 4*w^18 + 4*w^19 + 4*w^20 + 4*w^21 + 4*w^24 + w^4 + w^9 + w^10 + 2*w^11 + 4*w^12 + 3*w^14 + w^16 + 4*w^17 + + 4*w^18 + 4*w^19 + 4*w^20 + 4*w^21 + 4*w^24 """ cdef pAdicZZpXFMElement ans if not isinstance(shift, Integer): @@ -667,9 +678,11 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 sage: W. = R.ext(f) sage: z = (1 + w)^5; z - 1 + w^5 + w^6 + 2*w^7 + 4*w^8 + 3*w^10 + w^12 + 4*w^13 + 4*w^14 + 4*w^15 + 4*w^16 + 4*w^17 + 4*w^20 + w^21 + 4*w^24 + 1 + w^5 + w^6 + 2*w^7 + 4*w^8 + 3*w^10 + w^12 + 4*w^13 + 4*w^14 + + 4*w^15 + 4*w^16 + 4*w^17 + 4*w^20 + w^21 + 4*w^24 sage: -z # indirect doctest - 4 + 3*w^5 + 4*w^6 + w^7 + w^8 + w^9 + w^10 + w^11 + 2*w^12 + 4*w^13 + 4*w^15 + 3*w^16 + w^17 + 2*w^18 + 3*w^19 + 2*w^21 + 4*w^23 + 4*w^24 + 4 + 3*w^5 + 4*w^6 + w^7 + w^8 + w^9 + w^10 + w^11 + 2*w^12 + 4*w^13 + + 4*w^15 + 3*w^16 + w^17 + 2*w^18 + 3*w^19 + 2*w^21 + 4*w^23 + 4*w^24 sage: y = z + (-z); y 0 sage: -y @@ -690,9 +703,11 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 sage: W. = R.ext(f) sage: (1 + w)^5 # indirect doctest - 1 + w^5 + w^6 + 2*w^7 + 4*w^8 + 3*w^10 + w^12 + 4*w^13 + 4*w^14 + 4*w^15 + 4*w^16 + 4*w^17 + 4*w^20 + w^21 + 4*w^24 + 1 + w^5 + w^6 + 2*w^7 + 4*w^8 + 3*w^10 + w^12 + 4*w^13 + 4*w^14 + + 4*w^15 + 4*w^16 + 4*w^17 + 4*w^20 + w^21 + 4*w^24 sage: (1 + w)^-5 - 1 + 4*w^5 + 4*w^6 + 3*w^7 + w^8 + 2*w^10 + w^11 + w^12 + 2*w^14 + 3*w^16 + 3*w^17 + 4*w^18 + 4*w^19 + 2*w^20 + 2*w^21 + 4*w^22 + 3*w^23 + 3*w^24 + 1 + 4*w^5 + 4*w^6 + 3*w^7 + w^8 + 2*w^10 + w^11 + w^12 + 2*w^14 + + 3*w^16 + 3*w^17 + 4*w^18 + 4*w^19 + 2*w^20 + 2*w^21 + 4*w^22 + 3*w^23 + 3*w^24 TESTS: @@ -745,7 +760,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cpdef _add_(self, right): """ - Returns ``self`` + ``right``. + Return ``self`` + ``right``. EXAMPLES:: @@ -764,7 +779,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cpdef _mul_(self, right): """ - Returns the product of ``self`` and ``right``. + Return the product of ``self`` and ``right``. EXAMPLES:: @@ -775,7 +790,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: a = W(329) sage: b = W(111) sage: a*b #indirect doctest - 4 + 3*w^5 + w^7 + 2*w^9 + 4*w^11 + 3*w^12 + 2*w^13 + w^14 + 2*w^15 + 3*w^16 + 4*w^17 + 4*w^18 + 2*w^19 + 2*w^21 + 4*w^22 + 2*w^23 + w^24 + 4 + 3*w^5 + w^7 + 2*w^9 + 4*w^11 + 3*w^12 + 2*w^13 + w^14 + 2*w^15 + + 3*w^16 + 4*w^17 + 4*w^18 + 2*w^19 + 2*w^21 + 4*w^22 + 2*w^23 + w^24 sage: a * 0 0 sage: W(125) * W(375) @@ -787,7 +803,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cpdef _sub_(self, right): """ - Returns the difference of ``self`` and ``right``. + Return the difference of ``self`` and ``right``. EXAMPLES:: @@ -798,9 +814,11 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: a = W(329) sage: b = W(111) sage: a - b #indirect doctest - 3 + 3*w^5 + w^7 + 2*w^9 + 3*w^10 + 4*w^11 + 2*w^13 + 2*w^14 + w^15 + 4*w^16 + 2*w^18 + 3*w^19 + 2*w^20 + 3*w^21 + w^22 + w^24 + 3 + 3*w^5 + w^7 + 2*w^9 + 3*w^10 + 4*w^11 + 2*w^13 + 2*w^14 + w^15 + + 4*w^16 + 2*w^18 + 3*w^19 + 2*w^20 + 3*w^21 + w^22 + w^24 sage: W(218) - 3 + 3*w^5 + w^7 + 2*w^9 + 3*w^10 + 4*w^11 + 2*w^13 + 2*w^14 + w^15 + 4*w^16 + 2*w^18 + 3*w^19 + 2*w^20 + 3*w^21 + w^22 + w^24 + 3 + 3*w^5 + w^7 + 2*w^9 + 3*w^10 + 4*w^11 + 2*w^13 + 2*w^14 + w^15 + + 4*w^16 + 2*w^18 + 3*w^19 + 2*w^20 + 3*w^21 + w^22 + w^24 """ cdef pAdicZZpXFMElement ans = self._new_c() ZZ_pX_sub(ans.value, self.value, (right).value) @@ -840,7 +858,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cdef pAdicZZpXFMElement ans = self._new_c() sig_on() if self.prime_pow.e == 1: - ZZ_pX_InvMod_newton_unram(ans.value, right.value, self.prime_pow.get_top_modulus()[0], self.prime_pow.get_top_context().x, self.prime_pow.get_context(1).x) + ZZ_pX_InvMod_newton_unram(ans.value, right.value, self.prime_pow.get_top_modulus()[0], + self.prime_pow.get_top_context().x, self.prime_pow.get_context(1).x) else: ZZ_pX_InvMod_newton_ram(ans.value, right.value, self.prime_pow.get_top_modulus()[0], self.prime_pow.get_top_context().x) ZZ_pX_MulMod_pre(ans.value, self.value, ans.value, self.prime_pow.get_top_modulus()[0]) @@ -849,7 +868,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def __copy__(self): """ - Returns a copy of ``self``. + Return a copy of ``self``. EXAMPLES:: @@ -858,9 +877,11 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 sage: W. = R.ext(f) sage: b = W(45); b - 4*w^5 + 3*w^7 + w^9 + w^10 + 2*w^11 + w^12 + w^13 + 3*w^14 + w^16 + 2*w^17 + w^19 + 4*w^20 + w^21 + 3*w^22 + 3*w^23 + 4*w^24 + 4*w^5 + 3*w^7 + w^9 + w^10 + 2*w^11 + w^12 + w^13 + 3*w^14 + w^16 + + 2*w^17 + w^19 + 4*w^20 + w^21 + 3*w^22 + 3*w^23 + 4*w^24 sage: c = copy(b); c - 4*w^5 + 3*w^7 + w^9 + w^10 + 2*w^11 + w^12 + w^13 + 3*w^14 + w^16 + 2*w^17 + w^19 + 4*w^20 + w^21 + 3*w^22 + 3*w^23 + 4*w^24 + 4*w^5 + 3*w^7 + w^9 + w^10 + 2*w^11 + w^12 + w^13 + 3*w^14 + w^16 + + 2*w^17 + w^19 + 4*w^20 + w^21 + 3*w^22 + 3*w^23 + 4*w^24 sage: c is b False """ @@ -870,8 +891,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def is_zero(self, absprec = None): """ - Returns whether the valuation of ``self`` is at least - ``absprec``. If ``absprec`` is ``None``, returns whether + Return whether the valuation of ``self`` is at least + ``absprec``; if ``absprec`` is ``None``, return whether ``self`` is indistinguishable from zero. EXAMPLES:: @@ -908,8 +929,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): return ans def add_bigoh(self, absprec): - """ - Return a new element truncated modulo \pi^absprec. + r""" + Return a new element truncated modulo `\pi^{\text{absprec}}`. This is only implemented for unramified extension at this point. @@ -920,7 +941,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): OUTPUT: - a new element truncated modulo `\pi^{\mbox{absprec}}`. + A new element truncated modulo `\pi^{\mbox{absprec}}`. EXAMPLES:: @@ -955,9 +976,9 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): return ans def _integer_(self, Z=None): - """ - Returns an integer congruent to this element modulo - `\pi` ^ ``self.absolute_precision()``, if possible. + r""" + Return an integer congruent to this element modulo + `\pi^a`, where `a` is ``self.absolute_precision()``, if possible. EXAMPLES:: @@ -986,15 +1007,16 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): return ans def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this - extension field. Thus the \emph{rows} of this matrix give the - images of each of the `x^i`. The entries of the matrices are - ``IntegerMod`` elements, defined modulo ``p^(self.absprec() / - e)``. + extension field. + + The \emph{rows} of this matrix give the images of each of the `x^i`. + The entries of the matrices are ``IntegerMod`` elements, + defined modulo ``p^(self.absprec() / e)``. - Raises an error if self has negative valuation. + Raises an error if ``self`` has negative valuation. EXAMPLES:: @@ -1047,13 +1069,16 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): # raise NotImplementedError def norm(self, base = None): - """ + r""" Return the absolute or relative norm of this element. - NOTE! This is not the `p`-adic absolute value. This is a - field theoretic norm down to a ground ring. + .. NOTE:: + + This is not the `p`-adic absolute value. This is a + field theoretic norm down to a ground ring. - If you want the `p`-adic absolute value, use the ``abs()`` function instead. + If you want the `p`-adic absolute value, use the :func:`abs()` + function instead. If `K` is given then `K` must be a subfield of the parent `L` of ``self``, in which case the norm is the relative norm from `L` to `K`. @@ -1084,7 +1109,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): return self.parent().ground_ring()(self.unit_part().matrix_mod_pn().det()) * norm_of_uniformizer**self.valuation() def trace(self, base = None): - """ + r""" Return the absolute or relative trace of this element. If `K` is given then `K` must be a subfield of the parent `L` of @@ -1124,7 +1149,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def _ntl_rep(self): """ - Returns an ``ntl_ZZ_pX`` holding ``self.value``. + Return an ``ntl_ZZ_pX`` holding ``self.value``. EXAMPLES:: @@ -1149,7 +1174,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): INPUT: - - ``pad`` -- whether to pad the result with zeros of the appropriate precision + - ``pad`` -- whether to pad the result with zeros of the + appropriate precision EXAMPLES:: @@ -1194,8 +1220,9 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): """ Returns the constant term of ``self.unit``. - Note: this may be divisible by `p` if ``self`` is not - normalized. + .. NOTE:: + + This may be divisible by `p` if ``self`` is not normalized. EXAMPLES:: @@ -1209,9 +1236,9 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): """ return ZZ_pX_ConstTerm(self.value) - def is_equal_to(self, right, absprec = None): + def is_equal_to(self, right, absprec=None): """ - Returns whether ``self`` is equal to ``right`` modulo + Return whether ``self`` is equal to ``right`` modulo ``self.uniformizer()^absprec``. If ``absprec`` is ``None``, returns if ``self`` is equal to @@ -1242,7 +1269,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def lift_to_precision(self, absprec=None): """ - Returns ``self``. + Return ``self``. EXAMPLES:: @@ -1257,21 +1284,21 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def expansion(self, n = None, lift_mode = 'simple'): """ - Returns a list giving a series representation of this element. + Return a list giving a series representation of this element. - If ``lift_mode == 'simple' or 'smallest'``, the returned list will consist of - + integers (in the eisenstein case) or + * integers (in the eisenstein case) or - + lists of integers (in the unramified case). + * lists of integers (in the unramified case). - this element can be reconstructed as - + a sum of elements of the list times powers of the + * a sum of elements of the list times powers of the uniformiser (in the eisenstein case), or - + as a sum of powers of the `p` times polynomials in the + * as a sum of powers of the `p` times polynomials in the generator (in the unramified case). - If ``lift_mode == 'simple'``, all integers will be in the range @@ -1287,8 +1314,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): INPUT: - - ``n`` -- integer (default ``None``). If given, returns the corresponding - entry in the expansion. + - ``n`` -- integer (default ``None``); if given, returns the + corresponding entry in the expansion EXAMPLES:: @@ -1364,16 +1391,16 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def teichmuller_expansion(self, n = None): r""" - Returns a list [`a_0`, `a_1`,..., `a_n`] such that + Return a list `[a_0, a_1, \ldots, a_n]` such that - `a_i^q = a_i` - ``self.unit_part()`` = `\sum_{i = 0}^n a_i \pi^i`, where `\pi` is a - uniformizer of self.parent() + uniformizer of ``self.parent()`` INPUT: - - ``n`` -- integer (default ``None``). If given, returns the corresponding - entry in the expansion. + - ``n`` -- integer (default ``None``); f given, returns the corresponding + entry in the expansion EXAMPLES:: @@ -1381,7 +1408,10 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: E = a.teichmuller_expansion(); E 5-adic expansion of a (teichmuller) sage: list(E) - [a + (2*a^3 + 2*a^2 + 3*a + 4)*5 + (4*a^3 + 3*a^2 + 3*a + 2)*5^2 + (4*a^2 + 2*a + 2)*5^3, (3*a^3 + 3*a^2 + 2*a + 1) + (a^3 + 4*a^2 + 1)*5 + (a^2 + 4*a + 4)*5^2 + (4*a^2 + a + 3)*5^3, (4*a^3 + 2*a^2 + a + 1) + (2*a^3 + 2*a^2 + 2*a + 4)*5 + (3*a^3 + 2*a^2 + a + 1)*5^2 + (a^3 + a^2 + 2)*5^3, (a^3 + a^2 + a + 4) + (3*a^3 + 1)*5 + (3*a^3 + a + 2)*5^2 + (3*a^3 + 3*a^2 + 3*a + 1)*5^3] + [a + (2*a^3 + 2*a^2 + 3*a + 4)*5 + (4*a^3 + 3*a^2 + 3*a + 2)*5^2 + (4*a^2 + 2*a + 2)*5^3, + (3*a^3 + 3*a^2 + 2*a + 1) + (a^3 + 4*a^2 + 1)*5 + (a^2 + 4*a + 4)*5^2 + (4*a^2 + a + 3)*5^3, + (4*a^3 + 2*a^2 + a + 1) + (2*a^3 + 2*a^2 + 2*a + 4)*5 + (3*a^3 + 2*a^2 + a + 1)*5^2 + (a^3 + a^2 + 2)*5^3, + (a^3 + a^2 + a + 4) + (3*a^3 + 1)*5 + (3*a^3 + a + 2)*5^2 + (3*a^3 + 3*a^2 + 3*a + 1)*5^3] sage: sum([c * 5^i for i, c in enumerate(E)]) a sage: all(c^625 == c for c in E) @@ -1453,7 +1483,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def _teichmuller_set_unsafe(self): """ - Sets this element to the Teichmuller representative with the + Set this element to the Teichmuller representative with the same residue. .. WARNING:: @@ -1468,7 +1498,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 sage: W. = R.ext(f) sage: y = W.teichmuller(3); y #indirect doctest - 3 + 3*w^5 + w^7 + 2*w^9 + 2*w^10 + 4*w^11 + w^12 + 2*w^13 + 3*w^15 + 2*w^16 + 3*w^17 + w^18 + 3*w^19 + 3*w^20 + 2*w^21 + 2*w^22 + 3*w^23 + 4*w^24 + 3 + 3*w^5 + w^7 + 2*w^9 + 2*w^10 + 4*w^11 + w^12 + 2*w^13 + 3*w^15 + + 2*w^16 + 3*w^17 + w^18 + 3*w^19 + 3*w^20 + 2*w^21 + 2*w^22 + 3*w^23 + 4*w^24 sage: y^5 == y True sage: g = x^3 + 3*x + 3 @@ -1515,7 +1546,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def precision_absolute(self): """ - Returns the absolute precision of ``self``, ie the precision cap + Return the absolute precision of ``self``, ie the precision cap of ``self.parent()``. EXAMPLES:: @@ -1533,7 +1564,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: a.precision_relative() 15 sage: a.unit_part() - 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + 3*w^21 + w^22 + 3*w^24 + 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + 3*w^21 + w^22 + 3*w^24 """ cdef Integer ans = Integer.__new__(Integer) mpz_set_ui(ans.value, self.prime_pow.ram_prec_cap) @@ -1541,7 +1573,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def precision_relative(self): """ - Returns the relative precision of ``self``, ie the precision cap + Return the relative precision of ``self``, ie the precision cap of ``self.parent()`` minus the ``valuation of self``. EXAMPLES:: @@ -1559,7 +1591,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: a.precision_relative() 15 sage: a.unit_part() - 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + 3*w^21 + w^22 + 3*w^24 + 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + 3*w^21 + w^22 + 3*w^24 """ cdef Integer ans = Integer.__new__(Integer) mpz_set_ui(ans.value, self.prime_pow.ram_prec_cap - self.valuation_c()) @@ -1567,14 +1600,14 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cpdef pAdicZZpXFMElement unit_part(self): """ - Returns the unit part of ``self``, ie + Return the unit part of ``self``, ie ``self / uniformizer^(self.valuation())`` .. WARNING:: If this element has positive valuation then the unit part is not defined to the full precision of the ring. Asking - for the unit part of ZpFM(5)(0) will not raise an error, + for the unit part of ``ZpFM(5)(0)`` will not raise an error, but rather return itself. EXAMPLES:: @@ -1592,7 +1625,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: a.precision_relative() 15 sage: a.unit_part() - 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + 3*w^21 + w^22 + 3*w^24 + 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + 3*w^21 + w^22 + 3*w^24 The unit part inserts nonsense digits if this element has positive valuation:: @@ -1604,7 +1638,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cdef long valuation_c(self): """ - Returns the valuation of ``self``. + Return the valuation of ``self``. EXAMPLES:: @@ -1621,7 +1655,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: a.precision_relative() 15 sage: a.unit_part() - 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + 3*w^21 + w^22 + 3*w^24 + 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + 3*w^21 + w^22 + 3*w^24 """ cdef long valuation, index ZZ_pX_min_val_coeff(valuation, index, self.value, self.prime_pow.pow_ZZ_tmp(1)[0]) @@ -1636,21 +1671,21 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): return index + valuation * self.prime_pow.e cdef ext_p_list(self, bint pos): - """ - Returns a list giving a series representation of ``self``. + r""" + Return a list giving a series representation of ``self``. - The returned list will consist of - + integers (in the eisenstein case) or + * integers (in the eisenstein case) or - + a lists of integers (in the unramified case). + * a lists of integers (in the unramified case). - ``self`` can be reconstructed - + as a sum of elements of the list times powers of the + * as a sum of elements of the list times powers of the uniformiser (in the eisenstein case), or - + as a sum of powers of `p` times polynomials in the + * as a sum of powers of `p` times polynomials in the generator (in the unramified case). - If ``pos`` is ``True``, all integers will be in the range `[0,p-1]`, @@ -1688,7 +1723,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): def make_ZZpXFMElement(parent, f): """ - Creates a new ``pAdicZZpXFMElement`` out of an ``ntl_ZZ_pX`` f, with + Create a new ``pAdicZZpXFMElement`` out of an ``ntl_ZZ_pX`` ``f``, with parent ``parent``. For use with pickling. EXAMPLES:: @@ -1702,3 +1737,4 @@ def make_ZZpXFMElement(parent, f): True """ return pAdicZZpXFMElement(parent, f) + diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index 420bf72703f..1cdd02f9620 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" `p`-Adic ``ZZ_pX Element`` A common superclass implementing features shared by all elements that @@ -350,12 +350,14 @@ cdef class pAdicZZpXElement(pAdicExtElement): return ans def norm(self, base=None): - """ + r""" Return the absolute or relative norm of this element. - NOTE! This is not the `p`-adic absolute value. This is a - field theoretic norm down to a ground ring. If you want the - `p`-adic absolute value, use the ``abs()`` function instead. + .. NOTE:: + + This is not the `p`-adic absolute value. This is a + field theoretic norm down to a ground ring. If you want the + `p`-adic absolute value, use the ``abs()`` function instead. If ``base`` is given then ``base`` must be a subfield of the parent `L` of ``self``, in which case the norm is the relative @@ -426,7 +428,7 @@ cdef class pAdicZZpXElement(pAdicExtElement): return self.parent().ground_ring()(self.unit_part().matrix_mod_pn().det()) * norm_of_uniformizer**self.valuation() def trace(self, base = None): - """ + r""" Return the absolute or relative trace of this element. If ``base`` is given then ``base`` must be a subfield of the @@ -495,7 +497,7 @@ cdef class pAdicZZpXElement(pAdicExtElement): def _rational_(self): """ - Returns a rational approximation of ``self``. + Return a rational approximation of ``self``. This does not try to optimize which rational is picked: see ``algdep`` for another option. @@ -550,9 +552,10 @@ cdef class pAdicZZpXElement(pAdicExtElement): raise NotImplementedError def _test_preprocess_list(R, L): - """ - Given a list of elements convertible to ``ntl_ZZ_p``s, finds the - appropriate absolute precision and returns a list of either ``ntl_ZZs`` or ``ntl_ZZ_ps``. + r""" + Given a list of elements convertible to ``ntl_ZZ_p``s, find the + appropriate absolute precision and return a list of either + ``ntl_ZZs`` or ``ntl_ZZ_ps``. INPUT: @@ -564,16 +567,16 @@ def _test_preprocess_list(R, L): OUTPUT: - - ``LL`` -- if all inputs are integral, a list of ``ntl_ZZs``. - Otherwise, a list of ``ntl_ZZ_ps``, modulo `p^n` which is + - ``LL`` -- if all inputs are integral, a list of ``ntl_ZZs``; + otherwise, a list of ``ntl_ZZ_ps``, modulo `p^n` which is determined by the precision cap of ``R`` and the precisions of - the elements in ``L``. + the elements in ``L`` - - ``min_val`` -- A valuation by which to multiply the elements of - ``LL`` in order to recover the input elements of ``L``. + - ``min_val`` -- a valuation by which to multiply the elements of + ``LL`` in order to recover the input elements of ``L`` - - ``ctx`` -- An ``ntl_ZZ_p_Context`` giving the power of `p` - modulo which the elements in ``LL`` are defined. If ``None``, + - ``ctx`` -- an ``ntl_ZZ_p_Context`` giving the power of `p` + modulo which the elements in ``LL`` are defined; if ``None``, then the elements of ``LL`` are ``ntl_ZZs``. EXAMPLES:: @@ -604,7 +607,7 @@ def _test_preprocess_list(R, L): cdef preprocess_list(pAdicZZpXElement elt, L): """ - See the documentation for _test_preprocess_list + See the documentation for :func:`_test_preprocess_list`. """ cdef Py_ssize_t i cdef ZZ_c tmp @@ -678,7 +681,7 @@ cdef preprocess_list(pAdicZZpXElement elt, L): return L, min_val, ctx def _find_val_aprec_test(R, L): - """ + r""" Given a list ``L``, finds the minimum valuation, minimum absolute precision and minimum common type of the elements. @@ -689,20 +692,19 @@ def _find_val_aprec_test(R, L): OUTPUT: - - ``min_val`` -- the minimum valuation of any element in the list. + - ``min_val`` -- the minimum valuation of any element in the list - ``min_aprec`` -- the minimum absolute precision of any element - in the list. If infinite, a predefined constant ``big`` is - returned instead. - + in the list; if infinite, a predefined constant ``big`` is + returned instead - ``total_type`` -- - + If all elements are integers or ints, 2. + * if all elements are integers or ints: 2 - + If all elements are rationals or integers, 1. + * if all elements are rationals or integers: 1 - + If some elements have finite precision, 0. + * if some elements have finite precision: 0 EXAMPLES:: @@ -720,7 +722,7 @@ def _find_val_aprec_test(R, L): return find_val_aprec(R.prime_pow, L) cdef find_val_aprec(PowComputer_ext pp, L): - """ + r""" Given a list ``L``, finds the minimum valuation, minimum absolute precision and minimum common type of the elements. @@ -731,7 +733,7 @@ cdef find_val_aprec(PowComputer_ext pp, L): - ``L`` -- a list of integers, rationals, ``IntegerMods``, etc. - See the documentation for _find_val_aprec_test for more details. + See the documentation for :func:`_find_val_aprec_test` for more details. """ cdef Py_ssize_t i min_val = big @@ -754,31 +756,31 @@ cdef find_val_aprec(PowComputer_ext pp, L): def _test_get_val_prec(R, a): """ - Returns valuation, absolute precision and type of an input + Return valuation, absolute precision and type of an input element. INPUT: - - ``R`` -- A `p`-adic extension ring to provide a ``PowComputer_ext`` + - ``R`` -- a `p`-adic extension ring to provide a ``PowComputer_ext`` - - ``a`` -- A rational, integer, int, long, ``ntl_ZZ_p``, - ``ntl_ZZ``, ``IntegerMod`` or `p`-adic base element. + - ``a`` -- a rational, integer, int, long, ``ntl_ZZ_p``, + ``ntl_ZZ``, ``IntegerMod`` or `p`-adic base element OUTPUT: - ``val`` -- if ``a`` is exact, ``a.valuation(p)``, otherwise ``min(0, a.valuation())`` - - ``aprec`` -- the absolute precision of ``a``. If ``a`` is - exact, a large predefined constant. + - ``aprec`` -- the absolute precision of ``a``; if ``a`` is + exact, a large predefined constant - type -- - + 2 if ``a`` is an integer, int or long; + * 2 - if ``a`` is an integer, int or long - + 1 if ``a`` is a rational. + * 1 - if ``a`` is a rational - + 0 if ``a`` has finite precision. + * 0 - if ``a`` has finite precision EXAMPLES:: @@ -825,17 +827,17 @@ def _test_get_val_prec(R, a): return get_val_prec(R.prime_pow, a) cdef get_val_prec(PowComputer_ext pp, a): - """ - Returns valuation, absolute precision and type of an input element. + r""" + Return valuation, absolute precision and type of an input element. INPUT: - - ``pp`` -- A ``PowComputer_ext`` + - ``pp`` -- a ``PowComputer_ext`` - - ``a`` -- A rational, integer, int, long, ``ntl_ZZ_p``, - ``ntl_ZZ``, ``IntegerMod`` or `p`-adic base element. + - ``a`` -- a rational, integer, int, long, ``ntl_ZZ_p``, + ``ntl_ZZ``, ``IntegerMod`` or `p`-adic base element - See _test_get_val_prec for more details. + See :func:`_test_get_val_prec` for more details. """ cdef ntl_ZZ py_tmp if isinstance(a, Integer): @@ -897,3 +899,4 @@ cdef get_val_prec(PowComputer_ext pp, a): print(py_tmp) raise TypeError("modulus must be a positive power of the appropriate prime") raise TypeError("unsupported type for list element: %s" % type(a)) + diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index cbf71588bfd..4d4f44735d3 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1101,7 +1101,7 @@ def random_element(self, prec=None, integral=False): ######### class pAdicRingRelaxed(pAdicRelaxedGeneric, pAdicRingBaseGeneric): - """ + r""" An implementation of relaxed arithmetics over `\ZZ_p`. INPUT: @@ -1136,7 +1136,7 @@ def __init__(self, p, prec, print_mode, names): self._element_class_prefix = "pAdicRelaxedElement_" class pAdicFieldRelaxed(pAdicRelaxedGeneric, pAdicFieldBaseGeneric): - """ + r""" An implementation of relaxed arithmetics over `\QQ_p`. INPUT: diff --git a/src/sage/rings/padics/padic_ext_element.pyx b/src/sage/rings/padics/padic_ext_element.pyx index f1f663cc6da..fcf74c412e3 100644 --- a/src/sage/rings/padics/padic_ext_element.pyx +++ b/src/sage/rings/padics/padic_ext_element.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" p-Adic Extension Element A common superclass for all elements of extension rings and field of `\ZZ_p` and @@ -270,7 +270,7 @@ cdef class pAdicExtElement(pAdicGenericElement): def _const_term_test(self): """ - Returns the constant term of a polynomial representing self. + Returns the constant term of a polynomial representing ``self``. This function is mainly for troubleshooting, and the meaning of the return value will depend on whether self is capped @@ -295,25 +295,26 @@ cdef class pAdicExtElement(pAdicGenericElement): def _ext_p_list(self, pos): """ - Returns a list of integers (in the Eisenstein case) or a list - of lists of integers (in the unramified case). self can be - reconstructed as a sum of elements of the list times powers of - the uniformiser (in the Eisenstein case), or as a sum of + Return a list of integers (in the Eisenstein case) or a list + of lists of integers (in the unramified case). + + ``self`` can be reconstructed as a sum of elements of the list times + powers of the uniformiser (in the Eisenstein case), or as a sum of powers of the p times polynomials in the generator (in the unramified case). - Note that zeros are truncated from the returned list, so you - must use the valuation() function to completely recover self. + Note that zeros are truncated from the returned list, so you must + use the :func:`valuation()` function to completely recover ``self``. INPUT: - - pos -- bint. If True, all integers will be in the range [0,p-1], - otherwise they will be in the range [(1-p)/2, p/2]. + - ``pos`` -- boolean; if ``True``, all integers will be in the range + `[0,p-1]`, otherwise they will be in the range `[(1-p)/2, p/2]`. OUTPUT: - - L -- A list of integers or list of lists giving the - series expansion of self. + - a list of integers or list of lists giving the + series expansion of ``self`` EXAMPLES:: @@ -331,13 +332,12 @@ cdef class pAdicExtElement(pAdicGenericElement): return self.ext_p_list(pos) def frobenius(self, arithmetic=True): - """ - Returns the image of this element under the Frobenius automorphism + r""" + Return the image of this element under the Frobenius automorphism applied to its parent. INPUT: - - ``self`` -- an element of an unramified extension. - ``arithmetic`` -- whether to apply the arithmetic Frobenius (acting by raising to the `p`-th power on the residue field). If ``False`` is provided, the image of geometric Frobenius (raising to the `(1/p)`-th @@ -412,7 +412,7 @@ cdef class pAdicExtElement(pAdicGenericElement): INPUT: - - ``absprec`` - a non-negative integer (default: ``1``) + - ``absprec`` -- a non-negative integer (default: ``1``) - ``field`` -- boolean (default ``None``). For precision 1, whether to return an element of the residue field or a residue ring. Currently unused. @@ -449,7 +449,7 @@ cdef class pAdicExtElement(pAdicGenericElement): sage: a.residue(2) Traceback (most recent call last): ... - NotImplementedError: reduction modulo p^n with n>1. + NotImplementedError: reduction modulo p^n with n>1 Eisenstein case:: @@ -461,9 +461,9 @@ cdef class pAdicExtElement(pAdicGenericElement): sage: a.residue(2) Traceback (most recent call last): ... - NotImplementedError: residue() not implemented in extensions for absprec larger than one. + NotImplementedError: residue() not implemented in extensions for absprec larger than one - TESTS: + TESTS:: sage: K = Qp(3,5) sage: S. = R[] @@ -471,7 +471,7 @@ cdef class pAdicExtElement(pAdicGenericElement): sage: (a/3).residue(0) Traceback (most recent call last): ... - ValueError: element must have non-negative valuation in order to compute residue. + ValueError: element must have non-negative valuation in order to compute residue sage: R = ZpFM(3,5) sage: S. = R[] @@ -481,23 +481,22 @@ cdef class pAdicExtElement(pAdicGenericElement): sage: a.residue(-1) Traceback (most recent call last): ... - ValueError: cannot reduce modulo a negative power of the uniformizer. + ValueError: cannot reduce modulo a negative power of the uniformizer sage: a.residue(16) Traceback (most recent call last): ... - NotImplementedError: residue() not implemented in extensions for absprec larger than one. - + NotImplementedError: residue() not implemented in extensions for absprec larger than one """ if absprec < 0: - raise ValueError("cannot reduce modulo a negative power of the uniformizer.") + raise ValueError("cannot reduce modulo a negative power of the uniformizer") if self.valuation() < 0: - raise ValueError("element must have non-negative valuation in order to compute residue.") + raise ValueError("element must have non-negative valuation in order to compute residue") R = self.parent() if check_prec and (R.is_fixed_mod() or R.is_floating_point()): check_prec = False if check_prec and absprec > self.precision_absolute(): from precision_error import PrecisionError - raise PrecisionError("not enough precision known in order to compute residue.") + raise PrecisionError("not enough precision known in order to compute residue") if field and absprec != 1: raise ValueError("field keyword may only be set at precision 1") @@ -507,4 +506,5 @@ cdef class pAdicExtElement(pAdicGenericElement): elif absprec == 1: return R.residue_field()(self.expansion(0)) else: - raise NotImplementedError("residue() not implemented in extensions for absprec larger than one.") + raise NotImplementedError("residue() not implemented in extensions for absprec larger than one") + diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index ae15159be84..36ee00206d7 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -1,4 +1,4 @@ -""" +r""" p-Adic Generic A generic superclass for all p-adic parents. @@ -49,15 +49,15 @@ def __init__(self, base, p, prec, print_mode, names, element_class, category=Non INPUT: - - base -- Base ring. - - p -- prime - - print_mode -- dictionary of print options - - names -- how to print the uniformizer - - element_class -- the class for elements of this ring + - ``base`` -- Base ring + - ``p`` -- prime + - ``print_mode`` -- dictionary of print options + - ``names`` -- how to print the uniformizer + - ``element_class`` -- the class for elements of this ring EXAMPLES:: - sage: R = Zp(17) #indirect doctest + sage: R = Zp(17) # indirect doctest """ if category is None: if self.is_field(): @@ -71,7 +71,7 @@ def __init__(self, base, p, prec, print_mode, names, element_class, category=Non def some_elements(self): r""" - Returns a list of elements in this ring. + Return a list of elements in this ring. This is typically used for running generic tests (see :class:`TestSuite`). @@ -92,13 +92,24 @@ def some_elements(self): def _modified_print_mode(self, print_mode): """ - Returns a dictionary of print options, starting with self's + Return a dictionary of print options, starting with ``self``'s print options but modified by the options in the dictionary - print_mode. + ``print_mode``. INPUT: - - print_mode -- dictionary with keys in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet'] + - ``print_mode`` -- dictionary with keys in + + * mode + * pos + * ram_name + * unram_name + * var_name + * max_ram_terms + * max_unram_terms + * max_terse_terms + * sep + * alphabet EXAMPLES:: @@ -117,7 +128,7 @@ def _modified_print_mode(self, print_mode): def ngens(self): """ - Returns the number of generators of self. + Return the number of generators of ``self``. We conventionally define this as 1: for base rings, we take a uniformizer as the generator; for extension rings, we take a @@ -134,7 +145,7 @@ def ngens(self): def gens(self): """ - Returns a list of generators. + Return a list of generators. EXAMPLES:: @@ -149,7 +160,7 @@ def gens(self): def __richcmp__(self, other, op): """ - Return 0 if self == other, and 1 or -1 otherwise. + Rich comparison of ``self`` with ``other``. We consider two p-adic rings or fields to be equal if they are equal mathematically, and also have the same precision cap and @@ -192,15 +203,7 @@ def __richcmp__(self, other, op): def print_mode(self): r""" - Returns the current print mode as a string. - - INPUT: - - self -- a p-adic field - - OUTPUT: - - string -- self's print mode + Return the current print mode as a string. EXAMPLES:: @@ -212,15 +215,7 @@ def print_mode(self): def characteristic(self): r""" - Returns the characteristic of self, which is always 0. - - INPUT: - - self -- a p-adic parent - - OUTPUT: - - integer -- self's characteristic, i.e., 0 + Return the characteristic of ``self``, which is always 0. EXAMPLES:: @@ -231,11 +226,7 @@ def characteristic(self): def prime(self): """ - Returns the prime, ie the characteristic of the residue field. - - INPUT: - - self -- a p-adic parent + Return the prime, ie the characteristic of the residue field. OUTPUT: @@ -250,10 +241,10 @@ def prime(self): return self.prime_pow._prime() def uniformizer_pow(self, n): - """ - Returns p^n, as an element of self. + r""" + Return `p^n`, as an element of ``self``. - If n is infinity, returns 0. + If ``n`` is infinity, returns 0. EXAMPLES:: @@ -269,7 +260,8 @@ def uniformizer_pow(self, n): def _unram_print(self): """ - For printing. Will be None if the unramified subextension of self is of degree 1 over Z_p or Q_p. + For printing; will be ``None`` if the unramified subextension of + ``self`` is of degree 1 over Z_p or Q_p. EXAMPLES:: @@ -295,15 +287,7 @@ def residue_characteristic(self): def residue_class_field(self): """ - Returns the residue class field. - - INPUT: - - self -- a p-adic ring - - OUTPUT: - - the residue field + Return the residue class field. EXAMPLES:: @@ -317,15 +301,7 @@ def residue_class_field(self): def residue_field(self): """ - Returns the residue class field. - - INPUT: - - self -- a p-adic ring - - OUTPUT: - - the residue field + Return the residue class field. EXAMPLES:: @@ -338,7 +314,8 @@ def residue_field(self): def residue_ring(self, n): """ - Returns the quotient of the ring of integers by the nth power of the maximal ideal. + Return the quotient of the ring of integers by the ``n``-th + power of the maximal ideal. EXAMPLES:: @@ -351,15 +328,7 @@ def residue_ring(self, n): def residue_system(self): """ - Returns a list of elements representing all the residue classes. - - INPUT: - - self -- a p-adic ring - - OUTPUT: - - list of elements -- a list of elements representing all the residue classes + Return a list of elements representing all the residue classes. EXAMPLES:: @@ -371,7 +340,8 @@ def residue_system(self): def _fraction_field_key(self, print_mode=None): """ - Changes print_mode from a dictionary to a tuple and raises a deprecation warning if it is present. + Changes``print_mode`` from a dictionary to a tuple and raises + a deprecation warning if it is present. EXAMPLES:: @@ -391,7 +361,7 @@ def _fraction_field_key(self, print_mode=None): @cached_method(key=_fraction_field_key) def fraction_field(self, print_mode=None): r""" - Returns the fraction field of this ring or field. + Return the fraction field of this ring or field. For `\ZZ_p`, this is the `p`-adic field with the same options, and for extensions, it is just the extension of the fraction @@ -402,12 +372,12 @@ def fraction_field(self, print_mode=None): INPUT: - - ``print_mode`` -- a dictionary containing print options. - Defaults to the same options as this ring. + - ``print_mode`` -- (optional) a dictionary containing print options; + defaults to the same options as this ring OUTPUT: - - the fraction field of this ring. + - the fraction field of this ring EXAMPLES:: @@ -454,7 +424,7 @@ def fraction_field(self, print_mode=None): def integer_ring(self, print_mode=None): r""" - Returns the ring of integers of this ring or field. + Return the ring of integers of this ring or field. For `\QQ_p`, this is the `p`-adic ring with the same options, and for extensions, it is just the extension of the ring @@ -462,12 +432,12 @@ def integer_ring(self, print_mode=None): INPUT: - - ``print_mode`` -- a dictionary containing print options. - Defaults to the same options as this ring. + - ``print_mode`` -- (optional) a dictionary containing print options; + defaults to the same options as this ring OUTPUT: - - the ring of elements of this field with nonnegative valuation. + - the ring of elements of this field with nonnegative valuation EXAMPLES:: @@ -519,16 +489,15 @@ def integer_ring(self, print_mode=None): def teichmuller(self, x, prec = None): r""" - Returns the teichmuller representative of x. + Return the Teichmuller representative of ``x``. INPUT: - - self -- a p-adic ring - - x -- something that can be cast into self + - ``x`` -- something that can be cast into ``self`` OUTPUT: - - element -- the teichmuller lift of x + - the teichmuller lift of ``x`` EXAMPLES:: @@ -549,13 +518,16 @@ def teichmuller(self, x, prec = None): sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 sage: W. = R.ext(f) sage: y = W.teichmuller(3); y - 3 + 3*w^5 + w^7 + 2*w^9 + 2*w^10 + 4*w^11 + w^12 + 2*w^13 + 3*w^15 + 2*w^16 + 3*w^17 + w^18 + 3*w^19 + 3*w^20 + 2*w^21 + 2*w^22 + 3*w^23 + 4*w^24 + O(w^25) + 3 + 3*w^5 + w^7 + 2*w^9 + 2*w^10 + 4*w^11 + w^12 + 2*w^13 + 3*w^15 + + 2*w^16 + 3*w^17 + w^18 + 3*w^19 + 3*w^20 + 2*w^21 + 2*w^22 + + 3*w^23 + 4*w^24 + O(w^25) sage: y^5 == y True sage: g = x^3 + 3*x + 3 sage: A. = R.ext(g) sage: b = A.teichmuller(1 + 2*a - a^2); b - (4*a^2 + 2*a + 1) + 2*a*5 + (3*a^2 + 1)*5^2 + (a + 4)*5^3 + (a^2 + a + 1)*5^4 + O(5^5) + (4*a^2 + 2*a + 1) + 2*a*5 + (3*a^2 + 1)*5^2 + (a + 4)*5^3 + + (a^2 + a + 1)*5^4 + O(5^5) sage: b^125 == b True @@ -573,7 +545,7 @@ def teichmuller(self, x, prec = None): # Since teichmuller representatives are defined at infinite precision, # we can lift to precision prec, as long as the absolute precision of ans is positive. if ans.precision_absolute() <= 0: - raise ValueError("Not enough precision to determine Teichmuller representative") + raise ValueError("not enough precision to determine Teichmuller representative") if ans.valuation() > 0: return self(0) if prec is None else self(0, prec) ans = ans.lift_to_precision(prec) @@ -584,15 +556,13 @@ def teichmuller(self, x, prec = None): def teichmuller_system(self): r""" - Returns a set of teichmuller representatives for the invertible elements of `\ZZ / p\ZZ`. - - INPUT: - - - self -- a p-adic ring + Return a set of teichmuller representatives for the invertible + elements of `\ZZ / p\ZZ`. OUTPUT: - - list of elements -- a list of teichmuller representatives for the invertible elements of `\ZZ / p\ZZ` + - a list of teichmuller representatives for the invertible + elements of `\ZZ / p\ZZ` EXAMPLES:: @@ -606,9 +576,9 @@ def teichmuller_system(self): sage: F.teichmuller_system()[3] (2*a + 2) + (4*a + 1)*5 + 4*5^2 + (2*a + 1)*5^3 + (4*a + 1)*5^4 + (2*a + 3)*5^5 + O(5^6) - NOTES: + .. TODO:: - Should this return 0 as well? + Should this return 0 as well? """ R = self.residue_class_field() prec = self.precision_cap() @@ -738,7 +708,7 @@ def _test_add(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -747,7 +717,6 @@ def _test_add(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) elements = tester.some_elements() @@ -778,7 +747,7 @@ def _test_sub(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -787,7 +756,6 @@ def _test_sub(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -818,7 +786,7 @@ def _test_invert(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -827,7 +795,6 @@ def _test_invert(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -856,7 +823,7 @@ def _test_mul(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -865,7 +832,6 @@ def _test_mul(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -886,7 +852,7 @@ def _test_div(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -895,7 +861,6 @@ def _test_div(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -926,7 +891,7 @@ def _test_neg(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -953,7 +918,7 @@ def _test_shift(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -999,7 +964,7 @@ def _test_log(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -1049,7 +1014,7 @@ def _test_teichmuller(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -1082,7 +1047,7 @@ def _test_convert_residue_field(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester` EXAMPLES:: @@ -1134,15 +1099,13 @@ def _log_unit_part_p(self): def frobenius_endomorphism(self, n=1): """ + Return the `n`-th power of the absolute arithmetic Frobenius + endomorphism on this field. + INPUT: - ``n`` -- an integer (default: 1) - OUTPUT: - - The `n`-th power of the absolute arithmetic Frobenius - endomorphism on this field. - EXAMPLES:: sage: K. = Qq(3^5) @@ -1186,7 +1149,6 @@ def _test_elements_eq_transitive(self, **options): sage: R(3) == R(6) False sage: R._test_elements_eq_transitive() - """ pass @@ -1196,7 +1158,7 @@ def valuation(self): OUTPUT: - a valuation that is normalized such that the rational prime `p` has + A valuation that is normalized such that the rational prime `p` has valuation 1. EXAMPLES:: @@ -1229,7 +1191,7 @@ def valuation(self): return pAdicValuation(self) def _primitive_qth_root_of_unity(self, exponent): - """ + r""" Compute the ``p^exponent``-th roots of unity in this ring. INPUT: @@ -1304,13 +1266,13 @@ def _primitive_qth_root_of_unity(self, exponent): return self._qth_roots_of_unity[-2][0], n-2, self._qth_roots_of_unity[-1] def primitive_root_of_unity(self, n=None, order=False): - """ + r""" Return a generator of the group of ``n``-th roots of unity in this ring. INPUT: - - ``n`` -- an integer or ``None`` (default: ``None``): + - ``n`` -- an integer or ``None`` (default: ``None``) - ``order`` -- a boolean (default: ``False``) @@ -1393,7 +1355,7 @@ def roots_of_unity(self, n=None): INPUT: - ``n`` -- an integer or ``None`` (default: ``None``); if - ``None``, the full group of roots of unity is returned. + ``None``, the full group of roots of unity is returned EXAMPLES:: @@ -1467,29 +1429,29 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur - ``secure`` -- a boolean (default: ``False``) - NOTE: + .. NOTE:: - When ``secure`` is ``True``, this method raises an error when - the precision on the input polynomial is not enough to determine - the number of roots in the ground field. This happens when two - roots cannot be separated. - A typical example is the polynomial + When ``secure`` is ``True``, this method raises an error when + the precision on the input polynomial is not enough to determine + the number of roots in the ground field. This happens when two + roots cannot be separated. + A typical example is the polynomial - .. MATH:: + .. MATH:: - (1 + O(p^10))*X^2 + O(p^10)*X + O(p^10) + (1 + O(p^10))*X^2 + O(p^10)*X + O(p^10). - Indeed its discriminant might be any `p`-adic integer divisible - by `p^{10}` (resp. `p^{11}` when `p=2`) and so can be as well - zero, a square and a non-square. - In the first case, the polynomial has one double root; in the - second case, it has two roots; in the third case, it has no - root in `\QQ_p`. + Indeed its discriminant might be any `p`-adic integer divisible + by `p^{10}` (resp. `p^{11}` when `p=2`) and so can be as well + zero, a square and a non-square. + In the first case, the polynomial has one double root; in the + second case, it has two roots; in the third case, it has no + root in `\QQ_p`. - When ``secure`` is ``False``, this method assumes that two - inseparable roots actually collapse. In the above example, - it then answers that the given polynomial has a double root - `O(p^5)`. + When ``secure`` is ``False``, this method assumes that two + inseparable roots actually collapse. In the above example, + it then answers that the given polynomial has a double root + `O(p^5)`. This keyword is ignored when using the ``pari`` algorithm. @@ -1728,7 +1690,7 @@ def _call_(self, x): def section(self): """ - Returns the section from the residue ring or field + Return the section from the residue ring or field back to the p-adic ring or field. EXAMPLES:: @@ -1902,7 +1864,7 @@ def _richcmp_(self, other, op): return NotImplemented return richcmp((self.domain(), self.codomain()), (other.domain(), other.codomain()), op) -def local_print_mode(obj, print_options, pos = None, ram_name = None): +def local_print_mode(obj, print_options, pos=None, ram_name=None): r""" Context manager for safely temporarily changing the print_mode of a p-adic ring/field. @@ -1918,7 +1880,7 @@ def local_print_mode(obj, print_options, pos = None, ram_name = None): .. NOTE:: - For more documentation see ``localvars`` in parent_gens.pyx + For more documentation see ``localvars`` in ``parent_gens.pyx``. """ if isinstance(print_options, str): print_options = {'mode': print_options} @@ -1932,3 +1894,4 @@ def local_print_mode(obj, print_options, pos = None, ram_name = None): if option not in print_options: print_options[option] = obj._printer.dict()[option] return pAdicPrinter(obj, print_options) + From 2de91d2da44ce3dbf578e3ecd1b6af4a41d0e3d3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 29 Jun 2021 00:09:37 -0700 Subject: [PATCH 113/336] build/pkgs/_prereq/distros/opensuse.txt: Add gcc-c++ --- build/pkgs/_prereq/distros/opensuse.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/_prereq/distros/opensuse.txt b/build/pkgs/_prereq/distros/opensuse.txt index fe5391d0314..e30eccb0ac4 100644 --- a/build/pkgs/_prereq/distros/opensuse.txt +++ b/build/pkgs/_prereq/distros/opensuse.txt @@ -16,6 +16,7 @@ python3 tar bc gcc +gcc-c++ # Needed if we download some packages from a https upstream URL ca-certificates # gunzip needed for ppl spkg From 7fc196b04f5708380b49195a4f2c0b1bcb07d396 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Tue, 29 Jun 2021 10:29:32 +0100 Subject: [PATCH 114/336] Typo fix and rephrasing. As per a comment by slelievre some small issues with the documentation were neatened up. --- src/sage/numerical/gauss_legendre.pyx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 000d4339747..1afc283c7ff 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -181,8 +181,8 @@ def integrate_vector_N(f, prec, N=3): This function uses the Gauss-Legendre quadrature scheme to approximate the integral `\int_0^1 f(t) \, dt`. It is different from ``integrate_vector`` - by fixing the number of nodes to use, rather than aiming for a certain - error. + by using a specific number of nodes rather than targeting a specified error + bound on the result. INPUT: @@ -210,9 +210,8 @@ def integrate_vector_N(f, prec, N=3): The nodes and weights are calculated in the real field with ``prec`` bits of precision. If the the vector space in which ``f`` takes values - is over a field which is incomatible with this field (e.g. a finite + is over a field which is incompatible with this field (e.g. a finite field) then a TypeError occurs. - """ nodelist = nodes(N, prec) I = nodelist[0][1]*f(nodelist[0][0]) From 71f134d22244eac6ec4cca7f03c622adb6b6f529 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 29 Jun 2021 09:44:41 -0700 Subject: [PATCH 115/336] build/pkgs/_prereq/distros/opensuse.txt: Add which, glibc-locale-base --- build/pkgs/_prereq/distros/opensuse.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/pkgs/_prereq/distros/opensuse.txt b/build/pkgs/_prereq/distros/opensuse.txt index e30eccb0ac4..f3bfb123e1c 100644 --- a/build/pkgs/_prereq/distros/opensuse.txt +++ b/build/pkgs/_prereq/distros/opensuse.txt @@ -15,6 +15,8 @@ perl python3 tar bc +which +glibc-locale-base gcc gcc-c++ # Needed if we download some packages from a https upstream URL From 20365ae3158ebc77e2c8ea70b1e08316af7b2f5d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 30 Jun 2021 12:28:33 +0200 Subject: [PATCH 116/336] make modular ready for fuzzed doctests --- .../modular/arithgroup/arithgroup_perm.py | 43 ++++++++++++------- src/sage/modular/arithgroup/congroup_sl2z.py | 23 ++++++---- src/sage/modular/dirichlet.py | 35 ++++++++++++--- src/sage/modular/hecke/algebra.py | 8 +++- src/sage/modular/local_comp/local_comp.py | 14 +++--- src/sage/modular/local_comp/type_space.py | 9 +++- src/sage/modular/modform/numerical.py | 9 ++-- .../modular/overconvergent/hecke_series.py | 38 +++++++++++----- 8 files changed, 124 insertions(+), 55 deletions(-) diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index ee60b091032..5ea925b2a0d 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -2049,8 +2049,14 @@ def _spanning_tree_kulkarni(self, root=0, on_right=True): s3,s3 s3,s3,s2 - sage: gens - [(0, 1, 's2'), (3, 3, 's3')] + sage: edges = G.coset_graph().edges(); edges + [(0, 1, 's2'), (0, 1, 's3'), (1, 0, 's2'), (1, 2, 's3'), (2, 0, 's3'), (2, 3, 's2'), (3, 2, 's2'), (3, 3, 's3')] + sage: len(gens) + 2 + sage: (3, 3, 's3') in gens + True + sage: any(e in gens for e in edges[:5]) + True """ from sage.graphs.digraph import DiGraph from sage.misc.prandom import randint @@ -2178,8 +2184,14 @@ def _spanning_tree_verrill(self, root=0, on_right=True): **** sage: wreps ['', 's', 'll', 'l'] - sage: gens - [(2, 0, 'l'), (1, 1, 'l'), (2, 3, 's')] + sage: len(gens) + 3 + sage: (1, 1, 'l') in gens + True + sage: (2, 3, 's') in gens or (3, 2, 's') in gens + True + sage: (2, 0, 'l') in gens + True .. TODO:: @@ -2279,12 +2291,10 @@ def todd_coxeter_s2_s3(self): sage: g1,g2 = gens sage: g1 in G and g2 in G True - sage: g1 - [-1 0] - [ 1 -1] - sage: g2 - [-1 3] - [-1 2] + sage: Matrix(2, 2, [-1, 3, -1, 2]) in gens + True + sage: Matrix(2, 2, [-1, 0, 1, -1]) in gens or Matrix(2, 2, [1, 0, 1, 1]) in gens + True sage: S2 = SL2Z([0,-1,1,0]) sage: S3 = SL2Z([0,1,-1,1]) sage: reps[0] == SL2Z([1,0,0,1]) @@ -2323,11 +2333,14 @@ def todd_coxeter_l_s2(self): [1 0] [ 0 -1] [1 2] [1 1] [0 1], [ 1 0], [0 1], [0 1] ] - sage: gens - [ - [1 3] [ 1 0] [ 2 -3] - [0 1], [-1 1], [ 1 -1] - ] + sage: len(gens) + 3 + sage: Matrix(2, 2, [1, 3, 0, 1]) in gens + True + sage: Matrix(2, 2, [1, 0, -1, 1]) in gens + True + sage: Matrix(2, 2, [1, -3, 1, -2]) in gens or Matrix(2, 2, [2, -3, 1, -1]) in gens + True sage: l [3, 1, 0, 2] sage: s diff --git a/src/sage/modular/arithgroup/congroup_sl2z.py b/src/sage/modular/arithgroup/congroup_sl2z.py index b8d5ed689e3..e58e95fd96f 100644 --- a/src/sage/modular/arithgroup/congroup_sl2z.py +++ b/src/sage/modular/arithgroup/congroup_sl2z.py @@ -194,18 +194,23 @@ def random_element(self, bound=100, *args, **kwds): EXAMPLES:: - sage: SL2Z.random_element() - [60 13] - [83 18] - sage: SL2Z.random_element(5) - [-1 3] - [ 1 -4] + sage: s = SL2Z.random_element() + sage: s.parent() is SL2Z + True + sage: all(a in range(-99, 100) for a in list(s)) + True + sage: S = set() + sage: while len(S) < 180: + ....: s = SL2Z.random_element(5) + ....: assert all(a in range(-4, 5) for a in list(s)) + ....: assert s.parent() is SL2Z + ....: assert s in SL2Z + ....: S.add(s) Passes extra positional or keyword arguments through:: - sage: SL2Z.random_element(5, distribution='1/n') - [ 1 -4] - [ 0 1] + sage: SL2Z.random_element(5, distribution='1/n').parent() is SL2Z + True """ if bound <= 1: raise ValueError("bound must be greater than 1") c = ZZ.random_element(1-bound, bound, *args, **kwds) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index d101f6ab4b5..96e70a3ed00 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -3143,12 +3143,35 @@ def random_element(self): EXAMPLES:: - sage: DirichletGroup(37).random_element() - Dirichlet character modulo 37 of conductor 37 mapping 2 |--> zeta36^4 - sage: DirichletGroup(20).random_element() - Dirichlet character modulo 20 of conductor 4 mapping 11 |--> -1, 17 |--> 1 - sage: DirichletGroup(60).random_element() - Dirichlet character modulo 60 of conductor 3 mapping 31 |--> 1, 41 |--> -1, 37 |--> 1 + sage: D = DirichletGroup(37) + sage: g = D.random_element() + sage: g.parent() is D + True + sage: g**36 + Dirichlet character modulo 37 of conductor 1 mapping 2 |--> 1 + sage: S = set(D.random_element().conductor() for _ in range(100)) + sage: while S != {1, 37}: + ....: S.add(D.random_element().conductor()) + + sage: D = DirichletGroup(20) + sage: g = D.random_element() + sage: g.parent() is D + True + sage: g**4 + Dirichlet character modulo 20 of conductor 1 mapping 11 |--> 1, 17 |--> 1 + sage: S = set(D.random_element().conductor() for _ in range(100)) + sage: while S != {1, 4, 5, 20}: + ....: S.add(D.random_element().conductor()) + + sage: D = DirichletGroup(60) + sage: g = D.random_element() + sage: g.parent() is D + True + sage: g**4 + Dirichlet character modulo 60 of conductor 1 mapping 31 |--> 1, 41 |--> 1, 37 |--> 1 + sage: S = set(D.random_element().conductor() for _ in range(100)) + sage: while S != {1, 3, 4, 5, 12, 15, 20, 60}: + ....: S.add(D.random_element().conductor()) """ e = self(1) for i in range(self.ngens()): diff --git a/src/sage/modular/hecke/algebra.py b/src/sage/modular/hecke/algebra.py index 7966e76db12..ac0d0f3b60a 100644 --- a/src/sage/modular/hecke/algebra.py +++ b/src/sage/modular/hecke/algebra.py @@ -410,8 +410,12 @@ def basis(self): [0 2]) sage: M = ModularSymbols(Gamma0(22), sign=1) - sage: [B[0,0] for B in M.hecke_algebra().basis()] - [-955, -994, -706, -490, -1070] + sage: H = M.hecke_algebra() + sage: B = H.basis() + sage: len(B) + 5 + sage: all(b in H for b in B) + True sage: [B[0, 0] for B in M.anemic_hecke_algebra().basis()] Traceback (most recent call last): ... diff --git a/src/sage/modular/local_comp/local_comp.py b/src/sage/modular/local_comp/local_comp.py index a00268ee911..f0520621474 100644 --- a/src/sage/modular/local_comp/local_comp.py +++ b/src/sage/modular/local_comp/local_comp.py @@ -650,9 +650,11 @@ def characters(self): sage: f = Newforms(GammaH(25, [6]), 3, names='j')[0]; f q + j0*q^2 + 1/3*j0^3*q^3 - 1/3*j0^2*q^4 + O(q^6) sage: Pi = LocalComponent(f, 5) - sage: set(Pi.characters()) - {Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> 1/3*j0^2*d - 1/3*j0^3, 5 |--> 5, - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -1/3*j0^2*d, 5 |--> 5} + sage: Pi.characters() + [ + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> 1/3*j0^2*d - 1/3*j0^3, 5 |--> 5, + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -1/3*j0^2*d, 5 |--> 5 + ] sage: Pi.characters()[0].base_ring() Number Field in d with defining polynomial x^2 - j0*x + 1/3*j0^2 over its base field @@ -779,7 +781,7 @@ def characters(self): flag = G._reduce_Qp(1, x) except ValueError: flag = None - if flag is not None: + if flag is not None: verbose("skipping x=%s as congruent to %s mod p" % (x, flag)) continue @@ -1021,8 +1023,8 @@ def _repr_(self): def characters(self): r""" Return the pair of characters (either of `\QQ_p^*` or of some quadratic - extension) corresponding to this representation. - + extension) corresponding to this representation. + EXAMPLES:: sage: f = [f for f in Newforms(63, 4, names='a') if f[2] == 1][0] diff --git a/src/sage/modular/local_comp/type_space.py b/src/sage/modular/local_comp/type_space.py index ce78d301979..c47b388946b 100644 --- a/src/sage/modular/local_comp/type_space.py +++ b/src/sage/modular/local_comp/type_space.py @@ -370,8 +370,13 @@ def minimal_twist(self): sage: f = Newforms(256,names='a')[0] sage: T = TypeSpace(f,2) - sage: g = T.minimal_twist(); g - q - a*q^3 + O(q^6) + sage: g = T.minimal_twist() + sage: g[0:3] + [0, 1, 0] + sage: str(g[3]) in ('a', '-a', '-1/2*a', '1/2*a') + True + sage: g[4:] + [] sage: g.level() 64 """ diff --git a/src/sage/modular/modform/numerical.py b/src/sage/modular/modform/numerical.py index 933b334d3ea..5cad646379d 100644 --- a/src/sage/modular/modform/numerical.py +++ b/src/sage/modular/modform/numerical.py @@ -211,8 +211,9 @@ def _eigenvectors(self): sage: n = numerical_eigenforms(61, eps=2.0) sage: evectors = n._eigenvectors() - sage: evalues = diagonal_matrix(CDF, [-283.0, 142.0, 108.522012456]) - sage: diff = n._hecke_matrix*evectors - evectors*evalues + sage: evalues = [(matrix((n._hecke_matrix*evectors).column(i))/matrix(evectors.column(i)))[0, 0] + ....: for i in range(evectors.ncols())] + sage: diff = n._hecke_matrix*evectors - evectors*diagonal_matrix(evalues) sage: sum([abs(diff[i,j]) for i in range(5) for j in range(3)]) < 1.0e-9 True """ @@ -443,7 +444,7 @@ def systems_of_eigenvalues(self, bound): EXAMPLES:: - sage: numerical_eigenforms(61).systems_of_eigenvalues(10) # rel tol 6e-14 + sage: numerical_eigenforms(61).systems_of_eigenvalues(10) # rel tol 1e-12 [ [-1.4811943040920152, 0.8060634335253695, 3.1563251746586642, 0.6751308705666477], [-1.0, -2.0000000000000027, -3.000000000000003, 1.0000000000000044], @@ -470,7 +471,7 @@ def systems_of_abs(self, bound): EXAMPLES:: - sage: numerical_eigenforms(61).systems_of_abs(10) # rel tol 6e-14 + sage: numerical_eigenforms(61).systems_of_abs(10) # rel tol 1e-12 [ [0.3111078174659775, 2.903211925911551, 2.525427560843529, 3.214319743377552], [1.0, 2.0000000000000027, 3.000000000000003, 1.0000000000000044], diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index fe6147b0a07..220d34696e6 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -266,8 +266,14 @@ def random_solution(B,K): EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import random_solution - sage: random_solution(5,10) - [1, 1, 1, 1, 0] + sage: s = random_solution(5,10) + sage: sum(s[i]*(i+1) for i in range(5)) + 10 + sage: S = set() + sage: while len(S) != 30: + ....: s = random_solution(5,10) + ....: assert sum(s[i]*(i+1) for i in range(5)) == 10 + ....: S.add(tuple(s)) """ a = [] for i in range(B,1,-1): @@ -703,15 +709,25 @@ def higher_level_UpGj(p, N, klist, m, modformsring, bound, extra_data=False): EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import higher_level_UpGj - sage: higher_level_UpGj(5,3,[4],2,true,6) - [ - [ 1 0 0 0 0 0] - [ 0 1 0 0 0 0] - [ 0 7 0 0 0 0] - [ 0 5 10 20 0 0] - [ 0 7 20 0 20 0] - [ 0 1 24 0 20 0] - ] + sage: A = Matrix([ + ....: [1, 0, 0, 0, 0, 0], + ....: [0, 1, 0, 0, 0, 0], + ....: [0, 7, 0, 0, 0, 0], + ....: [0, 5, 10, 20, 0, 0], + ....: [0, 7, 20, 0, 20, 0], + ....: [0, 1, 24, 0, 20, 0]]) + sage: B = Matrix([ + ....: [1, 0, 0, 0, 0, 0], + ....: [0, 1, 0, 0, 0, 0], + ....: [0, 7, 0, 0, 0, 0], + ....: [0, 19, 0, 20, 0, 0], + ....: [0, 7, 20, 0, 20, 0], + ....: [0, 1, 24, 0, 20, 0]]) + sage: C = higher_level_UpGj(5,3,[4],2,true,6) + sage: len(C) + 1 + sage: C[0] in (A, B) + True sage: len(higher_level_UpGj(5,3,[4],2,true,6,extra_data=True)) 4 """ From f47c47e4560c449524a77daa7cc17f4e630e8483 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 30 Jun 2021 14:19:29 -0700 Subject: [PATCH 117/336] build/pkgs/gcc/spkg-configure.m4, .homebrew-build-env: Accept gcc 11.x --- .homebrew-build-env | 2 +- build/pkgs/gcc/spkg-configure.m4 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.homebrew-build-env b/.homebrew-build-env index a3d58b57ba9..29f0ba3b304 100644 --- a/.homebrew-build-env +++ b/.homebrew-build-env @@ -31,7 +31,7 @@ for l in readline bzip2 ntl; do CPATH="$HOMEBREW/opt/$l/include:$CPATH" fi done -for l in "gcc/lib/gcc/10 gcc/lib/gcc/9"; do +for l in "gcc/lib/gcc/11 gcc/lib/gcc/10 gcc/lib/gcc/9"; do if [ -d "$HOMEBREW/opt/$l" ]; then LIBRARY_PATH="$HOMEBREW/opt/$l:$LIBRARY_PATH" break diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index eb90e6e2c40..4ccbfbce6ca 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -149,8 +149,8 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ # Install our own GCC if the system-provided one is older than gcc-4.8. SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is quite old]) ], - [1[[1-9]].*], [ - # Install our own GCC if the system-provided one is newer than 10.x. + [1[[2-9]].*], [ + # Install our own GCC if the system-provided one is newer than 11.x. # See https://trac.sagemath.org/ticket/29456 SAGE_SHOULD_INSTALL_GCC([$CXX is g++ version $GXX_VERSION, which is too recent for this version of Sage]) ], From 2d5d4b9550eebe91aaa4f46a46a2e07a109c0e9a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 30 Jun 2021 16:45:47 -0700 Subject: [PATCH 118/336] sage.sets.condition_set: New --- src/doc/en/reference/sets/index.rst | 1 + src/sage/sets/all.py | 1 + src/sage/sets/condition_set.py | 125 ++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 src/sage/sets/condition_set.py diff --git a/src/doc/en/reference/sets/index.rst b/src/doc/en/reference/sets/index.rst index 8631e55850a..5f8eb46a83e 100644 --- a/src/doc/en/reference/sets/index.rst +++ b/src/doc/en/reference/sets/index.rst @@ -15,6 +15,7 @@ Set Constructions sage/sets/set_from_iterator sage/sets/finite_enumerated_set sage/sets/recursively_enumerated_set + sage/sets/condition_set sage/sets/finite_set_maps sage/sets/finite_set_map_cy sage/sets/totally_ordered_finite_set diff --git a/src/sage/sets/all.py b/src/sage/sets/all.py index 7e5572945e4..1c1a69e57a7 100644 --- a/src/sage/sets/all.py +++ b/src/sage/sets/all.py @@ -11,4 +11,5 @@ from .primes import Primes from .family import Family from .disjoint_set import DisjointSet +from .condition_set import ConditionSet from .finite_set_maps import FiniteSetMaps diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py new file mode 100644 index 00000000000..c86352f8f16 --- /dev/null +++ b/src/sage/sets/condition_set.py @@ -0,0 +1,125 @@ +r""" +Subsets of a Universe Defined by a Predicate +""" + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.sets_cat import Sets +from sage.symbolic.callable import is_CallableSymbolicExpression +from sage.symbolic.ring import SymbolicRing + +from .set import Set + +class ConditionSet(Parent, UniqueRepresentation): + r""" + Set of elements of a universe that satisfy a predicate + + EXAMPLES:: + + sage: Evens = ConditionSet(ZZ, is_even); Evens + { x ∈ Integer Ring : (x) } + sage: 2 in Evens + True + sage: 3 in Evens + False + sage: 2.0 in Evens + True + + sage: P = polytopes.cube(); P + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices + sage: P.rename("P") + sage: P_inter_B = ConditionSet(P, lambda x: x.norm() < 1.2); P_inter_B + { x ∈ P : at 0x...>(x) } + sage: vector([1, 0, 0]) in P_inter_B + True + sage: vector([1, 1, 1]) in P_inter_B + False + + sage: predicate(x, y, z) = sqrt(x^2 + y^2 + z^2) < 1.2; predicate + (x, y, z) |--> sqrt(x^2 + y^2 + z^2) < 1.20000000000000 + sage: P_inter_B_again = ConditionSet(P, predicate); P_inter_B_again + { (x, y, z) ∈ P : sqrt(x^2 + y^2 + z^2) < 1.20000000000000 } + sage: vector([1, 0, 0]) in P_inter_B + True + sage: vector([1, 1, 1]) in P_inter_B + False + """ + @staticmethod + def __classcall_private__(cls, universe, predicate, category=None): + if category is None: + category = Sets() + if isinstance(universe, Parent): + if universe in Sets().Finite(): + category = category & Sets().Finite() + if is_CallableSymbolicExpression(predicate): + return ConditionSet_callable_symbolic_expression(universe, predicate, category) + return super().__classcall__(cls, universe, predicate, category) + + def __init__(self, universe, predicate, category): + self._universe = universe + self._predicate = predicate + facade = None + if isinstance(universe, Parent): + facade = universe + super().__init__(facade=facade, category=category) + + def _repr_(self): + universe = self._universe + predicate = self._predicate + var = 'x' + return '{ ' + f'{var} ∈ {universe} : {predicate}({var})' + ' }' + + def _element_constructor_(self, *args, **kwds): + try: + universe_element_constructor = self._universe._element_constructor_ + except AttributeError: + if len(args) != 1 or kwds: + raise ValueError('element constructor only takes 1 argument') + element = args[0] + if element not in self._universe: + raise ValueError(f'{element} is not an element of the universe') + else: + element = universe_element_constructor(*args, **kwds) + if not self._predicate(element): + raise ValueError(f'{element} does not satisfy the condition') + return element + + def ambient(self): + return self._universe + +class ConditionSet_callable_symbolic_expression(ConditionSet): + + def _repr_(self): + universe = self._universe + predicate = self._predicate + args = predicate.arguments() + predicate_expr = SymbolicRing._repr_element_(predicate.parent(), predicate) + return '{ ' + f'{args} ∈ {universe} : {predicate_expr}' + ' }' + + def _sympy_(self): + r""" + EXAMPLES:: + + sage: predicate(x, y, z) = sqrt(x^2 + y^2 + z^2) < 12; predicate + (x, y, z) |--> sqrt(x^2 + y^2 + z^2) < 12 + sage: (ZZ^3).rename('ZZ^3') + sage: SmallTriples = ConditionSet(ZZ^3, predicate); SmallTriples + { (x, y, z) ∈ ZZ^3 : sqrt(x^2 + y^2 + z^2) < 12 } + sage: ST = SmallTriples._sympy_(); ST + ConditionSet((x, y, z), sqrt(x**2 + y**2 + z**2) < 12, + ProductSet(Integers, Integers, Integers)) + sage: (1, 3, 5) in ST + True + sage: (5, 7, 9) in ST + False + """ + import sympy + predicate = self._predicate + args = predicate.arguments() + if len(args) == 1: + sym = args._sympy_() + else: + sym = tuple(x._sympy_() for x in args) + return sympy.ConditionSet(sym, + predicate._sympy_(), + base_set=self._universe._sympy_()) From 030bbb0b8b5fb546af3367c9c9a8a55022b420c2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 7 Oct 2018 11:42:31 +1000 Subject: [PATCH 119/336] Implementing deformed Clifford algebras. --- src/doc/en/reference/algebras/index.rst | 3 +- src/doc/en/reference/references/index.rst | 9 + src/sage/algebras/catalog.py | 7 +- src/sage/algebras/quantum_clifford.py | 556 ++++++++++++++++++++++ 4 files changed, 572 insertions(+), 3 deletions(-) create mode 100644 src/sage/algebras/quantum_clifford.py diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 1a415c7c337..b0eb4ad2edb 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -54,9 +54,10 @@ Named associative algebras sage/combinat/posets/moebius_algebra sage/algebras/orlik_terao sage/algebras/orlik_solomon - sage/algebras/quantum_matrix_coordinate_algebra sage/combinat/partition_algebra + sage/algebras/quantum_clifford sage/algebras/quantum_groups/quantum_group_gap + sage/algebras/quantum_matrix_coordinate_algebra sage/algebras/quatalg/quaternion_algebra sage/algebras/rational_cherednik_algebra sage/algebras/schur_algebra diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index c27809681d3..f58cabaa203 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2731,6 +2731,10 @@ REFERENCES: .. [Hat2002] Allen Hatcher, "Algebraic Topology", Cambridge University Press (2002). +.. [Hayashi1990] \T. Hayashi. `q`-*analogues of Clifford and Weyl algebras - + spinor and oscillator representationsof quantum enveloping + algebras*. Comm. Math. Phys. **127** (1990) pp. 129-144. + .. [HC2006] Mark van Hoeij and John Cremona, Solving Conics over function fields. J. Théor. Nombres Bordeaux, 2006. @@ -3568,6 +3572,11 @@ REFERENCES: International Mathematics Research Notices. Vol. 2014, No. 2, pp. 512-550 (2012) +.. [Kwon2014] Jae-Hoon Kwon. `q`-*deformed Clifford algebra and level zero + fundamental representations of quantum affine algebras*. + J. ALgebra, **399** (2014), pp. 927--947. + :doi:`10.1016/j.jalgebra.2013.10.026`. + .. [KX1998] \S. König and C. Xi. *On the structure of cellular algebras*. Algebras and modules, II (Geiranger, 1996), 365–386, diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index 76a59c355cc..8b40fd238ab 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -47,10 +47,12 @@ ` - :class:`algebras.OrlikSolomon ` -- :class:`algebras.QuantumMatrixCoordinate - ` +- :class:`algebras.QuantumClifford + ` - :class:`algebras.QuantumGL ` +- :class:`algebras.QuantumMatrixCoordinate + ` - :class:`algebras.QSym ` - :class:`algebras.Partition ` - :class:`algebras.PlanarPartition ` @@ -113,6 +115,7 @@ lazy_import('sage.combinat.chas.fsym', 'FreeSymmetricFunctions', 'FSym') lazy_import('sage.combinat.ncsf_qsym.qsym', 'QuasiSymmetricFunctions', 'QSym') lazy_import('sage.combinat.grossman_larson_algebras', 'GrossmanLarsonAlgebra', 'GrossmanLarson') +lazy_import('sage.algebras.quantum_clifford', 'QuantumCliffordAlgebra', 'QuantumClifford') lazy_import('sage.algebras.quantum_matrix_coordinate_algebra', 'QuantumMatrixCoordinateAlgebra', 'QuantumMatrixCoordinate') lazy_import('sage.algebras.quantum_matrix_coordinate_algebra', 'QuantumGL') diff --git a/src/sage/algebras/quantum_clifford.py b/src/sage/algebras/quantum_clifford.py new file mode 100644 index 00000000000..d9a75695c13 --- /dev/null +++ b/src/sage/algebras/quantum_clifford.py @@ -0,0 +1,556 @@ +r""" +Quantum Clifford Algebras + +AUTHORS: + +- Travis Scrimshaw (2021-05): initial version +""" + +#***************************************************************************** +# Copyright (C) 2021 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.integer_ring import ZZ +from sage.categories.algebras import Algebras +from sage.categories.fields import Fields +from sage.combinat.free_module import CombinatorialFreeModule +from sage.categories.cartesian_product import cartesian_product +from sage.modules.free_module import FreeModule +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing +from sage.sets.family import Family +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from itertools import product + +class QuantumCliffordAlgebra(CombinatorialFreeModule): + r""" + The quantum Clifford algebra. + + The *quantum Clifford algebra*, or `q`-Clifford algebra, + of rank `n` and twist `k` is the unital associative algebra + `\mathrm{Cl}_{q}(n, k)` over a field `F` with generators + `\psi_a, \psi_a^*, \omega_a` for `a = 1, \dotsc, n` that + satisfy the following relations: + + .. MATH:: + + \begin{aligned} + \omega_a \omega_b & = \omega_b \omega_a, + & \omega_a^{4k} & = (1 + q^{-2k}) \omega_a^{2k} - q^{-2k}, + \\ \omega_a \psi_b & = q^{\delta_{ab}} \psi_b \omega_a, + & \omega_a \psi^*_b & = \psi^*_b \omega_a, + \\ \psi_a \psi_b & + \psi_b \psi_a = 0, + & \psi^*_a \psi^*_b & + \psi^*_b \psi^*_a = 0, + \\ \psi_a \psi^*_a & = \frac{q^k \omega_a^{3k} - q^{-k} \omega_a^k}{q^k - q^{-k}}, + & \psi^*_a \psi_a & = \frac{q^{2k} (\omega_a - \omega_a^{3k})}{q^k - q^{-k}}, + \\ \psi_a \psi^*_b & + \psi_b^* \psi_a = 0 + & & \text{if } a \neq b, + \end{aligned} + + where `q \in F` such that `q^{2k} \neq 1`. + + When `k = 2`, we recover the original definition given by Hayashi in + [Hayashi1990]_. The `k = 1` version was used in [Kwon2014]_. + + INPUT: + + - ``n`` -- the rank + - ``k`` -- (default: 1) the twist + - ``q`` -- (optional) the parameter `q` + - ``F`` -- (default: `\QQ(q)`) the base field that contains ``q`` + + EXAMPLES: + + We construct the rank 3 and twist 1 `q`-Clifford algebra:: + + sage: Cl = algebras.QuantumClifford(3) + sage: Cl + Quantum Clifford algebra of rank 3 and twist 1 with q=q over + Fraction Field of Univariate Polynomial Ring in q over Integer Ring + sage: q = Cl.q() + + Some sample computations:: + + sage: p0, p1, p2, d0, d1, d2, w0, w1, w2 = Cl.gens() + sage: p0 * p1 + psi0*psi1 + sage: p1 * p0 + -psi0*psi1 + sage: p0 * w0 * p1 * d0 * w2 + (1/(q^3-q))*psi1*w2 + (-q/(q^2-1))*psi1*w0^2*w2 + sage: w0^4 + -1/q^2 + ((q^2+1)/q^2)*w0^2 + + We construct the homomorphism from `U_q(\mathfrak{sl}_3)` to + `\mathrm{Cl}(3, 1)` given in (3.17) of [Hayashi1990]_:: + + sage: e1 = p0*d1; e2 = p1*d2 + sage: f1 = p1*d0; f2 = p2*d1 + sage: k1 = w0*~w1; k2 = w1*~w2 + sage: k1i = w1*~w0; k2i = w2*~w1 + sage: (e1, e2, f1, f2, k1, k2, k1i, k2i) + (psi0*psid1, psi1*psid2, + -psid0*psi1, -psid1*psi2, + (q^2+1)*w0*w1 - q^2*w0*w1^3, (q^2+1)*w1*w2 - q^2*w1*w2^3, + (q^2+1)*w0*w1 - q^2*w0^3*w1, (q^2+1)*w1*w2 - q^2*w1^3*w2) + + We check that `k_i` and `k_i^{-1}` are invereses:: + + sage: k1 * k1i + 1 + sage: k2 * k2i + 1 + + The relations between `e_i`, `f_i`, and `k_i`:: + + sage: k1 * f1 == q^-2 * f1 * k1 + True + sage: k2 * f1 == q^1 * f1 * k2 + True + sage: k2 * e1 == q^-1 * e1 * k2 + True + sage: k1 * e1 == q^2 * e1 * k1 + True + sage: e1 * f1 - f1 * e1 == (k1 - k1i)/(q-q^-1) + True + sage: e2 * f1 - f1 * e2 + 0 + + The `q`-Serre relations:: + + sage: e1 * e1 * e2 - (q^1 + q^-1) * e1 * e2 * e1 + e2 * e1 * e1 + 0 + sage: f1 * f1 * f2 - (q^1 + q^-1) * f1 * f2 * f1 + f2 * f1 * f1 + 0 + """ + @staticmethod + def __classcall_private__(cls, n, k=1, q=None, F=None): + """ + Standardize input to ensure a unique representation. + + TESTS:: + + sage: Cl1 = algebras.QuantumClifford(3) + sage: q = PolynomialRing(ZZ, 'q').fraction_field().gen() + sage: Cl2 = algebras.QuantumClifford(3, q=q) + sage: Cl3 = algebras.QuantumClifford(3, 1, q, q.parent()) + sage: Cl1 is Cl2 and Cl2 is Cl3 + True + """ + if q is None: + q = PolynomialRing(ZZ, 'q').fraction_field().gen() + if F is None: + F = q.parent() + q = F(q) + if F not in Fields(): + raise TypeError("base ring must be a field") + return super(QuantumCliffordAlgebra, cls).__classcall__(cls, n, k, q, F) + + def __init__(self, n, k, q, F): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(1,2) + sage: TestSuite(Cl).run(elements=Cl.basis()) + + sage: Cl = algebras.QuantumClifford(1,3) + sage: TestSuite(Cl).run(elements=Cl.basis()) # long time + + sage: Cl = algebras.QuantumClifford(3) # long time + sage: elts = Cl.some_elements() + list(Cl.algebra_generators()) # long time + sage: TestSuite(Cl).run(elements=elts) # long time + + sage: Cl = algebras.QuantumClifford(2,4) # long time + sage: elts = Cl.some_elements() + list(Cl.algebra_generators()) # long time + sage: TestSuite(Cl).run(elements=elts) # long time + """ + self._n = n + self._k = k + self._q = q + self._psi = cartesian_product([(-1,0,1)]*n) + self._w_poly = PolynomialRing(F, n, 'w') + indices = [(tuple(psi), tuple(w)) + for psi in self._psi + for w in product(*[list(range((4-2*abs(psi[i]))*k)) for i in range(n)])] + indices = FiniteEnumeratedSet(indices) + + cat = Algebras(F).FiniteDimensional().WithBasis() + CombinatorialFreeModule.__init__(self, F, indices, category=cat) + self._assign_names(self.algebra_generators().keys()) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: algebras.QuantumClifford(3) + Quantum Clifford algebra of rank 3 and twist 1 with q=q over + Fraction Field of Univariate Polynomial Ring in q over Integer Ring + """ + return "Quantum Clifford algebra of rank {} and twist {} with q={} over {}".format( + self._n, self._k, self._q, self.base_ring()) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3) + sage: latex(Cl) + \operatorname{Cl}_{q}(3, 1) + """ + return "\\operatorname{Cl}_{%s}(%s, %s)" % (self._q, self._n, self._k) + + def _repr_term(self, m): + r""" + Return a string representation of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3, 3) + sage: Cl._repr_term( ((1, 0, -1), (0, -2, 5)) ) + 'psi0*psid2*w1^-2*w2^5' + sage: Cl._repr_term( ((1, 0, -1), (0, 0, 0)) ) + 'psi0*psid2' + sage: Cl._repr_term( ((0, 0, 0), (0, -2, 5)) ) + 'w1^-2*w2^5' + sage: Cl._repr_term( ((0, 0, 0), (0, 0, 0)) ) + '1' + + sage: Cl(5) + 5 + """ + p, v = m + rp = '*'.join('psi%s'%i if p[i] > 0 else 'psid%s'%i + for i in range(self._n) if p[i] != 0) + gen_str = lambda e: '' if e == 1 else '^%s'%e + rv = '*'.join('w%s'%i + gen_str(v[i]) for i in range(self._n) if v[i] != 0) + if rp: + if rv: + return rp + '*' + rv + return rp + if rv: + return rv + return '1' + + def _latex_term(self, m): + r""" + Return a latex representation for the basis element indexed by ``m``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3, 3) + sage: Cl._latex_term( ((1, 0, -1), (0, -2, 5)) ) + '\\psi_{0}\\psi^{\\dagger}_{2}\\omega_{1}^{-2}\\omega_{2}^{5}' + sage: Cl._latex_term( ((1, 0, -1), (0, 0, 0)) ) + '\\psi_{0}\\psi^{\\dagger}_{2}' + sage: Cl._latex_term( ((0, 0, 0), (0, -2, 5)) ) + '\\omega_{1}^{-2}\\omega_{2}^{5}' + sage: Cl._latex_term( ((0, 0, 0), (0, 0, 0)) ) + '1' + + sage: latex(Cl(5)) + 5 + """ + from sage.misc.latex import latex + p, v = m + rp = ''.join('\\psi_{%s}'%i if p[i] > 0 else '\\psi^{\dagger}_{%s}'%i + for i in range(self._n) if p[i] != 0) + gen_str = lambda e: '' if e == 1 else '^{%s}'%e + rv = ''.join('\\omega_{%s}'%i + gen_str(v[i]) + for i in range(self._n) if v[i] != 0) + if not rp and not rv: + return '1' + return rp + rv + + def q(self): + r""" + Return the `q` of ``self``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3) + sage: Cl.q() + q + + sage: Cl = algebras.QuantumClifford(3, q=QQ(-5)) + sage: Cl.q() + -5 + """ + return self._q + + def twist(self): + r""" + Return the twist `k` of ``self``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3, 2) + sage: Cl.twist() + 2 + """ + return self._k + + def rank(self): + r""" + Return the rank `k` of ``self``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3, 2) + sage: Cl.rank() + 3 + """ + return self._n + + def dimension(self): + r""" + Return the dimension of ``self``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3) + sage: Cl.dimension() + 512 + + sage: Cl = algebras.QuantumClifford(4, 2) + sage: Cl.dimension() + 65536 + """ + return ZZ(8*self._k)**self._n + + @cached_method + def algebra_generators(self): + r""" + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3) + sage: Cl.algebra_generators() + Finite family {'psi0': psi0, 'psi1': psi1, 'psi2': psi2, + 'psid0': psid0, 'psid1': psid1, 'psid2': psid2, + 'w0': w0, 'w1': w1, 'w2': w2} + """ + one = (0,) * self._n # one in the corresponding free abelian group + zero = [0] * self._n + I = self._indices + d = {} + for i in range(self._n): + r = list(zero) # Make a copy + r[i] = 1 + d['psi%s'%i] = self.monomial( (self._psi(r), one) ) + r[i] = -1 + d['psid%s'%i] = self.monomial( (self._psi(r), one) ) + zero = self._psi(zero) + for i in range(self._n): + temp = list(zero) # Make a copy + temp[i] = 1 + d['w%s'%i] = self.monomial( (zero, tuple(temp)) ) + return Family(sorted(d), lambda i: d[i]) + + @cached_method + def gens(self): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3) + sage: Cl.gens() + (psi0, psi1, psi2, psid0, psid1, psid2, w0, w1, w2) + """ + return tuple(self.algebra_generators()) + + @cached_method + def one_basis(self): + r""" + Return the index of the basis element of `1`. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3) + sage: Cl.one_basis() + ((0, 0, 0), (0, 0, 0)) + """ + return (self._psi([0]*self._n), (0,)*self._n) + + @cached_method + def product_on_basis(self, m1, m2): + r""" + Return the product of the basis elements indexed by ``m1`` and ``m2``. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3) + sage: Cl.inject_variables() + Defining psi0, psi1, psi2, psid0, psid1, psid2, w0, w1, w2 + sage: psi0^2 # indirect doctest + 0 + sage: psid0^2 + 0 + sage: w0 * psi0 + q*psi0*w0 + sage: w0 * psid0 + 1/q*psid0*w0 + sage: w2 * w0 + w0*w2 + sage: w0^4 + -1/q^2 + ((q^2+1)/q^2)*w0^2 + """ + p1, w1 = m1 + p2, w2 = m2 + # Check for \psi_i^2 == 0 and for the dagger version + if any(p1[i] != 0 and p1[i] == p2[i] for i in range(self._n)): + return self.zero() + + # \psi_i is represented by a 1 in p1[i] and p2[i] + # \psi_i^{\dagger} is represented by a -1 in p1[i] and p2[i] + + k = self._k + q_power = 0 + sign = 1 + pairings = [] + supported = [] + p = [0] * self._n + # Move w1 * p2 to q^q_power * p2 * w1 + # Also find pairs \psi_i \psi_i^{\dagger} (or vice versa) + for i in range(self._n): + if p2[i] != 0: + supported.append(i) + q_power += w1[i] * p2[i] + if p1[i] != 0: + # We make pairings 1-based because we cannot distinguish 0 and -0 + pairings.append((i+1) * p1[i]) + # we know p1[i] != p2[i] if non-zero, so their sum is -1, 0, 1 + p[i] = p1[i] + p2[i] + + supported.append(self._n-1) # To get betweent the last support and the end + # Get the sign of moving \psi_i and \psi_i^{\dagger} into position + for i in reversed(range(1, len(supported))): + if i % 2 != 0: + for j in reversed(range(supported[i-1]+1, supported[i]+1)): + if p1[j] != 0: + sign = (-1)**i * sign + + # We move the pairs \psi_i \psi_i^{\dagger} (or the reverse) to the + # end of the \psi part. This does not change the sign because they + # move in pairs. + # We take the products. + vp = self._w_poly.gens() + poly = self._w_poly.one() + q = self._q + for i in pairings: + if i < 0: + i = -i - 1 # Go back to 0-based + vpik = -q**(2*k) * vp[i]**(3*k) + (1 + q**(2*k)) * vp[i]**k + poly *= -(vp[i]**k - vpik) / (q**k - q**(-k)) + else: + i -= 1 # Go back to 0-based + vpik = -q**(2*k) * vp[i]**(3*k) + (1 + q**(2*k)) * vp[i]**k + poly *= (q**k * vp[i]**k - q**(-k) * vpik) / (q**k - q**(-k)) + + I = self._indices + v = list(w1) + for i in range(self._n): + v[i] += w2[i] + + # For all \psi_i v_i^k, convert this to either k = 0, 1 + # and same for \psi_i^{\dagger} + for i in range(self._n): + if p[i] > 0 and v[i] != 0: + q_power -= 2 * k * (v[i] // (2*k)) + v[i] = v[i] % (2*k) + if p[i] < 0 and v[i] != 0: + v[i] = v[i] % (2*k) + + poly *= self._w_poly.monomial(*v) + poly = poly.reduce([vp[i]**(4*k) - (1 + q**(-2*k)) * vp[i]**(2*k) + q**(-2*k) + for i in range(self._n)]) + pdict = poly.dict() + ret = {(self._psi(p), tuple(e)): pdict[e] * q**q_power * sign + for e in pdict} + + return self._from_dict(ret) + + class Element(CombinatorialFreeModule.Element): + def inverse(self): + r""" + Return the inverse if ``self`` is a basis element. + + EXAMPLES:: + + sage: Cl = algebras.QuantumClifford(3) + sage: Cl.inject_variables() + Defining psi0, psi1, psi2, psid0, psid1, psid2, w0, w1, w2 + sage: w0^-1 + (q^2+1)*w0 - q^2*w0^3 + sage: w0^-1 * w0 + 1 + sage: w0^-2 + (q^2+1) - q^2*w0^2 + sage: w0^-2 * w0^2 + 1 + sage: w0^-2 * w0 == w0^-1 + True + sage: w = w0 * w1 + sage: w^-1 + (q^4+2*q^2+1)*w0*w1 + (-q^4-q^2)*w0*w1^3 + + (-q^4-q^2)*w0^3*w1 + q^4*w0^3*w1^3 + sage: w^-1 * w + 1 + sage: w * w^-1 + 1 + + sage: (w0 + w1)^-1 + Traceback (most recent call last): + ... + NotImplementedError: inverse only implemented for basis elements + sage: (psi0 * w0)^-1 + Traceback (most recent call last): + ... + NotImplementedError: inverse only implemented for product of w generators + + sage: Cl = algebras.QuantumClifford(2, 2) + sage: Cl.inject_variables() + Defining psi0, psi1, psid0, psid1, w0, w1 + sage: w0^-1 + (q^4+1)*w0^3 - q^4*w0^7 + sage: w0 * w0^-1 + 1 + """ + if not self: + raise ZeroDivisionError + if len(self) != 1: + raise NotImplementedError("inverse only implemented for basis elements") + Cl = self.parent() + p, w = self.support_of_term() + if any(p[i] != 0 for i in range(Cl._n)): + raise NotImplementedError("inverse only implemented for" + " product of w generators") + c = ~self.coefficients()[0] + poly = Cl._w_poly.monomial(*w) + wp = Cl._w_poly.gens() + q = Cl._q + k = Cl._k + poly = poly.subs({wi: -q**(2*k) * wi**(4*k-1) + (1 + q**(2*k)) * wi**(2*k-1) + for wi in wp}) + poly = poly.reduce([wi**(4*k) - (1 + q**(-2*k)) * wi**(2*k) + q**(-2*k) + for wi in wp]) + pdict = poly.dict() + ret = {(p, tuple(e)): c for e, c in pdict.items()} + return Cl._from_dict(ret) + + __invert__ = inverse + From 8c2cce8eb5aef51c981ef0bf116c3193603ab712 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 30 Jun 2021 21:18:43 -0700 Subject: [PATCH 120/336] ConditionSet: Complete basic implementation --- src/sage/sets/condition_set.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index c86352f8f16..2fdebec2036 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -2,15 +2,16 @@ Subsets of a Universe Defined by a Predicate """ -from sage.structure.parent import Parent +from sage.structure.parent import Parent, Set_generic from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets from sage.symbolic.callable import is_CallableSymbolicExpression from sage.symbolic.ring import SymbolicRing -from .set import Set +from .set import Set, Set_base, Set_boolean_operators, Set_add_sub_operators -class ConditionSet(Parent, UniqueRepresentation): +class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_operators, + UniqueRepresentation): r""" Set of elements of a universe that satisfy a predicate @@ -39,10 +40,16 @@ class ConditionSet(Parent, UniqueRepresentation): (x, y, z) |--> sqrt(x^2 + y^2 + z^2) < 1.20000000000000 sage: P_inter_B_again = ConditionSet(P, predicate); P_inter_B_again { (x, y, z) ∈ P : sqrt(x^2 + y^2 + z^2) < 1.20000000000000 } - sage: vector([1, 0, 0]) in P_inter_B + sage: vector([1, 0, 0]) in P_inter_B_again True - sage: vector([1, 1, 1]) in P_inter_B + sage: vector([1, 1, 1]) in P_inter_B_again False + + TESTS:: + + sage: TestSuite(Evens).run() + sage: TestSuite(P_inter_B).run(skip='_test_pickling') # cannot pickle lambdas + sage: TestSuite(P_inter_B_again).run() """ @staticmethod def __classcall_private__(cls, universe, predicate, category=None): @@ -80,10 +87,19 @@ def _element_constructor_(self, *args, **kwds): raise ValueError(f'{element} is not an element of the universe') else: element = universe_element_constructor(*args, **kwds) - if not self._predicate(element): + if not self._call_predicate(element): raise ValueError(f'{element} does not satisfy the condition') return element + def _call_predicate(self, element): + return self._predicate(element) + + def _an_element_(self): + for element in self._universe.some_elements(): + if element in self: + return element + raise NotImplementedError + def ambient(self): return self._universe @@ -96,6 +112,11 @@ def _repr_(self): predicate_expr = SymbolicRing._repr_element_(predicate.parent(), predicate) return '{ ' + f'{args} ∈ {universe} : {predicate_expr}' + ' }' + def _call_predicate(self, element): + if len(self._predicate.arguments()) != 1: + return self._predicate(*element) + return self._predicate(element) + def _sympy_(self): r""" EXAMPLES:: From 126773766f68934ef1f7f99836c837ca2284a63e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 30 Jun 2021 22:08:31 -0700 Subject: [PATCH 121/336] ConditionSet: Add example --- src/sage/sets/condition_set.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index 2fdebec2036..daeae3bc756 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -26,6 +26,17 @@ class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_ope sage: 2.0 in Evens True + sage: Odds = ConditionSet(ZZ, is_odd); Odds + { x ∈ Integer Ring : (x) } + sage: EvensAndOdds = Evens | Odds; EvensAndOdds + Set-theoretic union of + { x ∈ Integer Ring : (x) } and + { x ∈ Integer Ring : (x) } + sage: 5 in EvensAndOdds + True + sage: 7/2 in EvensAndOdds + False + sage: P = polytopes.cube(); P A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices sage: P.rename("P") From 44f72930283a95afb6aa860fa905f57d28e4d513 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Jul 2021 12:14:03 -0700 Subject: [PATCH 122/336] ConditionSet: Accept several predicates --- src/sage/sets/condition_set.py | 147 +++++++++++++++++++++++++-------- 1 file changed, 113 insertions(+), 34 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index daeae3bc756..4fa9240eb41 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -1,19 +1,22 @@ r""" -Subsets of a Universe Defined by a Predicate +Subsets of a Universe Defined by Predicates """ +from sage.structure.category_object import normalize_names from sage.structure.parent import Parent, Set_generic from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets +from sage.misc.cachefunc import cached_method +from sage.symbolic.expression import is_Expression from sage.symbolic.callable import is_CallableSymbolicExpression -from sage.symbolic.ring import SymbolicRing +from sage.symbolic.ring import SymbolicRing, SR, is_SymbolicVariable from .set import Set, Set_base, Set_boolean_operators, Set_add_sub_operators class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_operators, UniqueRepresentation): r""" - Set of elements of a universe that satisfy a predicate + Set of elements of a universe that satisfy given predicates EXAMPLES:: @@ -27,16 +30,21 @@ class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_ope True sage: Odds = ConditionSet(ZZ, is_odd); Odds - { x ∈ Integer Ring : (x) } + { x ∈ Integer Ring : (x) } sage: EvensAndOdds = Evens | Odds; EvensAndOdds Set-theoretic union of - { x ∈ Integer Ring : (x) } and - { x ∈ Integer Ring : (x) } + { x ∈ Integer Ring : (x) } and + { x ∈ Integer Ring : (x) } sage: 5 in EvensAndOdds True sage: 7/2 in EvensAndOdds False + sage: var('y') + y + sage: SmallOdds = ConditionSet(ZZ, is_odd, abs(y) <= 11, vars=[y]); SmallOdds + { y ∈ Integer Ring : abs(y) <= 11, (y) } + sage: P = polytopes.cube(); P A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices sage: P.rename("P") @@ -63,29 +71,98 @@ class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_ope sage: TestSuite(P_inter_B_again).run() """ @staticmethod - def __classcall_private__(cls, universe, predicate, category=None): + def __classcall_private__(cls, universe, *predicates, vars=None, names=None, category=None): if category is None: category = Sets() if isinstance(universe, Parent): if universe in Sets().Finite(): category = category & Sets().Finite() - if is_CallableSymbolicExpression(predicate): - return ConditionSet_callable_symbolic_expression(universe, predicate, category) - return super().__classcall__(cls, universe, predicate, category) - def __init__(self, universe, predicate, category): + if vars is not None: + if names is not None: + raise ValueError('cannot use names and vars at the same time; they are aliases') + names, vars = vars, None + + if names is not None: + names = normalize_names(-1, names) + + callable_symbolic_predicates = [] + other_predicates = [] + + for predicate in predicates: + if is_CallableSymbolicExpression(predicate): + if names is None: + names = tuple(str(var) for var in predicate.args()) + elif len(names) != predicates.args(): + raise TypeError('mismatch in number of arguments') + callable_symbolic_predicates.append(predicate) + elif is_Expression(predicate): + if names is None: + raise TypeError('use callable symbolic expressions or provide variable names') + if vars is None: + vars = tuple(SR.var(name) for name in names) + callable_symbolic_predicates.append(predicate.function(*vars)) + else: + other_predicates.append(predicate) + + predicates = callable_symbolic_predicates + other_predicates + + if not other_predicates: + if not callable_symbolic_predicates: + if names is None and category is None: + # No conditions, no variable names, no category, just use Set. + return Set(universe) + # Use ConditionSet_callable_symbolic_expression even if no conditions + # are present; this will make the _sympy_ method available. + return ConditionSet_callable_symbolic_expression(universe, *predicates, + names=names, category=category) + + if names is None: + names = ("x",) + return super().__classcall__(cls, universe, *predicates, + names=names, category=category) + + def __init__(self, universe, *predicates, names=None, category=None): self._universe = universe - self._predicate = predicate + self._predicates = predicates facade = None if isinstance(universe, Parent): facade = universe - super().__init__(facade=facade, category=category) + super().__init__(facade=facade, category=category, + names=names, normalize=False) # names already normalized by classcall def _repr_(self): + s = "{ " + names = self.variable_names() + comma_sep_names = ", ".join(str(name) for name in names) + if len(names) == 1: + s += f"{comma_sep_names}" + else: + s += f"({comma_sep_names})" universe = self._universe - predicate = self._predicate - var = 'x' - return '{ ' + f'{var} ∈ {universe} : {predicate}({var})' + ' }' + s += f" ∈ {universe}" + sep = " : " + for predicate in self._predicates: + s += sep + self._repr_condition(predicate) + sep = ", " + s += " }" + return s + + @cached_method + def _repr_condition(self, predicate): + if is_CallableSymbolicExpression(predicate): + args = self.arguments() + if len(args) == 1: + args = args[0] + condition = self._call_predicate(predicate, args) + return str(condition) + comma_sep_names = ", ".join(str(name) + for name in self.variable_names()) + return f"{predicate}({comma_sep_names})" + + @cached_method + def arguments(self): + return SR.var(self.variable_names()) def _element_constructor_(self, *args, **kwds): try: @@ -98,12 +175,16 @@ def _element_constructor_(self, *args, **kwds): raise ValueError(f'{element} is not an element of the universe') else: element = universe_element_constructor(*args, **kwds) - if not self._call_predicate(element): + if not all(self._call_predicate(predicate, element) + for predicate in self._predicates): raise ValueError(f'{element} does not satisfy the condition') return element - def _call_predicate(self, element): - return self._predicate(element) + def _call_predicate(self, predicate, element): + if is_CallableSymbolicExpression(predicate): + if len(predicate.arguments()) != 1: + return predicate(*element) + return predicate(element) def _an_element_(self): for element in self._universe.some_elements(): @@ -114,20 +195,10 @@ def _an_element_(self): def ambient(self): return self._universe -class ConditionSet_callable_symbolic_expression(ConditionSet): - def _repr_(self): - universe = self._universe - predicate = self._predicate - args = predicate.arguments() - predicate_expr = SymbolicRing._repr_element_(predicate.parent(), predicate) - return '{ ' + f'{args} ∈ {universe} : {predicate_expr}' + ' }' - - def _call_predicate(self, element): - if len(self._predicate.arguments()) != 1: - return self._predicate(*element) - return self._predicate(element) +class ConditionSet_callable_symbolic_expression(ConditionSet): + @cached_method def _sympy_(self): r""" EXAMPLES:: @@ -144,14 +215,22 @@ def _sympy_(self): True sage: (5, 7, 9) in ST False + + sage: Interval = ConditionSet(RR, x >= -7, x <= 4, vars=[x]); Interval + { x ∈ Real Field with 53 bits of precision : x >= -7, x <= 4 } + sage: Interval._sympy_() + ConditionSet(x, (x >= -7) & (x <= 4), SageSet(Real Field with 53 bits of precision)) """ import sympy - predicate = self._predicate - args = predicate.arguments() + args = self.arguments() if len(args) == 1: + args = args[0] sym = args._sympy_() else: sym = tuple(x._sympy_() for x in args) + conditions = [self._call_predicate(predicate, args) + for predicate in self._predicates] return sympy.ConditionSet(sym, - predicate._sympy_(), + sympy.And(*[condition._sympy_() + for condition in conditions]), base_set=self._universe._sympy_()) From 4e604eccb9e12b20b9b6120a30a8bfc1cbb9ebac Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Jul 2021 14:58:13 -0700 Subject: [PATCH 123/336] RealSet.interval: New --- src/sage/sets/real_set.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 69910f7e832..f4f3c069847 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1325,6 +1325,33 @@ def _prep(lower, upper=None): else: return lower, upper + @staticmethod + def interval(lower, upper, *, lower_closed=None, upper_closed=None): + """ + Construct an interval + + INPUT: + + - ``lower``, ``upper`` -- two real numbers or infinity. They + will be sorted if necessary. + + - ``lower_closed``, ``upper_closed`` -- boolean; whether the interval + is closed at the lower and upper bound of the interval, respectively. + + OUTPUT: + + A new :class:`RealSet`. + + EXAMPLES:: + + sage: RealSet.interval(1, 0, lower_closed=True, upper_closed=False) + [0, 1) + """ + if lower_closed is None or upper_closed is None: + raise ValueError('lower_closed and upper_closed must be explicitly given') + lower, upper = RealSet._prep(lower, upper) + return RealSet(InternalRealInterval(lower, lower_closed, upper, upper_closed)) + @staticmethod def open(lower, upper): """ From 48ac8e90570e067b068d9b6a8cf55703991ac572 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Jul 2021 14:59:12 -0700 Subject: [PATCH 124/336] Chart._check_restrictions: Simplify code by using any/all --- src/sage/manifolds/chart.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 2eea57e30de..988ace7e8b1 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -840,17 +840,11 @@ def _check_restrictions(self, restrict, substitutions): """ if isinstance(restrict, tuple): # case of 'or' conditions - combine = False - for cond in restrict: - combine = combine or self._check_restrictions(cond, - substitutions) - return combine + return any(self._check_restrictions(cond, substitutions) + for cond in restrict) elif isinstance(restrict, list): # case of 'and' conditions - combine = True - for cond in restrict: - combine = combine and self._check_restrictions(cond, - substitutions) - return combine + return all(self._check_restrictions(cond, substitutions) + for cond in restrict) # Case of a single condition: return bool(restrict.subs(substitutions)) From 720bed24066b1adf00935238edf14ab88c8d4e45 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Jul 2021 14:59:43 -0700 Subject: [PATCH 125/336] {Chart,RealChart}.codomain: New --- src/sage/manifolds/chart.py | 103 ++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 988ace7e8b1..3139fcb4e2d 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -848,6 +848,66 @@ def _check_restrictions(self, restrict, substitutions): # Case of a single condition: return bool(restrict.subs(substitutions)) + def codomain(self): + r""" + Return the codomain of ``self`` as a set. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: X.codomain() + Vector space of dimension 2 over Complex Field with 53 bits of precision + + """ + from sage.modules.free_module import VectorSpace + ambient = VectorSpace(self.manifold().base_field(), self.manifold().dimension()) + if self._restrictions: + return self._restrict_set(ambient, self._restrictions) + else: + return ambient + + def _restrict_set(self, universe, restrict): + """ + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: universe = RR^2 + sage: X._restrict_set(universe, x>0) + { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : x > 0 } + sage: X._restrict_set(universe, x>0) + { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : x > 0 } + sage: X._restrict_set(universe, (x>0, [x 0 } and + Set-theoretic intersection of + { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : x < y } and + { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : y < 0 } + sage: X._restrict_set(universe, [(x0]) + Set-theoretic intersection of + Set-theoretic union of + { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : x < y } and + { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : y < 0 } and + { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : x > 0 } + """ + if isinstance(restrict, tuple): # case of 'or' conditions + A = self._restrict_set(universe, restrict[0]) + if len(restrict) == 1: + return A + else: + return A.union(self._restrict_set(universe, restrict[1:])) + elif isinstance(restrict, list): # case of 'and' conditions + A = self._restrict_set(universe, restrict[0]) + if len(restrict) == 1: + return A + else: + return A.intersection(self._restrict_set(universe, restrict[1:])) + # Case of a single condition: + from sage.sets.condition_set import ConditionSet + return ConditionSet(universe, restrict, vars=self._xx) + def transition_map(self, other, transformations, intersection_name=None, restrictions1=None, restrictions2=None): r""" @@ -1752,6 +1812,49 @@ def coord_bounds(self, i=None): else: return self._bounds[i-self._sindex] + def codomain(self): + """ + Return the codomain of ``self`` as a set. + + EXAMPLES:: + + sage: M = Manifold(2, 'R^2', structure='topological') + sage: U = M.open_subset('U') # the complement of the half line {y=0, x >= 0} + sage: c_spher. = U.chart(r'r:(0,+oo) phi:(0,2*pi):\phi') + sage: c_spher.codomain() + The Cartesian product of ((0, +oo), (0, 2*pi)) + + sage: M = Manifold(3, 'R^3', r'\RR^3', structure='topological', start_index=1) + sage: c_cart. = M.chart() + sage: c_cart.codomain() + Vector space of dimension 3 over Real Field with 53 bits of precision + + In the current implementation, the codomain of periodic coordinates are represented + by a fundamental domain:: + + sage: V = M.open_subset('V') + sage: c_spher1. = \ + ....: V.chart(r'r:(0,+oo) th:(0,pi):\theta ph1:(0,2*pi):periodic:\phi_1') + sage: c_spher1.codomain() + The Cartesian product of ((0, +oo), (0, pi), [0, 2*pi)) + """ + from sage.sets.real_set import RealSet + from sage.modules.free_module import VectorSpace + from sage.categories.cartesian_product import cartesian_product + intervals = tuple(RealSet.interval(xmin, xmax, + lower_closed=(min_included == 'periodic' or min_included), + upper_closed=(max_included != 'periodic' and max_included)) + for ((xmin, min_included), (xmax, max_included)) in self._bounds) + if all(interval.is_universe() + for interval in intervals): + ambient = VectorSpace(self.manifold().base_field(), self.manifold().dimension()) + else: + ambient = cartesian_product(intervals) + if self._restrictions: + return self._restrict_set(ambient, self._restrictions) + else: + return ambient + def coord_range(self, xx=None): r""" Display the range of a coordinate (or all coordinates), as an From b31b2c7a3116336b6cc9e2cd0744b7ddec4ec0ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Jul 2021 15:43:31 -0700 Subject: [PATCH 126/336] ConditionSet: Support generator notation, add examples --- src/sage/sets/condition_set.py | 48 ++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index 4fa9240eb41..5574253b181 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -43,7 +43,7 @@ class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_ope sage: var('y') y sage: SmallOdds = ConditionSet(ZZ, is_odd, abs(y) <= 11, vars=[y]); SmallOdds - { y ∈ Integer Ring : abs(y) <= 11, (y) } + { y ∈ Integer Ring : abs(y) <= 11, (y) } sage: P = polytopes.cube(); P A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices @@ -64,9 +64,25 @@ class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_ope sage: vector([1, 1, 1]) in P_inter_B_again False + Using ``ConditionSet`` without predicates provides a way of attaching variable names + to a set:: + + sage: Z3 = ConditionSet(ZZ^3, vars=['x', 'y', 'z']); Z3 + { (x, y, z) ∈ Ambient free module of rank 3 over the principal ideal domain Integer Ring } + sage: Z3.variable_names() + ('x', 'y', 'z') + sage: Z3.arguments() + (x, y, z) + + sage: Q4. = ConditionSet(QQ^4); Q4 + { (a, b, c, d) ∈ Vector space of dimension 4 over Rational Field } + sage: Q4.variable_names() + ('a', 'b', 'c', 'd') + sage: Q4.arguments() + (a, b, c, d) + TESTS:: - sage: TestSuite(Evens).run() sage: TestSuite(P_inter_B).run(skip='_test_pickling') # cannot pickle lambdas sage: TestSuite(P_inter_B_again).run() """ @@ -123,6 +139,13 @@ def __classcall_private__(cls, universe, *predicates, vars=None, names=None, cat names=names, category=category) def __init__(self, universe, *predicates, names=None, category=None): + """ + TESTS:: + + sage: Evens = ConditionSet(ZZ, is_even); Evens + { x ∈ Integer Ring : (x) } + sage: TestSuite(Evens).run() + """ self._universe = universe self._predicates = predicates facade = None @@ -131,6 +154,18 @@ def __init__(self, universe, *predicates, names=None, category=None): super().__init__(facade=facade, category=category, names=names, normalize=False) # names already normalized by classcall + def _first_ngens(self, n): + r""" + Return the list of variables. + + This is useful only for the use of Sage preparser:: + + sage: preparse("Q3. = ConditionSet(QQ^3)") + "Q3 = ConditionSet(QQ**Integer(3), names=('x', 'y', 'z',)); (x, y, z,) = Q3._first_ngens(3)" + + """ + return self.arguments() + def _repr_(self): s = "{ " names = self.variable_names() @@ -165,6 +200,12 @@ def arguments(self): return SR.var(self.variable_names()) def _element_constructor_(self, *args, **kwds): + """ + Construct an element of the set. + + This element constructor raises an error if the element does not + satisfy the predicates. + """ try: universe_element_constructor = self._universe._element_constructor_ except AttributeError: @@ -193,6 +234,9 @@ def _an_element_(self): raise NotImplementedError def ambient(self): + """ + Return the universe of ``self``. + """ return self._universe From eb975f37a99b8390a1fc7f042f47982d5a6704f5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Jul 2021 18:09:38 -0700 Subject: [PATCH 127/336] ConditionSet: Remove unnecessary subclass, add examples --- src/sage/sets/condition_set.py | 167 ++++++++++++++++++++++++++++++--- 1 file changed, 153 insertions(+), 14 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index 5574253b181..521f1c76c8c 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -2,6 +2,16 @@ Subsets of a Universe Defined by Predicates """ +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + from sage.structure.category_object import normalize_names from sage.structure.parent import Parent, Set_generic from sage.structure.unique_representation import UniqueRepresentation @@ -18,6 +28,18 @@ class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_ope r""" Set of elements of a universe that satisfy given predicates + INPUT: + + - ``universe`` -- a set + + - ``*predicates`` -- callables + + - ``vars`` or ``names`` -- (default: inferred from ``predicates`` if any predicate is + an element of a :class:`~sage.symbolic.callable.CallableSymbolicExpressionRing_class`) + variables or names of variables + + - ``category`` -- (default: inferred from ``universe``) a category + EXAMPLES:: sage: Evens = ConditionSet(ZZ, is_even); Evens @@ -88,6 +110,16 @@ class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_ope """ @staticmethod def __classcall_private__(cls, universe, *predicates, vars=None, names=None, category=None): + r""" + Normalize init arguments. + + TESTS:: + + sage: ConditionSet(ZZ, names=["x"]) is ConditionSet(ZZ, names=x) + True + sage: ConditionSet(RR, x > 0, names=x) is ConditionSet(RR, (x > 0).function(x)) + True + """ if category is None: category = Sets() if isinstance(universe, Parent): @@ -111,6 +143,8 @@ def __classcall_private__(cls, universe, *predicates, vars=None, names=None, cat names = tuple(str(var) for var in predicate.args()) elif len(names) != predicates.args(): raise TypeError('mismatch in number of arguments') + if vars is None: + vars = predicate.args() callable_symbolic_predicates.append(predicate) elif is_Expression(predicate): if names is None: @@ -123,15 +157,15 @@ def __classcall_private__(cls, universe, *predicates, vars=None, names=None, cat predicates = callable_symbolic_predicates + other_predicates - if not other_predicates: - if not callable_symbolic_predicates: - if names is None and category is None: - # No conditions, no variable names, no category, just use Set. - return Set(universe) - # Use ConditionSet_callable_symbolic_expression even if no conditions - # are present; this will make the _sympy_ method available. - return ConditionSet_callable_symbolic_expression(universe, *predicates, - names=names, category=category) + if not other_predicates and not callable_symbolic_predicates: + if names is None and category is None: + # No conditions, no variable names, no category, just use Set. + return Set(universe) + + if any(predicate.args() != vars + for predicate in callable_symbolic_predicates): + # TODO: Implement safe renaming of the arguments of a callable symbolic expressions + raise NotImplementedError('all callable symbolic expressions must use the same arguments') if names is None: names = ("x",) @@ -139,7 +173,7 @@ def __classcall_private__(cls, universe, *predicates, vars=None, names=None, cat names=names, category=category) def __init__(self, universe, *predicates, names=None, category=None): - """ + r""" TESTS:: sage: Evens = ConditionSet(ZZ, is_even); Evens @@ -167,6 +201,17 @@ def _first_ngens(self, n): return self.arguments() def _repr_(self): + """ + Print representation of this set. + + EXAMPLES:: + + sage: var('t') # parameter + t + sage: ZeroDimButNotNullary = ConditionSet(ZZ^0, t > 0, vars=("q")) + sage: ZeroDimButNotNullary._repr_() + '{ q ∈ Ambient free module of rank 0 over the principal ideal domain Integer Ring : t > 0 }' + """ s = "{ " names = self.variable_names() comma_sep_names = ", ".join(str(name) for name in names) @@ -185,6 +230,23 @@ def _repr_(self): @cached_method def _repr_condition(self, predicate): + """ + Format the predicate, applied to the arguments. + + EXAMPLES:: + + sage: Evens = ConditionSet(ZZ, is_even) + sage: Evens._repr_condition(is_even) + '(x)' + sage: BigSin = ConditionSet(RR, sin(x) > 0.9, vars=[x]) + sage: BigSin._repr_condition(BigSin._predicates[0]) + 'sin(x) > 0.900000000000000' + sage: var('t') # parameter + t + sage: ZeroDimButNotNullary = ConditionSet(ZZ^0, t > 0, vars=("q")) + sage: ZeroDimButNotNullary._repr_condition(ZeroDimButNotNullary._predicates[0]) + 't > 0' + """ if is_CallableSymbolicExpression(predicate): args = self.arguments() if len(args) == 1: @@ -197,6 +259,18 @@ def _repr_condition(self, predicate): @cached_method def arguments(self): + """ + Return the variables of ``self`` as elements of the symbolic ring. + + EXAMPLES:: + + sage: Odds = ConditionSet(ZZ, is_odd); Odds + { x ∈ Integer Ring : (x) } + sage: args = Odds.arguments(); args + (x,) + sage: args[0].parent() + Symbolic Ring + """ return SR.var(self.variable_names()) def _element_constructor_(self, *args, **kwds): @@ -205,6 +279,24 @@ def _element_constructor_(self, *args, **kwds): This element constructor raises an error if the element does not satisfy the predicates. + + EXAMPLES:: + + sage: Evens = ConditionSet(ZZ, is_even); Evens + { x ∈ Integer Ring : (x) } + sage: element_two = Evens(2r); element_two + 2 + sage: element_two.parent() + Integer Ring + sage: element_too = Evens(2.0); element_too + 2 + sage: element_too.parent() + Integer Ring + sage: Evens(3) + Traceback (most recent call last): + ... + ValueError: 3 does not satisfy the condition + """ try: universe_element_constructor = self._universe._element_constructor_ @@ -222,29 +314,76 @@ def _element_constructor_(self, *args, **kwds): return element def _call_predicate(self, predicate, element): + r""" + Call ``predicate`` on an ``element`` of the universe of ``self``. + + TESTS:: + + sage: TripleDigits = ZZ^3 + sage: predicate(x, y, z) = sqrt(x^2 + y^2 + z^2) < 12; predicate + (x, y, z) |--> sqrt(x^2 + y^2 + z^2) < 12 + sage: TripleDigits.rename('ZZ^3') + sage: SmallTriples = ConditionSet(ZZ^3, predicate); SmallTriples + { (x, y, z) ∈ ZZ^3 : sqrt(x^2 + y^2 + z^2) < 12 } + sage: predicate = SmallTriples._predicates[0] + sage: element = TripleDigits((1, 2, 3)) + sage: SmallTriples._call_predicate(predicate, element) + sqrt(14) < 12 + + sage: var('t') + t + sage: TinyUniverse = ZZ^0 + sage: Nullary = ConditionSet(TinyUniverse, t > 0, vars=()) + sage: predicate = Nullary._predicates[0] + sage: element = TinyUniverse(0) + sage: Nullary._call_predicate(predicate, element) + t > 0 + """ if is_CallableSymbolicExpression(predicate): if len(predicate.arguments()) != 1: return predicate(*element) return predicate(element) def _an_element_(self): + r""" + Return an element of ``self``. + + This may raise ``NotImplementedError``. + + TESTS:: + + sage: TripleDigits = ZZ^3 + sage: predicate(x, y, z) = sqrt(x^2 + y^2 + z^2) < 12; predicate + (x, y, z) |--> sqrt(x^2 + y^2 + z^2) < 12 + sage: TripleDigits.rename('ZZ^3') + sage: SmallTriples = ConditionSet(ZZ^3, predicate); SmallTriples + { (x, y, z) ∈ ZZ^3 : sqrt(x^2 + y^2 + z^2) < 12 } + sage: SmallTriples.an_element() # indirect doctest + (1, 0, 0) + """ for element in self._universe.some_elements(): if element in self: return element raise NotImplementedError def ambient(self): - """ + r""" Return the universe of ``self``. - """ - return self._universe + EXAMPLES:: -class ConditionSet_callable_symbolic_expression(ConditionSet): + sage: Evens = ConditionSet(ZZ, is_even); Evens + { x ∈ Integer Ring : (x) } + sage: Evens.ambient() + Integer Ring + """ + return self._universe @cached_method def _sympy_(self): r""" + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + EXAMPLES:: sage: predicate(x, y, z) = sqrt(x^2 + y^2 + z^2) < 12; predicate From d6162a667e4ed99d5c82649481fd8ffec8d36e82 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Jul 2021 19:02:21 -0700 Subject: [PATCH 128/336] sage.interfaces.sympy_wrapper.SageSet.__str__, __repr__: New --- src/sage/interfaces/sympy_wrapper.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py index 00f7b65dd27..827ee59ee85 100644 --- a/src/sage/interfaces/sympy_wrapper.py +++ b/src/sage/interfaces/sympy_wrapper.py @@ -173,3 +173,21 @@ def __len__(self): 48 """ return len(self._sage_()) + + def __str__(self): + """ + Return the print representation of ``self``. + + EXAMPLES:: + + sage: sPrimes = Primes()._sympy_() + sage: str(sPrimes) # indirect doctest + 'SageSet(Set of all prime numbers: 2, 3, 5, 7, ...)' + sage: repr(sPrimes) + 'SageSet(Set of all prime numbers: 2, 3, 5, 7, ...)' + """ + # Provide this method so that sympy's printing code does not try to inspect + # the Sage object. + return f"SageSet({self._sage_()})" + + __repr__ = __str__ From 468f621faec4ae084e70e3a4ae6fbe16275fe653 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Jul 2021 19:02:55 -0700 Subject: [PATCH 129/336] ConditionSet._sympy_: Fall back to creating a wrapper if necessary --- src/sage/sets/condition_set.py | 39 +++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index 521f1c76c8c..96d2ae182ea 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -403,17 +403,36 @@ def _sympy_(self): { x ∈ Real Field with 53 bits of precision : x >= -7, x <= 4 } sage: Interval._sympy_() ConditionSet(x, (x >= -7) & (x <= 4), SageSet(Real Field with 53 bits of precision)) + + If a predicate is not symbolic, we fall back to creating a wrapper:: + + sage: Evens = ConditionSet(ZZ, is_even); Evens + { x ∈ Integer Ring : (x) } + sage: Evens._sympy_() + SageSet({ x ∈ Integer Ring : (x) }) """ + from sage.interfaces.sympy import sympy_init + sympy_init() import sympy + args = self.arguments() - if len(args) == 1: + single_arg = len(args) == 1 + if single_arg: args = args[0] - sym = args._sympy_() - else: - sym = tuple(x._sympy_() for x in args) - conditions = [self._call_predicate(predicate, args) - for predicate in self._predicates] - return sympy.ConditionSet(sym, - sympy.And(*[condition._sympy_() - for condition in conditions]), - base_set=self._universe._sympy_()) + + try: + conditions = [self._call_predicate(predicate, args) + for predicate in self._predicates] + + sym = tuple(x._sympy_() for x in self.arguments()) + if single_arg: + sym = sym[0] + result = sympy.ConditionSet(sym, + sympy.And(*[condition._sympy_() + for condition in conditions]), + base_set=self._universe._sympy_()) + result._sage_object = self + return result + except TypeError: + # Fall back to creating a wrapper + return super()._sympy_() From 159d479ab93e3bdcbeb555e32f40fb6f54b14a8e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 2 Jul 2021 15:19:17 +1000 Subject: [PATCH 130/336] Fix pyflakes and a invalid escape sequence. --- src/sage/algebras/quantum_clifford.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/sage/algebras/quantum_clifford.py b/src/sage/algebras/quantum_clifford.py index d9a75695c13..88abe30b518 100644 --- a/src/sage/algebras/quantum_clifford.py +++ b/src/sage/algebras/quantum_clifford.py @@ -23,8 +23,6 @@ from sage.categories.fields import Fields from sage.combinat.free_module import CombinatorialFreeModule from sage.categories.cartesian_product import cartesian_product -from sage.modules.free_module import FreeModule -from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.sets.family import Family from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from itertools import product @@ -132,7 +130,7 @@ class QuantumCliffordAlgebra(CombinatorialFreeModule): """ @staticmethod def __classcall_private__(cls, n, k=1, q=None, F=None): - """ + r""" Standardize input to ensure a unique representation. TESTS:: @@ -263,9 +261,8 @@ def _latex_term(self, m): sage: latex(Cl(5)) 5 """ - from sage.misc.latex import latex p, v = m - rp = ''.join('\\psi_{%s}'%i if p[i] > 0 else '\\psi^{\dagger}_{%s}'%i + rp = ''.join('\\psi_{%s}'%i if p[i] > 0 else '\\psi^{\\dagger}_{%s}'%i for i in range(self._n) if p[i] != 0) gen_str = lambda e: '' if e == 1 else '^{%s}'%e rv = ''.join('\\omega_{%s}'%i + gen_str(v[i]) @@ -328,7 +325,7 @@ def dimension(self): sage: Cl.dimension() 65536 """ - return ZZ(8*self._k)**self._n + return ZZ(8*self._k) ** self._n @cached_method def algebra_generators(self): @@ -345,7 +342,6 @@ def algebra_generators(self): """ one = (0,) * self._n # one in the corresponding free abelian group zero = [0] * self._n - I = self._indices d = {} for i in range(self._n): r = list(zero) # Make a copy @@ -461,7 +457,6 @@ def product_on_basis(self, m1, m2): vpik = -q**(2*k) * vp[i]**(3*k) + (1 + q**(2*k)) * vp[i]**k poly *= (q**k * vp[i]**k - q**(-k) * vpik) / (q**k - q**(-k)) - I = self._indices v = list(w1) for i in range(self._n): v[i] += w2[i] @@ -539,7 +534,6 @@ def inverse(self): if any(p[i] != 0 for i in range(Cl._n)): raise NotImplementedError("inverse only implemented for" " product of w generators") - c = ~self.coefficients()[0] poly = Cl._w_poly.monomial(*w) wp = Cl._w_poly.gens() q = Cl._q From 54b325ed10094a7cc676b39973830e5cbf4b1b97 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 2 Jul 2021 09:31:41 +0200 Subject: [PATCH 131/336] sort groups of smooth characters by string for stable doctesting --- src/sage/modular/local_comp/local_comp.py | 32 +++++++++++------------ 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/sage/modular/local_comp/local_comp.py b/src/sage/modular/local_comp/local_comp.py index f0520621474..1143e41b138 100644 --- a/src/sage/modular/local_comp/local_comp.py +++ b/src/sage/modular/local_comp/local_comp.py @@ -74,9 +74,9 @@ def LocalComponent(f, p, twist_factor=None): Character of Q_7*, of level 0, mapping 7 |--> 1 sage: Pi.species() 'Supercuspidal' - sage: set(Pi.characters()) - {Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> -d, 7 |--> 1, - Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> d, 7 |--> 1} + sage: sorted(Pi.characters(), key=str) + [Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> -d, 7 |--> 1, + Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> d, 7 |--> 1] """ p = ZZ(p) if not p.is_prime(): @@ -634,9 +634,9 @@ def characters(self): sage: f = Newform('50a') sage: Pi = LocalComponent(f, 5) - sage: chars = Pi.characters(); set(chars) - {Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1, 5 |--> 1, - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 1} + sage: chars = Pi.characters(); sorted(chars, key=str) + [Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1, 5 |--> 1, + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 1] sage: chars[0].base_ring() Number Field in d with defining polynomial x^2 + x + 1 @@ -650,11 +650,9 @@ def characters(self): sage: f = Newforms(GammaH(25, [6]), 3, names='j')[0]; f q + j0*q^2 + 1/3*j0^3*q^3 - 1/3*j0^2*q^4 + O(q^6) sage: Pi = LocalComponent(f, 5) - sage: Pi.characters() - [ - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> 1/3*j0^2*d - 1/3*j0^3, 5 |--> 5, - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -1/3*j0^2*d, 5 |--> 5 - ] + sage: sorted(Pi.characters(), key=str) + [Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -1/3*j0^2*d, 5 |--> 5, + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> 1/3*j0^2*d - 1/3*j0^3, 5 |--> 5] sage: Pi.characters()[0].base_ring() Number Field in d with defining polynomial x^2 - j0*x + 1/3*j0^2 over its base field @@ -669,9 +667,9 @@ def characters(self): sage: f = Newform('81a', names='j'); f q + j0*q^2 + q^4 - j0*q^5 + O(q^6) - sage: set(LocalComponent(f, 3).characters()) # long time (12s on sage.math, 2012) - {Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -2*d + j0, 4 |--> 1, 3*s + 1 |--> -j0*d + 1, 3 |--> 1, - Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> 2*d - j0, 4 |--> 1, 3*s + 1 |--> j0*d - 2, 3 |--> 1} + sage: sorted(LocalComponent(f, 3).characters(), key=str) # long time (12s on sage.math, 2012) + [Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -2*d + j0, 4 |--> 1, 3*s + 1 |--> -j0*d + 1, 3 |--> 1, + Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> 2*d - j0, 4 |--> 1, 3*s + 1 |--> j0*d - 2, 3 |--> 1] Some ramified examples:: @@ -701,9 +699,9 @@ def characters(self): Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 3, mapping s |--> 1, 2*s + 1 |--> 1/2*a0, 4*s + 1 |--> 1, -1 |--> 1, 2 |--> 1, Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 3, mapping s |--> 1, 2*s + 1 |--> 1/2*a0, 4*s + 1 |--> -1, -1 |--> 1, 2 |--> 1 ] - sage: set(Newform('243a',names='a').local_component(3).characters()) # long time - {Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -2*s - 1 |--> -d - 1, 4 |--> 1, 3*s + 1 |--> -d - 1, s |--> 1, - Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -2*s - 1 |--> d, 4 |--> 1, 3*s + 1 |--> d, s |--> 1} + sage: sorted(Newform('243a',names='a').local_component(3).characters(), key=str) # long time + [Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -2*s - 1 |--> -d - 1, 4 |--> 1, 3*s + 1 |--> -d - 1, s |--> 1, + Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -2*s - 1 |--> d, 4 |--> 1, 3*s + 1 |--> d, s |--> 1] """ T = self.type_space() p = self.prime() From 2db5a070f620176d9ecb51ca9ca5b2e2ead4af60 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 28 Jun 2021 10:11:10 +0200 Subject: [PATCH 132/336] Trac 32098: interface with PARI modular symbols --- src/sage/modular/modsym/ambient.py | 148 +++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index 3bb80562667..d49fef546ef 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -72,6 +72,7 @@ class ``ModularSymbolsAmbient``, derived from # https://www.gnu.org/licenses/ ################################################################################ # Sage packages +from sage.misc.cachefunc import cached_method import sage.misc.latex as latex from sage.misc.verbose import verbose @@ -2414,6 +2415,125 @@ class of cuspidal newforms in this ambient space. lift=False, nz=nz))) return ans + def __pari__(self): + """ + Return a PARI object corresponding to ``self``. + + TESTS:: + + sage: ModularSymbols(Gamma1(5), 2).__pari__() + Traceback (most recent call last): + ... + NotImplementedError: PARI modular symbols are only implemented for Gamma0(n) + """ + raise NotImplementedError('PARI modular symbols are only implemented for Gamma0(n)') + + def _pari_pairing(self): + r""" + Return the matrix of the canonical pairing between this space and + the corresponding space of modular symbols in PARI. + + Let `M` be an ambient space of modular symbols over a field `K` + of characteristic 0. The corresponding space `P` in PARI is + canonically dual to `M`, giving rise to a `K`-bilinear map + + .. MATH:: + + E\colon M \times P \to K. + + OUTPUT: The matrix of the bilinear map `E`. + + This is currently only implemented for spaces of modular + symbols of trivial character. + + EXAMPLES:: + + sage: M = ModularSymbols(Gamma0(5), 6) + sage: P = M.__pari__() + sage: E = M._pari_pairing(); E + [ 0 -1 0 0] + [ 0 0 8 -27] + [ 8 0 0 13] + [ 24 0 8 37] + + The duality is compatible with (for example) Hecke operators + and the star involution:: + + sage: M.hecke_matrix(5) * E == E * P.mshecke(5) + True + sage: M.star_involution().matrix() * E == E * P.msstar() + True + """ + if self.weight() == 2: + return self._pari_tensor().inverse() + from sage.matrix.constructor import matrix + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + P = self.__pari__() + I = matrix.identity(self.rank()).__pari__() + m = Integer(self.weight() - 2) + R = PolynomialRing(QQ, 'x') + x = R.gen() + def ev(s): + # The Manin symbol s = X^i (c, d) corresponds to the + # modular symbol (dX - bY)^i (-cX + aY)^(m - i) {b/d, a/c}. + # The code below computes the canonical pairing of this + # modular symbol with the distinguished basis of P. + i = s.i + a, b, c, d = s.lift_to_sl2z() + e = [R(p) for p in P.mseval(I, matrix(2, 2, [b, a, d, c]))] + g = (d*x - b)**i * (-c*x + a)**(m - i) + return [sum(f[j] * g[m - j] / m.binomial(j) for j in range(m + 1)) + for f in e] + return matrix([ev(s) for s in self.manin_symbols_basis()]) + + def _pari_tensor(self): + r""" + Return a matrix expressing the duality between this space and the + corresponding space of modular symbols in PARI. + + Let `M` be an ambient space of modular symbols over a field `K` + of characteristic 0. The corresponding space `P` in PARI is + canonically dual to `M`, giving rise to an element + + .. MATH:: + + T \in P \otimes_K M. + + OUTPUT: The matrix of the element `T \in P \otimes_K M`. + This is the inverse of the matrix returned by + :meth:`_pari_pairing`. + + This is currently only implemented for spaces of modular + symbols of trivial character. + + EXAMPLES:: + + sage: M = ModularSymbols(Gamma0(37), 2) + sage: P = M.__pari__() + sage: T = M._pari_tensor(); T + [ 1 0 0 0 0] + [ 0 0 -1 0 0] + [ 0 0 1 -1 0] + [ 0 -1 0 -1 1] + [ 0 0 0 1 -1] + + The duality is compatible with (for example) Hecke operators + and the star involution:: + + sage: T * M.hecke_matrix(3) == P.mshecke(3) * T + True + sage: T * M.star_involution().matrix() == P.msstar() * T + True + """ + if self.weight() != 2: + return self._pari_pairing().inverse() + from sage.matrix.constructor import matrix + gens = self.__pari__().mspathgens()[0][:self.rank()].sage() + # gens is a basis for the space of modular symbols of weight 2 + # (in the sense of Sage), and the distinguished basis of the + # corresponding PARI space of modular symbols is dual to this. + return matrix([self.modular_symbol(g).element() for g in gens]) + class ModularSymbolsAmbient_wtk_g0(ModularSymbolsAmbient): r""" @@ -2723,6 +2843,34 @@ def _hecke_images(self, i, v): return heilbronn.hecke_images_gamma0_weight_k(c.u,c.v, c.i, N, self.weight(), v, self.manin_gens_to_basis()) + @cached_method + def __pari__(self): + """ + Return a PARI object corresponding to ``self``. + + EXAMPLES:: + + sage: ModularSymbols(Gamma0(1), 2).__pari__() + [[[[Vecsmall([0, 1])], [0], 1, [Vecsmall([]), Vecsmall([])], + Vecsmall([1]), Vecsmall([]), Vecsmall([])], + 0, 0, 0, Vecsmall([1]), 0, 0, [[1, 1; [0, 1; -1, 0], 1]], + [[1, 1; [0, -1; 1, -1], 1; [-1, 1; -1, 0], 1]], 0, + Vecsmall([0, 0, 1, 1, 2]), [[Vecsmall([1, 0]), Vecsmall([0, 1])]], + 0, 0, 0, [Vecsmall([1, 1]), [Vecsmall([0, 1]), 0], [Vecsmall([1, 0])]]], + [0, [;], [[;], [;], 1, Vecsmall([])]], + [[], Vecsmall([2, 0]), Vecsmall([0, 0]), 0, [[;], [;], 1, Vecsmall([])]]] + + .. NOTE:: + + Spaces of modular symbols as implemented in PARI are + canonically dual to those implemented in Sage. See + :meth:`ModularSymbolsAmbient._pari_pairing` and + :meth:`ModularSymbolsAmbient._pari_tensor` for how to use + this duality. + """ + return self.level().__pari__().msinit(self.weight(), self.sign()) + + class ModularSymbolsAmbient_wt2_g0(ModularSymbolsAmbient_wtk_g0): r""" Modular symbols for `\Gamma_0(N)` of integer weight `2` over the field From 7eee7608f230233890085f34662526205da5d13b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Jul 2021 00:58:42 -0700 Subject: [PATCH 133/336] sage.misc.misc.union: Deprecate, remove uses --- src/sage/combinat/cluster_algebra_quiver/cluster_seed.py | 9 +++------ src/sage/misc/all.py | 2 ++ src/sage/misc/misc.py | 4 ++++ src/sage/modular/etaproducts.py | 5 ++--- src/sage/rings/number_field/number_field.py | 3 +-- src/sage/rings/polynomial/laurent_polynomial.pyx | 5 ++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index b459b645422..61771810ab3 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -4626,7 +4626,7 @@ def coeff_recurs(p, q, a1, a2, b, c): return sum((-1)**(k-1)*coeff_recurs(p, q-k, a1, a2, b, c)*_bino(a1-b*p+k-1, k) for k in range(1, q+1)) -def PathSubset(n,m): +def PathSubset(n, m): r""" Encodes a *maximal* Dyck path from (0,0) to (n,m) (for n >= m >= 0) as a subset of {0,1,2,..., 2n-1}. The encoding is given by indexing horizontal edges by odd numbers and vertical edges by evens. @@ -4648,15 +4648,12 @@ def PathSubset(n,m): sage: PathSubset(4,4) {0, 1, 2, 3, 4, 5, 6, 7} """ - from sage.misc.misc import union from sage.functions.other import floor - S = [ ] - for i in range(n): - S = union(S, [2*i+1]) + S = set(2 * i + 1 for i in range(n)) if m > 0: for j in range(n): if floor((j+1)*m/n) - floor(j*m/n) == 1: - S = union(S, [2*j]) + S.add(2 * j) return set(S) diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index d108d4e5f74..375b8e852f1 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -10,6 +10,8 @@ SAGE_DB, SAGE_TMP, newton_method_sizes, compose, nest) +lazy_import('sage.misc.misc', 'union', + deprecation=32096) from .verbose import (set_verbose, set_verbose_files, get_verbose_files, unset_verbose_files, get_verbose) diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index 4580f836f18..fbc6a382a64 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -519,6 +519,8 @@ def union(x, y=None): EXAMPLES:: sage: answer = union([1,2,3,4], [5,6]); answer + doctest:...: DeprecationWarning: sage.misc.misc.union is deprecated... + See https://trac.sagemath.org/32096 for details. [1, 2, 3, 4, 5, 6] sage: union([1,2,3,4,5,6], [5,6]) == answer True @@ -527,6 +529,8 @@ def union(x, y=None): sage: union((1,2,3,4,5,6), set([5,6])) == answer True """ + from sage.misc.superseded import deprecation + deprecation(32096, "sage.misc.misc.union is deprecated, use 'list(set(x).union(y)' or a more suitable replacement") if y is None: return list(set(x)) return list(set(x).union(y)) diff --git a/src/sage/modular/etaproducts.py b/src/sage/modular/etaproducts.py index 4a90b2cecfe..223e776637b 100644 --- a/src/sage/modular/etaproducts.py +++ b/src/sage/modular/etaproducts.py @@ -35,7 +35,6 @@ from sage.arith.misc import divisors, prime_divisors, euler_phi, is_square, gcd from sage.categories.groups import Groups from sage.matrix.constructor import matrix -from sage.misc.misc import union from sage.modules.free_module import FreeModule from sage.rings.finite_rings.integer_mod import Mod from sage.rings.finite_rings.integer_mod_ring import IntegerModRing @@ -149,7 +148,7 @@ def _mul_(self, other): Eta product of level 4 : (eta_1)^24 (eta_2)^-48 (eta_4)^24 """ newdict = {d: self._rdict.get(d, 0) + other._rdict.get(d, 0) - for d in union(self._rdict, other._rdict)} + for d in set(self._rdict).union(other._rdict)} P = self.parent() return P.element_class(P, newdict) @@ -166,7 +165,7 @@ def _div_(self, other): True """ newdict = {d: self._rdict.get(d, 0) - other._rdict.get(d, 0) - for d in union(self._rdict, other._rdict)} + for d in set(self._rdict).union(other._rdict)} P = self.parent() return P.element_class(P, newdict) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 03d38c67168..928b0efcfe2 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -134,7 +134,6 @@ import sage.rings.ring from sage.misc.latex import latex_variable_name -from sage.misc.misc import union from .unit_group import UnitGroup from .class_group import ClassGroup @@ -10047,7 +10046,7 @@ def hilbert_conductor(self,a,b): """ a, b = self(a), self(b) d = self.ideal(1) - for p in union(union( self.ideal(2).prime_factors(), self.ideal(a).prime_factors()), self.ideal(b).prime_factors()): + for p in set(self.ideal(2).prime_factors()).union(self.ideal(a).prime_factors()).union(self.ideal(b).prime_factors()): if self.hilbert_symbol(a,b,p) == -1: d *= p return d diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 696d1bd84d9..c0229a7b935 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -13,7 +13,6 @@ Elements of Laurent polynomial rings from sage.rings.integer cimport Integer from sage.categories.map cimport Map from sage.structure.element import is_Element, coerce_binop -from sage.misc.misc import union from sage.structure.factorization import Factorization from sage.misc.derivative import multi_derivative from sage.rings.polynomial.polynomial_element import Polynomial @@ -2661,9 +2660,9 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): cdef dict d = self.dict() cdef tuple g = self._parent.gens() cdef Py_ssize_t nvars = len(g) - cdef list vars = [] + cdef set vars = set() for k in d: - vars = union(vars, k.nonzero_positions()) + vars.update(k.nonzero_positions()) if len(vars) == nvars: break cdef list v = [g[i] for i in vars] From 44da7d84b68709730bbcf58795dcaa610efba67a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Jul 2021 08:59:46 -0700 Subject: [PATCH 134/336] src/sage/schemes/elliptic_curves/ell_number_field.py: Remove useless use of union --- src/sage/schemes/elliptic_curves/ell_number_field.py | 10 ++++++---- src/sage/schemes/elliptic_curves/ell_rational_field.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index ccfa33915a9..821d2e96639 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -90,7 +90,9 @@ from .ell_point import EllipticCurvePoint_number_field from .constructor import EllipticCurve from sage.rings.all import PolynomialRing, ZZ, QQ, RealField, Integer -from sage.misc.all import cached_method, prod, union +from sage.misc.cachefunc import cached_method +from sage.misc.misc_c import prod + class EllipticCurve_number_field(EllipticCurve_field): r""" @@ -840,9 +842,9 @@ def global_integral_model(self): """ K = self.base_field() ai = self.a_invariants() - Ps = union(ff[0] - for a in ai if not a.is_integral() - for ff in a.denominator_ideal().factor()) + Ps = set(ff[0] + for a in ai if not a.is_integral() + for ff in a.denominator_ideal().factor()) for P in Ps: pi = K.uniformizer(P, 'positive') e = min((ai[i].valuation(P)/[1,2,3,4,6][i]) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index bda999eeca3..a8a18fc3e59 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2525,7 +2525,7 @@ def regulator(self, proof=None, precision=53, **kwds): return reg def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): - """Given a list of rational points on E, compute the saturation in + r"""Given a list of rational points on E, compute the saturation in E(Q) of the subgroup they generate. INPUT: From 8fb8315beb638d082011c88dba8b359e7b61be40 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Jul 2021 09:28:15 -0700 Subject: [PATCH 135/336] src/sage/topology/simplicial_complex.py: Remove use of sage.misc.misc.union --- src/sage/topology/simplicial_complex.py | 28 +++++++++---------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index b607728e9ba..e34c53cee6f 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -148,7 +148,6 @@ sage: S == T True """ -from operator import index as PyNumber_Index # possible future directions for SimplicialComplex: # @@ -171,12 +170,11 @@ from sage.rings.rational_field import QQ from sage.structure.category_object import normalize_names from sage.misc.latex import latex -from sage.misc.misc import union from sage.matrix.constructor import matrix from sage.homology.chain_complex import ChainComplex from sage.graphs.graph import Graph from functools import reduce, total_ordering -from itertools import combinations +from itertools import combinations, chain lazy_import('sage.categories.simplicial_complexes', 'SimplicialComplexes') @@ -1005,7 +1003,7 @@ def __init__(self, Parent.__init__(self, category=category) C = None - vertex_set = () + vertices = () if from_characteristic_function is not None: from sage.combinat.subsets_hereditary import subsets_with_hereditary_property f, X = from_characteristic_function @@ -1022,8 +1020,13 @@ def __init__(self, if not isinstance(maximal_faces, (list, tuple, Simplex)): # Convert it into a list (in case it is an iterable) maximal_faces = list(maximal_faces) - if maximal_faces: - vertex_set = reduce(union, maximal_faces) + if len(maximal_faces) == 1 and isinstance(maximal_faces[0], (int, Integer)): + # list containing a single non-negative integer n; + # construct the simplicial complex with a single n-simplex as the only facet. + vertices = tuple(range(maximal_faces[0] + 1)) + maximal_faces = [vertices] + elif maximal_faces: + vertices = tuple(set(chain.from_iterable(maximal_faces))) if C is not None: self._facets = list(C.facets()) self._faces = copy(C._faces) @@ -1038,16 +1041,6 @@ def __init__(self, self.set_immutable() return - try: - # Check whether vertex_set is an integer - n = PyNumber_Index(vertex_set) - except TypeError: - pass - else: - vertex_set = range(n + 1) - - vertices = tuple(vertex_set) - gen_dict = {} for v in vertices: if name_check: @@ -2719,8 +2712,7 @@ def remove_face(self, face, check=False): self._facets.append(Simplex(-1)) # Recreate the vertex set - from sage.misc.misc import union - vertices = tuple(reduce(union, self._facets)) + vertices = set(chain.from_iterable(self._facets)) for v in self.vertices(): if v not in vertices: del self._vertex_to_index[v] From bfddaaecd3704c84d2972f10c16baf619249d118 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Jul 2021 17:12:14 -0700 Subject: [PATCH 136/336] Remove uses of sage.misc.misc.union in doctests --- src/sage/arith/misc.py | 16 ++++++++-------- src/sage/geometry/polyhedron/library.py | 2 +- src/sage/groups/perm_gps/permgroup.py | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 76a3f2f7f8d..83f3a957d17 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -328,16 +328,16 @@ def bernoulli(n, algorithm='default', num_threads=1): TESTS:: - sage: algs = ['arb','gap','gp','pari','bernmm','flint'] + sage: algs = ['arb', 'gap', 'gp', 'pari', 'bernmm', 'flint'] sage: test_list = [ZZ.random_element(2, 2255) for _ in range(500)] - sage: vals = [[bernoulli(i,algorithm = j) for j in algs] for i in test_list] # long time (up to 21s on sage.math, 2011) - sage: union([len(union(x))==1 for x in vals]) # long time (depends on previous line) - [True] - sage: algs = ['gp','pari','bernmm'] + sage: vals = [[bernoulli(i, algorithm=j) for j in algs] for i in test_list] # long time (up to 21s on sage.math, 2011) + sage: all(len(set(x))==1 for x in vals) # long time (depends on previous line) + True + sage: algs = ['gp', 'pari', 'bernmm'] sage: test_list = [ZZ.random_element(2256, 5000) for _ in range(500)] - sage: vals = [[bernoulli(i,algorithm = j) for j in algs] for i in test_list] # long time (up to 30s on sage.math, 2011) - sage: union([len(union(x))==1 for x in vals]) # long time (depends on previous line) - [True] + sage: vals = [[bernoulli(i, algorithm=j) for j in algs] for i in test_list] # long time (up to 30s on sage.math, 2011) + sage: all(len(set(x))==1 for x in vals) # long time (depends on previous line) + True sage: from numpy import int8 sage: bernoulli(int8(12)) -691/2730 diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index fa1bd00d953..4fe82a3a62a 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -1734,7 +1734,7 @@ def Kirkman_icosahedron(self, backend=None): sage: vertices = ki.vertices() sage: edges = [[vector(edge[0]),vector(edge[1])] for edge in ki.bounded_edges()] sage: edge_lengths = [norm(edge[0]-edge[1]) for edge in edges] - sage: union(edge_lengths) + sage: sorted(set(edge_lengths)) [7, 8, 9, 11, 12, 14, 16] TESTS:: diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index e379fcf9d4a..e13042e6818 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -1549,12 +1549,12 @@ def orbit(self, point, action="OnPoints"): Action of `S_4` on sets of disjoint sets:: sage: S4 = groups.permutation.Symmetric(4) - sage: O = S4.orbit(((1,2),(3,4)), action = "OnSetsDisjointSets") + sage: O = S4.orbit(((1,2),(3,4)), action="OnSetsDisjointSets") sage: {1, 2} in O[0] and {3, 4} in O[0] True sage: {1, 4} in O[1] and {2, 3} in O[1] True - sage: all(set(union(*x)) == {1,2,3,4} for x in O) + sage: all(x[0].union(x[1]) == {1,2,3,4} for x in O) True Action of `S_4` (on a nonstandard domain) on tuples of sets:: From 0994f0f26a4f5d4f2e14f848d328676d3e34d57b Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Wed, 30 Jun 2021 15:28:03 -0600 Subject: [PATCH 137/336] trac 32084 fix nth root --- src/sage/rings/finite_rings/integer_mod.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index 03d7838b4a5..f95e6ef253d 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -1504,7 +1504,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): if self == 1: if all: return [s*K(p*k+m.lift()) for k in range(p**(k-(2 if p==2 else 1))) for m in modp for s in sign] - else: return self_orig + else: return K(modp) else: if all: return [] else: raise ValueError("no nth root") From 226fe8486103442d6426613e87491a98d2f11881 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Fri, 2 Jul 2021 19:10:11 -0600 Subject: [PATCH 138/336] trac 32084 fix nth root --- src/sage/rings/finite_rings/integer_mod.pyx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index f95e6ef253d..5e406272e1d 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -1387,6 +1387,11 @@ cdef class IntegerMod_abstract(FiniteRingElement): sage: mod(-1, 4489).nth_root(2, all=True) [] + We check that :trac:`32084` is fixed:: + + sage: mod(24, 25).nth_root(50)^50 + 24 + Check that the code path cunningham might be used:: sage: a = Mod(9,11) @@ -1504,7 +1509,8 @@ cdef class IntegerMod_abstract(FiniteRingElement): if self == 1: if all: return [s*K(p*k+m.lift()) for k in range(p**(k-(2 if p==2 else 1))) for m in modp for s in sign] - else: return K(modp) + else: + return K(modp.lift()) else: if all: return [] else: raise ValueError("no nth root") From e523ac00d9c89e36a6341a731633b12e87ee1bd7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Jul 2021 23:01:29 -0700 Subject: [PATCH 139/336] sage.calculus.all.symbolic_expression: Handle callables --- src/sage/calculus/all.py | 46 +++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/sage/calculus/all.py b/src/sage/calculus/all.py index a52d530db0b..2aefda65f56 100644 --- a/src/sage/calculus/all.py +++ b/src/sage/calculus/all.py @@ -73,7 +73,7 @@ def symbolic_expression(x): sage: symbolic_expression(E) in SR True - If x is a list or tuple, create a vector of symbolic expressions:: + If ``x`` is a list or tuple, create a vector of symbolic expressions:: sage: v=symbolic_expression([x,1]); v (x, 1) @@ -93,6 +93,28 @@ def symbolic_expression(x): (x*y + y^2 + y == x^3 + x^2 - 10*x - 10, x*y + y^2 + y == x^3 + x^2 - 10*x - 10) sage: v.base_ring() Symbolic Ring + + If ``x`` is a function, for example defined by a ``lambda`` expression, create a + symbolic function:: + + sage: f = symbolic_expression(lambda z: z^2 + 1); f + z |--> z^2 + 1 + sage: f.parent() + sage: f(7) + + If ``x`` is a list or tuple of functions, or if ``x`` is a function that returns a list + or tuple, create a callable symbolic vector:: + + sage: symbolic_expression([lambda mu, nu: mu^2 + nu^2, lambda mu, nu: mu^2 - nu^2]) + (mu, nu) |--> (mu^2 + nu^2, mu^2 - nu^2) + sage: f = symbolic_expression(lambda uwu: [1, uwu, uwu^2]); f + uwu |--> (1, uwu, uwu^2) + sage: f.parent() + Vector space of dimension 3 over Callable function ring with argument uwu + sage: f(5) + (1, 5, 25) + sage: f(5).parent() + Vector space of dimension 3 over Symbolic Ring """ from sage.symbolic.expression import Expression from sage.symbolic.ring import SR @@ -100,9 +122,23 @@ def symbolic_expression(x): return x elif hasattr(x, '_symbolic_'): return x._symbolic_(SR) - elif isinstance(x, (tuple,list)): - return vector(SR,x) - else: - return SR(x) + elif isinstance(x, (tuple, list)): + return vector([symbolic_expression(item) for item in x]) + elif callable(x): + from inspect import signature, Parameter + try: + s = signature(x) + except ValueError: + pass + else: + if all(param.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD) + for param in s.parameters.values()): + vars = [SR.var(name) for name in s.parameters.keys()] + result = x(*vars) + if isinstance(result, (tuple, list)): + return vector(SR, result).function(*vars) + else: + return SR(result).function(*vars) + return SR(x) from . import desolvers From 4d500b30654d7a0448f1ae1c0b22170c2e5c9e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Jul 2021 09:00:45 +0200 Subject: [PATCH 140/336] flake8 cleanup of latex.py --- src/sage/misc/latex.py | 155 ++++++++++++++++++++++------------------- 1 file changed, 85 insertions(+), 70 deletions(-) diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 279c21e2f6e..2b9567cd0b6 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -29,19 +29,18 @@ import random import re import shutil -import subprocess +from subprocess import call, PIPE from sage.misc import sage_eval from sage.misc.cachefunc import cached_function, cached_method from sage.misc.sage_ostools import have_program from sage.misc.temporary_file import tmp_dir - +from sage.structure.sage_object import SageObject from sage.misc.lazy_import import lazy_import lazy_import('sage.misc.html', ('MathJax', 'MathJaxExpr'), deprecation=31536) -COMMON_HEADER = \ -r'''\usepackage{amsmath} +COMMON_HEADER = r'''\usepackage{amsmath} \usepackage{amssymb} \usepackage{amsfonts} \usepackage{graphicx} @@ -51,8 +50,7 @@ \usepackage[T1]{fontenc} ''' -LATEX_HEADER = ( -r'''\documentclass{article} +LATEX_HEADER = (r'''\documentclass{article} ''' + COMMON_HEADER + r'''\oddsidemargin 0.0in \evensidemargin 0.0in @@ -63,8 +61,7 @@ \textheight 9.0in ''') -SLIDE_HEADER = ( -r'''\documentclass[a0,8pt]{beamer} +SLIDE_HEADER = (r'''\documentclass[a0,8pt]{beamer} ''' + COMMON_HEADER + r'''\textwidth=1.1\textwidth \textheight=2\textheight @@ -72,11 +69,11 @@ @cached_function -def have_latex(): +def have_latex() -> bool: """ Return ``True`` if this computer has the program ``latex``. - If this computer doesn't have LaTeX installed, you may obtain it + If this computer does not have LaTeX installed, you may obtain it from http://ctan.org/. EXAMPLES:: @@ -89,11 +86,11 @@ def have_latex(): @cached_function -def have_pdflatex(): +def have_pdflatex() -> bool: """ Return ``True`` if this computer has the program ``pdflatex``. - If this computer doesn't have pdflatex installed, you may obtain it + If this computer does not have pdflatex installed, you may obtain it from http://ctan.org/. EXAMPLES:: @@ -106,11 +103,11 @@ def have_pdflatex(): @cached_function -def have_xelatex(): +def have_xelatex() -> bool: """ Return ``True`` if this computer has the program ``xelatex``. - If this computer doesn't have xelatex installed, you may obtain it + If this computer does not have xelatex installed, you may obtain it from http://ctan.org/. EXAMPLES:: @@ -123,11 +120,11 @@ def have_xelatex(): @cached_function -def have_dvipng(): +def have_dvipng() -> bool: """ Return ``True`` if this computer has the program ``dvipng``. - If this computer doesn't have dvipng installed, you may obtain it + If this computer does not have dvipng installed, you may obtain it from http://sourceforge.net/projects/dvipng/ EXAMPLES:: @@ -140,11 +137,11 @@ def have_dvipng(): @cached_function -def have_convert(): +def have_convert() -> bool: """ Return ``True`` if this computer has the program ``convert``. - If this computer doesn't have convert installed, you may obtain it + If this computer does not have convert installed, you may obtain it (along with the rest of the ImageMagick suite) from http://www.imagemagick.org @@ -233,6 +230,7 @@ def bool_function(x): """ return r"\mathrm{%s}" % bool(x) + def builtin_constant_function(x): r""" Returns the LaTeX code for a builtin constant ``x``. @@ -258,6 +256,7 @@ def builtin_constant_function(x): """ return "\\mbox{\\rm %s}" % x + def None_function(x): r""" Returns the LaTeX code for ``None``. @@ -275,6 +274,7 @@ def None_function(x): assert x is None return r"\mathrm{None}" + def str_function(x): r""" Return a LaTeX representation of the string ``x``. @@ -338,6 +338,7 @@ def str_function(x): x = "\\begin{array}{l}\n%s\n\\end{array}" % x return x + def dict_function(x): r""" Return the LaTeX code for a dictionary ``x``. @@ -365,6 +366,7 @@ def dict_function(x): # One can add to the latex_table in order to install latexing # functionality for other types. (Suggested by Robert Kerns of Enthought.) + def float_function(x): r""" Returns the LaTeX code for a python float ``x``. @@ -471,7 +473,7 @@ def __add__(self, other): sage: type(o) - We add extra space only if it wasn't there yet:: + We add extra space only if it was not there yet:: sage: LatexExpr("foo ") + LatexExpr("bar") foo bar @@ -529,7 +531,8 @@ def _latex_(self): """ return str(self) -def has_latex_attr(x): + +def has_latex_attr(x) -> bool: """ Return ``True`` if ``x`` has a ``_latex_`` attribute, except if ``x`` is a ``type``, in which case return ``False``. @@ -558,7 +561,6 @@ def has_latex_attr(x): """ return hasattr(x, '_latex_') and not isinstance(x, type) -from sage.structure.sage_object import SageObject class _Latex_prefs_object(SageObject): """ @@ -585,6 +587,7 @@ def __init__(self, bb=False, delimiters=["(", ")"], self._option["engine"] = "pdflatex" self._option["engine_name"] = "LaTeX" + _Latex_prefs = _Latex_prefs_object() ############################################################## @@ -592,6 +595,7 @@ def __init__(self, bb=False, delimiters=["(", ")"], # the Sage Notebook ######################################### + def latex_extra_preamble(): r""" Return the string containing the user-configured preamble, @@ -631,12 +635,13 @@ def latex_extra_preamble(): "\n".join(sage_latex_macros()), _Latex_prefs._option['macros']]) + def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_in_background=False): """ This runs LaTeX on the TeX file "filename.tex". It produces files "filename.dvi" (or "filename.pdf"` if engine is either ``pdflatex`` or ``xelatex``) and if ``png`` is ``True``, "filename.png". If ``png`` - is ``True`` and dvipng can't convert the dvi file to png (because of + is ``True`` and dvipng cannot convert the dvi file to png (because of postscript specials or other issues), then dvips is called, and the PS file is converted to a png file. @@ -675,7 +680,7 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i If ``png`` is ``True``, then when using latex (the default), you must have 'dvipng' (or 'dvips' and 'convert') installed on your - operating system, or this command won't work. When using + operating system, or this command will not work. When using pdflatex or xelatex, you must have 'convert' installed. EXAMPLES:: @@ -762,7 +767,7 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i if len(filename.split()) > 1: raise ValueError("filename must contain no spaces") if not debug: - redirect = subprocess.PIPE + redirect = PIPE else: redirect = None # if do_in_background: @@ -788,14 +793,16 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i '{0}x{0}'.format(density), '-trim', filename + '.' + suffix, filename + '.png'] - e = False # it is possible to get through the following commands - # without running a program, so in that case we force error + e = False + # it is possible to get through the following commands + # without running a program, so in that case we force error # our standard way of calling programs here; change this if we want # finer-grained analysis of the return code. Think of the output as # a boolean: "the command exited normally" - subpcall = lambda x: not subprocess.call(x, stdout=redirect, - stderr=redirect, cwd=base) + def subpcall(x): + return not call(x, stdout=redirect, + stderr=redirect, cwd=base) if engine == "pdflatex" or engine == "xelatex": if debug: print(lt) @@ -842,7 +849,7 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i print("error running dvips and ps2pdf; trying pdflatex instead...") print(pdflt) e = subpcall(pdflt) - else: # don't have dvipng, so must have convert. run latex, dvips, convert. + else: # do not have dvipng, so must have convert. run latex, dvips, convert. if debug: print(lt) print(dvips) @@ -944,7 +951,7 @@ class Latex(LatexCall): .. WARNING:: You must have dvipng (or dvips and convert) installed - on your operating system, or this command won't work. + on your operating system, or this command will not work. EXAMPLES:: @@ -992,9 +999,9 @@ def _relation_symbols(self): ' \\geq ' """ import operator - return {operator.lt:' < ', operator.le:' \\leq ', - operator.eq:' = ', operator.ne:' \\neq ', - operator.ge:' \\geq ', operator.gt:' > '} + return {operator.lt: ' < ', operator.le: ' \\leq ', + operator.eq: ' = ', operator.ne: ' \\neq ', + operator.ge: ' \\geq ', operator.gt: ' > '} def _latex_preparse(self, s, locals): r""" @@ -1013,7 +1020,7 @@ def _latex_preparse(self, s, locals): if i == -1 or i == i0: return s i0 = i - t = s[i+6:] + t = s[i + 6:] j = t.find('}') if j == -1: return s @@ -1024,7 +1031,7 @@ def _latex_preparse(self, s, locals): except Exception as msg: print(msg) k = '\\mbox{\\rm [%s undefined]}' % var - s = s[:i] + k + t[j+1:] + s = s[:i] + k + t[j + 1:] def eval(self, x, globals, strip=False, filename=None, debug=None, density=None, pdflatex=None, engine=None, locals={}): @@ -1082,7 +1089,7 @@ def eval(self, x, globals, strip=False, filename=None, debug=None, if density is None: density = self.__density if filename is None: - filename = 'sage%s' % random.randint(1, 100) # to defeat browser caches + filename = 'sage%s' % random.randint(1, 100) # to defeat browser caches else: filename = os.path.splitext(filename)[0] # get rid of extension base = tmp_dir() @@ -1115,7 +1122,7 @@ def eval(self, x, globals, strip=False, filename=None, debug=None, else: engine = self.__engine e = _run_latex_(os.path.join(base, filename + ".tex"), debug=debug, - density=density, engine=engine, png=True) + density=density, engine=engine, png=True) if e.find("Error") == -1: shutil.copy(os.path.join(base, filename + ".png"), os.path.join(orig_base, filename + ".png")) @@ -1124,7 +1131,7 @@ def eval(self, x, globals, strip=False, filename=None, debug=None, else: return - def blackboard_bold(self, t = None): + def blackboard_bold(self, t=None): r"""nodetex Controls whether Sage uses blackboard bold or ordinary bold face for typesetting ZZ, RR, etc. @@ -1331,7 +1338,7 @@ def matrix_column_alignment(self, align=None): _Latex_prefs._option['matrix_column_alignment'] = align @cached_method - def has_file(self, file_name): + def has_file(self, file_name) -> bool: """ INPUT: @@ -1347,15 +1354,15 @@ def has_file(self, file_name): False """ assert isinstance(file_name, str) - from subprocess import call, PIPE try: - retcode = call("kpsewhich %s"%file_name, shell=True, stdout=PIPE, stderr=PIPE) + retcode = call("kpsewhich %s" % file_name, shell=True, + stdout=PIPE, stderr=PIPE) return (retcode == 0) except OSError: return False @cached_method - def check_file(self, file_name, more_info = ""): + def check_file(self, file_name, more_info=""): """ INPUT: @@ -1389,7 +1396,6 @@ def check_file(self, file_name, more_info = ""): if more_info: print(more_info) - def extra_macros(self, macros=None): r"""nodetex String containing extra LaTeX macros to use with %latex and %html. @@ -1541,10 +1547,10 @@ def add_package_to_preamble_if_available(self, package_name): sage: latex.extra_preamble('') """ assert isinstance(package_name, str) - if self.has_file(package_name+".sty"): - self.add_to_preamble("\\usepackage{%s}\n"%package_name) + if self.has_file(package_name + ".sty"): + self.add_to_preamble("\\usepackage{%s}\n" % package_name) - def engine(self, e = None): + def engine(self, e=None): r""" Set Sage to use ``e`` as latex engine when typesetting with :func:`view`, in ``%latex`` cells, etc. @@ -1597,13 +1603,15 @@ def engine(self, e = None): # old latex function are now in Latex.__call__; thus the following # assignment. + latex = Latex() # Ensure that latex appear in the sphinx doc as a function # so that the link :func:`latex` is correctly set up. -latex.__doc__ = Latex.__call__.__doc__ +latex.__doc__ = Latex.__call__.__doc__ ######################################### -def _latex_file_(objects, title='SAGE', debug=False, \ + +def _latex_file_(objects, title='SAGE', debug=False, sep='', tiny=False, math_left='\\[', math_right='\\]', extra_preamble=''): @@ -1687,12 +1695,12 @@ def _latex_file_(objects, title='SAGE', debug=False, \ objects = [objects] if tiny: - size='\\tiny\n' + size = '\\tiny\n' else: - size='' + size = '' - formatted_title = "\n\\begin{center}{\\Large\\bf %s}\\end{center}\n"%str(title) if title else "" - s = '%s\n\\begin{document}%s%s'%(extra_preamble, formatted_title, size) + formatted_title = "\n\\begin{center}{\\Large\\bf %s}\\end{center}\n" % str(title) if title else "" + s = '%s\n\\begin{document}%s%s' % (extra_preamble, formatted_title, size) if title: s += '\\vspace{40mm}' @@ -1704,17 +1712,17 @@ def _latex_file_(objects, title='SAGE', debug=False, \ # Resize the pgf figure to the text width if larger. s += r'\begingroup\makeatletter\@ifundefined{pgffigure}{\newsavebox{\pgffigure}}{}\makeatother\endgroup' s += r'\begin{lrbox}{\pgffigure}' + '\n' - s += '%s'%L + s += '%s' % L s += r'\end{lrbox}' s += r'\resizebox{\ifdim\width>\textwidth\textwidth\else\width\fi}{!}{\usebox{\pgffigure}}' + '\n' elif '\\begin{verbatim}' not in L: - s += '%s%s%s'%(math_left, L, math_right) + s += '%s%s%s' % (math_left, L, math_right) else: - s += '%s'%L - if i < len(objects)-1: - s += '\n\n%s\n\n'%sep + s += '%s' % L + if i < len(objects) - 1: + s += '\n\n%s\n\n' % sep else: - s += "\n\n".join([str(x) for x in objects]) + s += "\n\n".join(str(x) for x in objects) # latex_extra_preamble() is called here and not before because some objects # may require additional packages to be displayed in LaTeX. Hence, the call @@ -1728,6 +1736,7 @@ def _latex_file_(objects, title='SAGE', debug=False, \ return s + def view(objects, title='Sage', debug=False, sep='', tiny=False, pdflatex=None, engine=None, viewer=None, tightpage=True, margin=None, mode='inline', combine_all=False, **kwds): @@ -1916,10 +1925,11 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, viewer = 'sage-native-execute ' + viewer if debug: print('viewer: "{}"'.format(viewer)) - subprocess.call('%s %s' % (viewer, output_file), shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + call('%s %s' % (viewer, output_file), shell=True, + stdout=PIPE, stderr=PIPE) return + def png(x, filename, density=150, debug=False, do_in_background=False, tiny=False, pdflatex=True, engine='pdflatex'): """ @@ -1984,6 +1994,7 @@ def png(x, filename, density=150, debug=False, return s return + def coeff_repr(c): r""" LaTeX string representing coefficients in a linear combination. @@ -2015,6 +2026,7 @@ def coeff_repr(c): return "(%s)" % s return s + def repr_lincomb(symbols, coeffs): r""" Compute a latex representation of a linear combination of some @@ -2102,7 +2114,7 @@ def repr_lincomb(symbols, coeffs): i += 1 if first: s = "0" - s = s.replace("+ -","- ") + s = s.replace("+ -", "- ") return s @@ -2146,6 +2158,7 @@ def repr_lincomb(symbols, coeffs): 'times', 'star'] + def latex_varify(a, is_fname=False): r""" Convert a string ``a`` to a LaTeX string: if it's an element of @@ -2189,9 +2202,10 @@ def latex_varify(a, is_fname=False): elif len(a) == 1: return a elif is_fname is True: - return '{\\rm %s}'%a + return '{\\rm %s}' % a else: - return '\\mathit{%s}'%a + return '\\mathit{%s}' % a + def latex_variable_name(x, is_fname=False): r""" @@ -2274,7 +2288,7 @@ def latex_variable_name(x, is_fname=False): suffix = x[m.start():] else: prefix = x[:underscore] - suffix = x[underscore+1:] + suffix = x[underscore + 1:] if prefix == '': from sage.calculus.calculus import symtable for sym in symtable.values(): @@ -2284,7 +2298,7 @@ def latex_variable_name(x, is_fname=False): # handle the suffix specially because it very well might be numeric # I use strip to avoid using regex's -- It makes it a bit faster (and the code is more comprehensible to non-regex'ed people) if suffix.strip("1234567890") != "": - suffix = latex_variable_name(suffix, is_fname) # recurse to deal with recursive subscripts + suffix = latex_variable_name(suffix, is_fname) # recurse to deal with recursive subscripts return '%s_{%s}' % (latex_varify(prefix, is_fname), suffix) else: return latex_varify(prefix, is_fname) @@ -2339,7 +2353,7 @@ def _repr_(self): """ return r"""LaTeX example for testing display of graphs. -To use, first try calling 'view' on this object -- it won't work. +To use, first try calling 'view' on this object -- it will not work. Now, make sure that you have the most recent version of the TeX package pgf installed, along with the LaTeX package tkz-graph. Run 'latex.add_to_preamble("\\usepackage{tkz-graph}")', and try viewing it @@ -2407,11 +2421,11 @@ def _repr_(self): """ return """LaTeX example for testing display of pstricks output. -To use, first try calling 'view' on this object -- it won't work. Now, +To use, first try calling 'view' on this object -- it will not work. Now, make sure that you have the most recent version of the TeX package pstricks installed. Run 'latex.add_to_preamble("\\usepackage{pstricks}")' and try viewing it again. Call 'view' with the option `engine='latex'` --- the default behavior is to use pdflatex, which doesn't work with +-- the default behavior is to use pdflatex, which does not work with pstricks. From the command line, this should pop open a nice window with a picture of forces acting on a mass on a pendulum.""" @@ -2464,7 +2478,7 @@ def _repr_(self): """ return r"""LaTeX example for testing display of a knot produced by xypic. -To use, try to view this object -- it won't work. Now try +To use, try to view this object -- it will not work. Now try 'latex.add_to_preamble("\\usepackage[graph,knot,poly,curve]{xypic}")', and try viewing again. @@ -2514,7 +2528,7 @@ def _repr_(self): return r"""LaTeX example for testing display of a commutative diagram produced by xypic. -To use, try to view this object -- it won't work. Now try +To use, try to view this object -- it will not work. Now try 'latex.add_to_preamble("\\usepackage[matrix,arrow,curve,cmtip]{xy}")', and try viewing again. You should get a picture (a part of the diagram arising from a filtered chain complex).""" @@ -2561,4 +2575,5 @@ def _latex_(self): \restore }""" + latex_examples = LatexExamples() From 960ce8b1603ad55d857643b6293a9f3cad27db7b Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 3 Jul 2021 10:05:57 +0200 Subject: [PATCH 141/336] make groups ready for random seeds --- src/sage/groups/abelian_gps/abelian_group.py | 4 ++-- src/sage/groups/abelian_gps/dual_abelian_group.py | 12 ++++++------ src/sage/groups/additive_abelian/qmodnz.py | 12 ++++-------- src/sage/groups/additive_abelian/qmodnz_element.py | 4 ++-- src/sage/groups/perm_gps/permgroup.py | 7 +++++-- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 6567ac6c18b..02de6b3d44d 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -1161,8 +1161,8 @@ def random_element(self): EXAMPLES:: sage: G = AbelianGroup([2,3,9]) - sage: G.random_element() - f1^2 + sage: G.random_element().parent() is G + True """ from sage.misc.prandom import randint result = self.one() diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index 2e65e7f124a..b61e4b9654d 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -217,8 +217,8 @@ def random_element(self): sage: G = AbelianGroup([2,3,9]) sage: Gd = G.dual_group(base_ring=CC) - sage: Gd.random_element() - X1^2 + sage: Gd.random_element().parent() is Gd + True sage: N = 43^2-1 sage: G = AbelianGroup([N],names="a") @@ -226,10 +226,10 @@ def random_element(self): sage: a, = G.gens() sage: A, = Gd.gens() sage: x = a^(N/4); y = a^(N/3); z = a^(N/14) - sage: X = A*Gd.random_element(); X - A^615 - sage: len([a for a in [x,y,z] if abs(X(a)-1)>10^(-8)]) - 2 + sage: found = [False]*4 + sage: while not all(found): + ....: X = A*Gd.random_element() + ....: found[len([b for b in [x,y,z] if abs(X(b)-1)>10^(-8)])] = True """ from sage.misc.prandom import randint result = self.one() diff --git a/src/sage/groups/additive_abelian/qmodnz.py b/src/sage/groups/additive_abelian/qmodnz.py index c3f17bb150e..344344234a6 100644 --- a/src/sage/groups/additive_abelian/qmodnz.py +++ b/src/sage/groups/additive_abelian/qmodnz.py @@ -10,8 +10,8 @@ You can create random elements:: - sage: [G.random_element() for _ in range(4)] - [15/16, 0, 1/2, 139/190] + sage: all(G.random_element().parent() is G for _ in range(4)) + True There is an iterator over the (infinitely many) elements:: @@ -192,12 +192,8 @@ def random_element(self): EXAMPLES:: sage: G = QQ/(6*ZZ) - sage: G.random_element() - 47/16 - sage: G.random_element() - 1 - sage: G.random_element() - 3/5 + sage: G.random_element().parent() is G + True """ if self.n == 0: return self(QQ.random_element()) diff --git a/src/sage/groups/additive_abelian/qmodnz_element.py b/src/sage/groups/additive_abelian/qmodnz_element.py index 232fdf5a9eb..c5a857468c4 100644 --- a/src/sage/groups/additive_abelian/qmodnz_element.py +++ b/src/sage/groups/additive_abelian/qmodnz_element.py @@ -55,8 +55,8 @@ def __init__(self, parent, x, construct=False): EXAMPLES:: sage: G = QQ/(3*ZZ) - sage: G.random_element() - 47/16 + sage: G.random_element().parent() is G + True """ AdditiveGroupElement.__init__(self, parent) diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index e379fcf9d4a..2022bb69f7b 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -327,8 +327,11 @@ def PermutationGroup(gens=None, gap_group=None, domain=None, canonicalize=True, True sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]]) sage: current_randstate().set_seed_gap() - sage: G._gap_().DerivedSeries() - [ Group( [ (3,4), (1,2,3)(4,5) ] ), Group( [ (1,5)(3,4), (1,5)(2,4), (1,3,5) ] ) ] + sage: G1, G2 = G._gap_().DerivedSeries() + sage: G1 + Group( [ (3,4), (1,2,3)(4,5) ] ) + sage: G2.GeneratorsSmallest() + [ (3,4,5), (2,3)(4,5), (1,2)(4,5) ] TESTS:: From 5592c5868203029c1eebd11f7d156eaadeadbb94 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 3 Jul 2021 10:44:49 +0200 Subject: [PATCH 142/336] make some_elements of affine group ready for random seeds --- src/sage/groups/affine_gps/affine_group.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py index 6c1629f6dc8..e02d5ef425f 100644 --- a/src/sage/groups/affine_gps/affine_group.py +++ b/src/sage/groups/affine_gps/affine_group.py @@ -514,10 +514,12 @@ def some_elements(self): [0 1 0 0] [0] x |-> [0 0 1 0] x + [0] [0 0 0 1] [0], - [2 0 0 0] [0] - [0 1 0 0] [2] - x |-> [0 0 1 0] x + [0] - [0 0 0 1] [1]] + [2 0 0 0] [...] + [0 1 0 0] [...] + x |-> [0 0 1 0] x + [...] + [0 0 0 1] [...]] + sage: all(v.parent() is G for v in G.some_elements()) + True sage: G = AffineGroup(2,QQ) sage: G.some_elements() From a732a3fa1a1be4096569f890e46258565bc56058 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 3 Jul 2021 10:50:36 +0200 Subject: [PATCH 143/336] fix random tree on less than two vertices --- src/sage/graphs/generators/random.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index e06bc8a6d87..d73b7ab64c0 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -1314,6 +1314,12 @@ def RandomTree(n): ....: for i in range(100) ) True + Random tree with one and zero vertices:: + + sage: graphs.RandomTree(0) + Graph on 0 vertices + sage: graphs.RandomTree(1) + Graph on 1 vertex """ from sage.misc.prandom import randint g = Graph() @@ -1341,9 +1347,10 @@ def RandomTree(n): g.add_edge(x,s) count[s] -= 1 - # Adding as an edge the last two available vertices - last_edge = [ v for v in range(n) if count[v] != -1 ] - g.add_edge(last_edge) + if n > 1: + # Adding as an edge the last two available vertices + last_edge = [ v for v in range(n) if count[v] != -1 ] + g.add_edge(last_edge) return g From 9e5396562a1585b3c4e841be3614cd10ea251fb8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 2 Jul 2021 11:46:33 +0200 Subject: [PATCH 144/336] fix 0/0 in ore function field --- src/sage/rings/polynomial/ore_function_element.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/ore_function_element.py b/src/sage/rings/polynomial/ore_function_element.py index 97330acc180..2a3bad34c77 100644 --- a/src/sage/rings/polynomial/ore_function_element.py +++ b/src/sage/rings/polynomial/ore_function_element.py @@ -167,7 +167,7 @@ def _richcmp_(self, other, op): sage: P = K.random_element() sage: Q = K.random_element() sage: D = K.random_element() - sage: (P*D) / (Q*D) == P/Q + sage: Q == 0 or D == 0 or (P*D) / (Q*D) == P/Q True """ @@ -549,7 +549,7 @@ def _div_(self, other): sage: f = K.random_element() sage: g = K.random_element() sage: h = K.random_element() - sage: f / (g / h) == f*h / g + sage: g == 0 or h == 0 or f / (g / h) == f*h / g True sage: 0/f @@ -558,11 +558,15 @@ def _div_(self, other): Traceback (most recent call last): ... ZeroDivisionError: cannot divide by zero + sage: K(0)/K(0) + Traceback (most recent call last): + ... + ZeroDivisionError: cannot divide by zero """ - if not self._numerator: - return self if not other._numerator: raise ZeroDivisionError("cannot divide by zero") + if not self._numerator: + return self L, U, V = self._numerator.left_xlcm(other._numerator, monic=False) denominator = U * self._denominator numerator = V * other._denominator From 89c210e1d8fc7bb83c4c2ae501774531e67df463 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 3 Jul 2021 11:25:41 +0200 Subject: [PATCH 145/336] trac #32095: sorted is not inplace --- src/sage/graphs/base/boost_graph.pyx | 4 ++-- src/sage/graphs/digraph.py | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index c42063a9ba7..af1de86c552 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -2381,8 +2381,8 @@ cdef double diameter_DiFUB(BoostVecWeightedDiGraphU g_boost, # distances respectively. # Now order_1 and order_2 will contain order of vertices in which # further distance computations will be done. - sorted(order_1, reverse=True) - sorted(order_2, reverse=True) + order_1 = sorted(order_1, reverse=True) + order_2 = sorted(order_2, reverse=True) LB = max(LB, LB_1, LB_2) if LB == sys.float_info.max: diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 160795f5c4b..4812c659fa4 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -2490,6 +2490,15 @@ def diameter(self, by_weight=False, algorithm=None, weight_function=None, Traceback (most recent call last): ... ValueError: diameter is not defined for the empty DiGraph + + :trac:`32095` is fixed:: + + sage: set_random_seed(4) + sage: G = graphs.RandomGNP(40, 0.4).to_directed() + sage: G.diameter(algorithm='DiFUB', by_weight=False) + 3 + sage: G.diameter(algorithm='DiFUB', by_weight=True) + 3.0 """ if not self.order(): raise ValueError("diameter is not defined for the empty DiGraph") From 192b5646bcd0d9fdc6943eafe29d9cdbea14ef96 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 3 Jul 2021 11:29:20 +0200 Subject: [PATCH 146/336] add ticked number to test --- src/sage/rings/polynomial/ore_function_element.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/ore_function_element.py b/src/sage/rings/polynomial/ore_function_element.py index 2a3bad34c77..e9936f7996b 100644 --- a/src/sage/rings/polynomial/ore_function_element.py +++ b/src/sage/rings/polynomial/ore_function_element.py @@ -172,7 +172,7 @@ def _richcmp_(self, other, op): """ if self.parent()._simplification: - return richcmp((self._numerator, self._denominator), (other._numerator, other._denominator), op) + return richcmp((self._numerator, self._denominator), (other._numerator, other._denominator), op) if op == op_EQ or op == op_NE: _, U, V = self._denominator.left_xlcm(other._denominator) return richcmp(U * self._numerator, V * other._numerator, op) @@ -558,6 +558,9 @@ def _div_(self, other): Traceback (most recent call last): ... ZeroDivisionError: cannot divide by zero + + We check that :trac:`32109` is fixed:: + sage: K(0)/K(0) Traceback (most recent call last): ... From 3524d0f93f4007440cbb9744018e18b38f504dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Jul 2021 11:40:06 +0200 Subject: [PATCH 147/336] fix doctests --- src/doc/de/tutorial/latex.rst | 2 +- src/doc/en/tutorial/latex.rst | 2 +- src/doc/fr/tutorial/latex.rst | 2 +- src/doc/ja/tutorial/latex.rst | 2 +- src/doc/pt/tutorial/latex.rst | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/de/tutorial/latex.rst b/src/doc/de/tutorial/latex.rst index 7cf8e650784..ac186600cec 100644 --- a/src/doc/de/tutorial/latex.rst +++ b/src/doc/de/tutorial/latex.rst @@ -387,7 +387,7 @@ eingestellt sind). LaTeX example for testing display of a commutative diagram produced by xypic. - To use, try to view this object -- it won't work. Now try + To use, try to view this object -- it will not work. Now try 'latex.add_to_preamble("\\usepackage[matrix,arrow,curve,cmtip]{xy}")', and try viewing again. You should get a picture (a part of the diagram arising from a filtered chain complex). diff --git a/src/doc/en/tutorial/latex.rst b/src/doc/en/tutorial/latex.rst index 0f2e7e47cc1..1e2dadbbfa2 100644 --- a/src/doc/en/tutorial/latex.rst +++ b/src/doc/en/tutorial/latex.rst @@ -383,7 +383,7 @@ properly. To actually see the examples, it is necessary to use LaTeX example for testing display of a commutative diagram produced by xypic. - To use, try to view this object -- it won't work. Now try + To use, try to view this object -- it will not work. Now try 'latex.add_to_preamble("\\usepackage[matrix,arrow,curve,cmtip]{xy}")', and try viewing again. You should get a picture (a part of the diagram arising from a filtered chain complex). diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst index 4ec9426700b..542c557ac17 100644 --- a/src/doc/fr/tutorial/latex.rst +++ b/src/doc/fr/tutorial/latex.rst @@ -367,7 +367,7 @@ la commande ``view()`` pour visualiser l'exemple. :: LaTeX example for testing display of a commutative diagram produced by xypic. - To use, try to view this object -- it won't work. Now try + To use, try to view this object -- it will not work. Now try 'latex.add_to_preamble("\\usepackage[matrix,arrow,curve,cmtip]{xy}")', and try viewing again. You should get a picture (a part of the diagram arising from a filtered chain complex). diff --git a/src/doc/ja/tutorial/latex.rst b/src/doc/ja/tutorial/latex.rst index c737dbf0146..11d8e2910c8 100644 --- a/src/doc/ja/tutorial/latex.rst +++ b/src/doc/ja/tutorial/latex.rst @@ -329,7 +329,7 @@ LaTeX表式とLaTeXエンジンの生成するdvi形式にdvipngが扱えないs LaTeX example for testing display of a commutative diagram produced by xypic. - To use, try to view this object -- it won't work. Now try + To use, try to view this object -- it will not work. Now try 'latex.add_to_preamble("\\usepackage[matrix,arrow,curve,cmtip]{xy}")', and try viewing again. You should get a picture (a part of the diagram arising from a filtered chain complex). diff --git a/src/doc/pt/tutorial/latex.rst b/src/doc/pt/tutorial/latex.rst index cba4fd4344e..fe84b74027a 100644 --- a/src/doc/pt/tutorial/latex.rst +++ b/src/doc/pt/tutorial/latex.rst @@ -395,7 +395,7 @@ processador, etc. estão configurados corretamente). LaTeX example for testing display of a commutative diagram produced by xypic. - To use, try to view this object -- it won't work. Now try + To use, try to view this object -- it will not work. Now try 'latex.add_to_preamble("\\usepackage[matrix,arrow,curve,cmtip]{xy}")', and try viewing again. You should get a picture (a part of the diagram arising from a filtered chain complex). From 1d258966e84e3339245e31c0eb286f9cee6a6f35 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 3 Jul 2021 12:20:11 +0200 Subject: [PATCH 148/336] trac #32095: avoid using set_random_seed --- src/sage/graphs/digraph.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 4812c659fa4..8d6c3891292 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -2493,8 +2493,10 @@ def diameter(self, by_weight=False, algorithm=None, weight_function=None, :trac:`32095` is fixed:: - sage: set_random_seed(4) - sage: G = graphs.RandomGNP(40, 0.4).to_directed() + sage: g6 = 'guQOUOQCW[IaDBCVP_IE\\RfxV@WMSaeHgheEIA@tfOJkB~@EpGLCrs' + sage: g6 += 'aPIpwgQI_`Abs_x?VWxNJAo@w\\hffCDAW]bYGMIZGC_PYOrIw[Gp[' + sage: g6 += '@FTgc_O}E?fXAnGCB{gSaUcD' + sage: G = Graph(g6).to_directed() sage: G.diameter(algorithm='DiFUB', by_weight=False) 3 sage: G.diameter(algorithm='DiFUB', by_weight=True) From 69486615701328f0268a8fc6850e014e69d16837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 3 Jul 2021 13:02:30 +0200 Subject: [PATCH 149/336] full flake8 for typeset/ folder --- src/sage/typeset/character_art.py | 7 ++++--- src/sage/typeset/symbols.py | 11 ++++------- src/sage/typeset/unicode_art.py | 4 +--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/sage/typeset/character_art.py b/src/sage/typeset/character_art.py index f02ac0ff408..a451fbb73b0 100644 --- a/src/sage/typeset/character_art.py +++ b/src/sage/typeset/character_art.py @@ -167,9 +167,7 @@ def __format__(self, fmt): sage: M = matrix([[1,2],[3,4]]) sage: format(ascii_art(M)) '[1 2]\n[3 4]' - sage: format(unicode_art(M)) # py2 - u'\u239b1 2\u239e\n\u239d3 4\u23a0' - sage: format(unicode_art(M)) # py3 + sage: format(unicode_art(M)) '\u239b1 2\u239e\n\u239d3 4\u23a0' """ return format(self._string_type(self), fmt) @@ -315,10 +313,13 @@ class PrependIterator(): """ def __init__(self, stack): self._stack = [iter(elems) for elems in stack] + def prepend(self, elems): self._stack.append(iter(elems)) + def __iter__(self): return self + def __next__(self): while self._stack: try: diff --git a/src/sage/typeset/symbols.py b/src/sage/typeset/symbols.py index e68f77a1923..c4f0831b8d7 100644 --- a/src/sage/typeset/symbols.py +++ b/src/sage/typeset/symbols.py @@ -154,11 +154,11 @@ def __call__(self, num_lines): return [self.top_2, self.bottom_2] elif num_lines == 3: return [self.top, self.middle, self.bottom] - elif num_lines %2 == 0: - ext = [self.extension] * ((num_lines-4) // 2) + elif num_lines % 2 == 0: + ext = [self.extension] * ((num_lines - 4) // 2) return [self.top] + ext + [self.middle_top, self.middle_bottom] + ext + [self.bottom] - else: # num_lines %2 == 1 - ext = [self.extension] * ((num_lines-3) // 2) + else: # num_lines %2 == 1 + ext = [self.extension] * ((num_lines - 3) // 2) return [self.top] + ext + [self.middle] + ext + [self.bottom] def print_to_stdout(self, num_lines): @@ -335,6 +335,3 @@ def character_art(self, num_lines): unicodedata.lookup('UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION'), unicodedata.lookup('UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION'), ) - - - diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 9d9ad41ade9..03dd57a9965 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -59,9 +59,7 @@ def __unicode__(self): sage: i = var('i') sage: ua = unicode_art(sum(pi^i/factorial(i)*x^i, i, 0, oo)) - sage: unicode(ua) # py2 - u' \u03c0\u22c5x\n\u212f ' - sage: str(ua) # py3 + sage: str(ua) ' \u03c0\u22c5x\n\u212f ' """ return repr(self).decode("utf-8") From 4e316e9f4e3e714192568594197afb34cfd8121f Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Sat, 3 Jul 2021 16:48:19 +0200 Subject: [PATCH 150/336] Fix bug in Chart.__init__ regarding the determination of top charts (Trac #32112) --- src/sage/manifolds/chart.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 1994182cb71..d0d1bf15db3 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -277,6 +277,16 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): [] sage: TestSuite(X).run() + Check that :trac:`32112` has been fixed:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = M.open_subset('V') + sage: XU = U.chart('x y') + sage: XV = V.chart('x y') + sage: M.top_charts() + [Chart (U, (x, y)), Chart (V, (x, y))] + """ from sage.manifolds.manifold import TopologicalManifold if not isinstance(domain, TopologicalManifold): @@ -321,10 +331,11 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): # atlases of the domain's supersets; moreover the first defined chart # is considered as the default chart for sd in self._domain.open_supersets(): - # the chart is added in the top charts only if its coordinates have - # not been used: + # the chart is added in the top charts iff its coordinates have + # not been used on a domain including the chart's domain: for chart in sd._atlas: - if self._xx == chart._xx: + if (self._domain.is_subset(chart._domain) + and self._xx == chart._xx): break else: sd._top_charts.append(self) From d9520ae9989dfdccefdf3f20f45389cb4f632dc5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 10:58:08 -0700 Subject: [PATCH 151/336] Chart: Add coord_restrictions init arg --- src/sage/manifolds/chart.py | 14 +++++++++++--- src/sage/manifolds/differentiable/chart.py | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 1994182cb71..30a9d193eba 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -261,7 +261,15 @@ class Chart(UniqueRepresentation, SageObject): manifolds over `\RR`. """ - def __init__(self, domain, coordinates='', names=None, calc_method=None): + @staticmethod + def __classcall__(cls, domain, coordinates='', names=None, calc_method=None, + coord_restrictions=None): + + return super().__classcall__(cls, domain, coordinates, names, calc_method, + coord_restrictions) + + + def __init__(self, domain, coordinates, names, calc_method, coord_restrictions): r""" Construct a chart. @@ -1576,7 +1584,7 @@ class RealChart(Chart): :meth:`plot`. """ - def __init__(self, domain, coordinates='', names=None, calc_method=None): + def __init__(self, domain, coordinates, names, calc_method, coord_restrictions): r""" Construct a chart on a real topological manifold. @@ -1595,7 +1603,7 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): """ Chart.__init__(self, domain, coordinates=coordinates, names=names, - calc_method=calc_method) + calc_method=calc_method, coord_restrictions=coord_restrictions) self._fast_valid_coordinates = None def _init_coordinates(self, coord_list): diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 0e0ec6205c8..00290d933d9 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -259,7 +259,7 @@ class DiffChart(Chart): on differentiable manifolds over `\RR`. """ - def __init__(self, domain, coordinates='', names=None, calc_method=None): + def __init__(self, domain, coordinates, names, calc_method, coord_restrictions): r""" Construct a chart. @@ -942,7 +942,7 @@ class RealDiffChart(DiffChart, RealChart): :meth:`~sage.manifolds.chart.RealChart.plot`. """ - def __init__(self, domain, coordinates='', names=None, calc_method=None): + def __init__(self, domain, coordinates, names, calc_method, coord_restrictions): r""" Construct a chart on a real differentiable manifold. From c49fddb79e85ec08ae9007c9eb76dba8bfd45f6c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 11:01:51 -0700 Subject: [PATCH 152/336] sage.structure.misc: Remove --- src/doc/en/reference/structure/index.rst | 1 - src/sage/structure/misc.pyx | 37 ------------------------ 2 files changed, 38 deletions(-) delete mode 100644 src/sage/structure/misc.pyx diff --git a/src/doc/en/reference/structure/index.rst b/src/doc/en/reference/structure/index.rst index a24b48183a6..0e74c143bf5 100644 --- a/src/doc/en/reference/structure/index.rst +++ b/src/doc/en/reference/structure/index.rst @@ -89,7 +89,6 @@ Internals sage/structure/debug_options sage/structure/list_clone_timings sage/structure/list_clone_timings_cy - sage/structure/misc sage/structure/test_factory .. include:: ../footer.txt diff --git a/src/sage/structure/misc.pyx b/src/sage/structure/misc.pyx deleted file mode 100644 index cd402266178..00000000000 --- a/src/sage/structure/misc.pyx +++ /dev/null @@ -1,37 +0,0 @@ -""" -Miscellaneous utilities -""" - -from sage.misc.superseded import deprecation -deprecation(27099, "the module sage.structure.misc is deprecated") - -def is_extension_type(cls): - """ - Return whether or not instances of ``cls`` have a ``__dict__``. - - This is deprecated as there should not be any use case for it. - - INPUT: - - - ``cls`` -- a class - - EXAMPLES:: - - sage: from sage.structure.misc import is_extension_type - doctest:...: DeprecationWarning: the module sage.structure.misc is deprecated - See https://trac.sagemath.org/27099 for details. - sage: is_extension_type(int) - True - sage: is_extension_type(list) - True - sage: is_extension_type(ZZ.__class__) - True - sage: is_extension_type(QQ.__class__) - False - """ - # Robert B claims that this should be robust - try: - return cls.__dictoffset__ == 0 - except AttributeError: - pass - return False From deace8334e03bb4f722c5fa50787da723567be03 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 12:33:50 -0700 Subject: [PATCH 153/336] Chart: Replace _init_coordinates by _parse_coordinates --- src/sage/manifolds/chart.py | 147 +++++++++++---------- src/sage/manifolds/differentiable/chart.py | 12 +- 2 files changed, 86 insertions(+), 73 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 5573139ba55..466bb996ee6 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -261,7 +261,31 @@ class Chart(UniqueRepresentation, SageObject): manifolds over `\RR`. """ - def __init__(self, domain, coordinates='', names=None, calc_method=None): + + def __classcall__(cls, domain, coordinates='', + calc_method=None, coordinate_options=None, + names=None): + """ + Implement UniqueRepresentation behavior + """ + if isinstance(coordinates, str): + if coordinates == '': + for x in names: + coordinates += x + ' ' + coordinates = coordinates[:-1] + coordinates, coordinate_options = cls._parse_coordinates(domain, coordinates) + + coord_string = ' '.join(str(x) for x in coordinates) + + try: + return domain._charts_by_coord[coord_string] + except KeyError: + self = super().__classcall__(cls, domain, coordinates, + calc_method, coordinate_options) + domain._charts_by_coord[coord_string] = self + return self + + def __init__(self, domain, coordinates, calc_method, periods=None): r""" Construct a chart. @@ -273,7 +297,7 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): Chart (M, (x, y)) sage: type(X) - sage: assumptions() # no assumptions on x,y set by X._init_coordinates + sage: assumptions() # no assumptions on x,y set [] sage: TestSuite(X).run() @@ -282,10 +306,6 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): if not isinstance(domain, TopologicalManifold): raise TypeError("the first argument must be an open subset of " + "a topological manifold") - if coordinates == '': - for x in names: - coordinates += x + ' ' - coordinates = coordinates[:-1] self._manifold = domain.manifold() self._domain = domain self._sindex = self._manifold.start_index() @@ -295,24 +315,15 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): self.simplify = self._calc_method.simplify # Treatment of the coordinates: - self._periods = {} # dict. of periods (if any); key = coord. index - if ' ' in coordinates: - coord_list = coordinates.split() + if periods is None: + self._periods = {} else: - coord_list = [coordinates] - if len(coord_list) != self._manifold.dim(): + self._periods = periods # dict. of periods (if any); key = coord. index + + if len(coordinates) != self._manifold.dim(): raise ValueError("the list of coordinates must contain " + "{} elements".format(self._manifold.dim())) - # The treatment of coordinates is performed by a separate method, - # _init_coordinates, which sets self._xx and - # which may be redefined for subclasses (for instance RealChart). - self._init_coordinates(coord_list) - coord_string = ' '.join(str(x) for x in self._xx) - if coord_string in self._domain._charts_by_coord: - raise ValueError("the chart with coordinates " + coord_string + - " has already been declared on " + - "the {}".format(self._domain)) - self._domain._charts_by_coord[coord_string] = self + self._xx = coordinates # # Additional restrictions on the coordinates self._restrictions = [] # to be set with method add_restrictions() @@ -350,34 +361,40 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): dom._zero_scalar_field._express[self] = self.function_ring().zero() dom._one_scalar_field._express[self] = self.function_ring().one() - def _init_coordinates(self, coord_list): + @classmethod + def _parse_coordinates(cls, domain, coordinates): r""" Initialization of the coordinates as symbolic variables. - This method must be redefined by derived classes in order to take - into account specificities (e.g. enforcing real coordinates). - INPUT: - - ``coord_list`` -- list of coordinate fields, which items in each - field separated by ":"; there are at most 2 items per field: - the coordinate name and the coordinate LaTeX symbol + - ``coord_list`` -- list (or space-separated concatenation) of + coordinate fields. Each field is a string of at most 3 items, + separated by ":". These items are: the coordinate symbol, the + (optional) indicator of the periodic character of the + coordinate, and the (optional) coordinate LaTeX symbol + + OUTPUT: + + - a tuple of variables (as elements of ``SR``) + - a dictionary with possible keys: + - `"periods": a dictionary with keys = coordinate indices + and values = periods TESTS:: + sage: from sage.manifolds.chart import Chart sage: M = Manifold(2, 'M', field='complex', structure='topological') - sage: X. = M.chart() - sage: X._init_coordinates(['z1', 'z2']) - sage: X - Chart (M, (z1, z2)) - sage: X._init_coordinates([r'z1:\zeta_1', r'z2:\zeta_2']) - sage: X - Chart (M, (z1, z2)) - sage: latex(X) - \left(M,({\zeta_1}, {\zeta_2})\right) - + sage: Chart._parse_coordinates(M, ['z1', 'z2']) + sage: Chart._parse_coordinates(M, 'z1 z2') + sage: Chart._parse_coordinates(M, ['z1:\zeta_1', r'z2:\zeta_2']) """ + if isinstance(coordinates, str): + coord_list = coordinates.split() + else: + coord_list = coordinates xx_list = [] # will contain the coordinates as Sage symbolic variables + periods = {} for coord_index, coord_field in enumerate(coord_list): coord_properties = coord_field.split(':') coord_symb = coord_properties[0].strip() # the coordinate symbol @@ -390,14 +407,14 @@ def _init_coordinates(self, coord_list): period = SR(prop1[7:]) else: period = domain.base_field()(prop1[7:]) - self._periods[coord_index + self._sindex] = period + periods[coord_index + domain._sindex] = period else: # prop1 is the coordinate's LaTeX symbol coord_latex = prop1 # Construction of the coordinate as a Sage symbolic variable: coord_var = SR.var(coord_symb, latex_name=coord_latex) xx_list.append(coord_var) - self._xx = tuple(xx_list) + return tuple(xx_list), dict(periods=periods) def _repr_(self): r""" @@ -1576,7 +1593,7 @@ class RealChart(Chart): :meth:`plot`. """ - def __init__(self, domain, coordinates='', names=None, calc_method=None): + def __init__(self, domain, coordinates, calc_method=None, bounds=None, periods=None): r""" Construct a chart on a real topological manifold. @@ -1594,11 +1611,13 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): sage: TestSuite(X).run() """ - Chart.__init__(self, domain, coordinates=coordinates, names=names, - calc_method=calc_method) + super().__init__(domain, coordinates, calc_method=calc_method) + self._bounds = bounds + self._periods = periods self._fast_valid_coordinates = None - def _init_coordinates(self, coord_list): + @classmethod + def _parse_coordinates(cls, domain, coordinates): r""" Initialization of the coordinates as symbolic variables. @@ -1607,34 +1626,28 @@ def _init_coordinates(self, coord_list): INPUT: - - ``coord_list`` -- list of coordinate fields, which items in each - field separated by ":"; there are at most 3 items per field: - the coordinate name, the coordinate LaTeX symbol and the - coordinate range + - ``coord_list`` -- list (or space-separated concatenation) of + coordinate fields. Each field is a string of at most 3 items, + separated by ":". These items are: the coordinate symbol, the + (optional) coordinate range or indicator of the periodic + character of the coordinate, and the (optional) coordinate + LaTeX symbol TESTS:: + sage: from sage.manifolds.chart import RealChart sage: M = Manifold(2, 'M', structure='topological') - sage: X. = M.chart() - sage: X._init_coordinates(['x', 'y']) - sage: X - Chart (M, (x, y)) - sage: latex(X) - \left(M,(x, y)\right) - sage: X.coord_range() - x: (-oo, +oo); y: (-oo, +oo) - sage: X._init_coordinates([r'x1:\xi:(0,1)', r'y1:\eta']) - sage: X - Chart (M, (x1, y1)) - sage: latex(X) - \left(M,({\xi}, {\eta})\right) - sage: X.coord_range() - x1: (0, 1); y1: (-oo, +oo) - + sage: RealChart._parse_coordinates(M, ['x', 'y']) + sage: RealChart._parse_coordinates(M, [r'x1:\xi:(0,1)', r'y1:\eta']) """ from sage.symbolic.assumptions import assume + if isinstance(coordinates, str): + coord_list = coordinates.split() + else: + coord_list = coordinates xx_list = [] # will contain the coordinates as Sage symbolic variables bounds_list = [] # will contain the coordinate bounds + periods = {} for coord_index, coord_field in enumerate(coord_list): coord_properties = coord_field.split(':') coord_symb = coord_properties[0].strip() # the coordinate symbol @@ -1676,7 +1689,7 @@ def _init_coordinates(self, coord_list): latex_name=coord_latex) assume(coord_var, 'real') if is_periodic: - self._periods[coord_index + self._sindex] = xmax - xmin + periods[coord_index + domain._sindex] = xmax - xmin xmin_included = 'periodic' xmax_included = 'periodic' else: @@ -1692,8 +1705,8 @@ def _init_coordinates(self, coord_list): assume(coord_var < xmax) xx_list.append(coord_var) bounds_list.append(((xmin, xmin_included), (xmax, xmax_included))) - self._xx = tuple(xx_list) - self._bounds = tuple(bounds_list) + return tuple(xx_list), dict(bounds=tuple(bounds_list), + periods=periods) def coord_bounds(self, i=None): r""" diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 8584d8c19a0..91eaa9d7115 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -259,7 +259,7 @@ class DiffChart(Chart): on differentiable manifolds over `\RR`. """ - def __init__(self, domain, coordinates='', names=None, calc_method=None): + def __init__(self, domain, coordinates, calc_method=None, periods=None): r""" Construct a chart. @@ -276,8 +276,8 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): sage: TestSuite(X).run() """ - Chart.__init__(self, domain, coordinates=coordinates, names=names, - calc_method=calc_method) + super().__init__(domain, coordinates, + calc_method=calc_method, periods=periods) # Construction of the coordinate frame associated to the chart: self._frame = CoordFrame(self) self._coframe = self._frame._coframe @@ -942,7 +942,7 @@ class RealDiffChart(DiffChart, RealChart): :meth:`~sage.manifolds.chart.RealChart.plot`. """ - def __init__(self, domain, coordinates='', names=None, calc_method=None): + def __init__(self, domain, coordinates, calc_method=None, bounds=None, periods=None): r""" Construct a chart on a real differentiable manifold. @@ -960,8 +960,8 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None): sage: TestSuite(X).run() """ - RealChart.__init__(self, domain, coordinates=coordinates, names=names, - calc_method = calc_method) + RealChart.__init__(self, domain, coordinates, + calc_method=calc_method, bounds=bounds, periods=periods) # Construction of the coordinate frame associated to the chart: self._frame = CoordFrame(self) self._coframe = self._frame._coframe From 4db499543d24006d4ea3b87c956baa5f0bee03a7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 12:51:26 -0700 Subject: [PATCH 154/336] Chart: Fix up __classcall__ and _parse_coordinates by avoiding unhashable things --- src/sage/manifolds/chart.py | 42 ++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 466bb996ee6..f53ca0d1fd9 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -263,8 +263,7 @@ class Chart(UniqueRepresentation, SageObject): """ def __classcall__(cls, domain, coordinates='', - calc_method=None, coordinate_options=None, - names=None): + calc_method=None, names=None, **coordinate_options): """ Implement UniqueRepresentation behavior """ @@ -281,7 +280,7 @@ def __classcall__(cls, domain, coordinates='', return domain._charts_by_coord[coord_string] except KeyError: self = super().__classcall__(cls, domain, coordinates, - calc_method, coordinate_options) + calc_method, **coordinate_options) domain._charts_by_coord[coord_string] = self return self @@ -318,7 +317,10 @@ def __init__(self, domain, coordinates, calc_method, periods=None): if periods is None: self._periods = {} else: - self._periods = periods # dict. of periods (if any); key = coord. index + # dictionary of periods (if any); key = coord. index + self._periods = {self._sindex + i: period + for i, period in enumerate(periods) + if period is not None} if len(coordinates) != self._manifold.dim(): raise ValueError("the list of coordinates must contain " + @@ -378,27 +380,30 @@ def _parse_coordinates(cls, domain, coordinates): - a tuple of variables (as elements of ``SR``) - a dictionary with possible keys: - - `"periods": a dictionary with keys = coordinate indices - and values = periods + - `"periods": a tuple of periods TESTS:: sage: from sage.manifolds.chart import Chart sage: M = Manifold(2, 'M', field='complex', structure='topological') sage: Chart._parse_coordinates(M, ['z1', 'z2']) + ((z1, z2), {'periods': (None, None)}) sage: Chart._parse_coordinates(M, 'z1 z2') + ((z1, z2), {'periods': (None, None)}) sage: Chart._parse_coordinates(M, ['z1:\zeta_1', r'z2:\zeta_2']) + ((z1, z2), {'periods': (None, None)}) """ if isinstance(coordinates, str): coord_list = coordinates.split() else: coord_list = coordinates xx_list = [] # will contain the coordinates as Sage symbolic variables - periods = {} + period_list = [] for coord_index, coord_field in enumerate(coord_list): coord_properties = coord_field.split(':') coord_symb = coord_properties[0].strip() # the coordinate symbol coord_latex = None # possibly redefined below + period = None # possibly redefined below # scan of the properties other than the symbol: for prop in coord_properties[1:]: prop1 = prop.strip() @@ -407,14 +412,14 @@ def _parse_coordinates(cls, domain, coordinates): period = SR(prop1[7:]) else: period = domain.base_field()(prop1[7:]) - periods[coord_index + domain._sindex] = period else: # prop1 is the coordinate's LaTeX symbol coord_latex = prop1 # Construction of the coordinate as a Sage symbolic variable: coord_var = SR.var(coord_symb, latex_name=coord_latex) xx_list.append(coord_var) - return tuple(xx_list), dict(periods=periods) + period_list.append(period) + return tuple(xx_list), dict(periods=tuple(period_list)) def _repr_(self): r""" @@ -1611,9 +1616,8 @@ def __init__(self, domain, coordinates, calc_method=None, bounds=None, periods=N sage: TestSuite(X).run() """ - super().__init__(domain, coordinates, calc_method=calc_method) + super().__init__(domain, coordinates, calc_method=calc_method, periods=periods) self._bounds = bounds - self._periods = periods self._fast_valid_coordinates = None @classmethod @@ -1638,7 +1642,15 @@ def _parse_coordinates(cls, domain, coordinates): sage: from sage.manifolds.chart import RealChart sage: M = Manifold(2, 'M', structure='topological') sage: RealChart._parse_coordinates(M, ['x', 'y']) + ((x, y), + {'bounds': (((-Infinity, False), (+Infinity, False)), + ((-Infinity, False), (+Infinity, False))), + 'periods': (None, None)}) sage: RealChart._parse_coordinates(M, [r'x1:\xi:(0,1)', r'y1:\eta']) + ((x1, y1), + {'bounds': (((0, False), (1, False)), + ((-Infinity, False), (+Infinity, False))), + 'periods': (None, None)}) """ from sage.symbolic.assumptions import assume if isinstance(coordinates, str): @@ -1647,7 +1659,7 @@ def _parse_coordinates(cls, domain, coordinates): coord_list = coordinates xx_list = [] # will contain the coordinates as Sage symbolic variables bounds_list = [] # will contain the coordinate bounds - periods = {} + period_list = [] for coord_index, coord_field in enumerate(coord_list): coord_properties = coord_field.split(':') coord_symb = coord_properties[0].strip() # the coordinate symbol @@ -1657,6 +1669,7 @@ def _parse_coordinates(cls, domain, coordinates): xmin_included = False xmax = +Infinity xmax_included = False + period = None # scan of the properties other than the symbol: is_periodic = False for prop in coord_properties[1:]: @@ -1689,7 +1702,7 @@ def _parse_coordinates(cls, domain, coordinates): latex_name=coord_latex) assume(coord_var, 'real') if is_periodic: - periods[coord_index + domain._sindex] = xmax - xmin + period = xmax - xmin xmin_included = 'periodic' xmax_included = 'periodic' else: @@ -1705,8 +1718,9 @@ def _parse_coordinates(cls, domain, coordinates): assume(coord_var < xmax) xx_list.append(coord_var) bounds_list.append(((xmin, xmin_included), (xmax, xmax_included))) + period_list.append(period) return tuple(xx_list), dict(bounds=tuple(bounds_list), - periods=periods) + periods=tuple(period_list)) def coord_bounds(self, i=None): r""" From fc59c9d6d46faeefd12626ab9946631de2608424 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 13:01:21 -0700 Subject: [PATCH 155/336] Chart.__classcall__: Add doctest --- src/sage/manifolds/chart.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index f53ca0d1fd9..6c9f50a3b90 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -264,8 +264,17 @@ class Chart(UniqueRepresentation, SageObject): def __classcall__(cls, domain, coordinates='', calc_method=None, names=None, **coordinate_options): - """ - Implement UniqueRepresentation behavior + r""" + Normalize init args and implement unique representation behavior. + + TESTS:: + + sage: from sage.manifolds.chart import Chart + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: var("u v") + (u, v) + sage: Chart(M, (u, v)) is Chart(M, "u v") + True """ if isinstance(coordinates, str): if coordinates == '': From cd056b2023da6a3d240979631abb1482fd106b29 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 3 Jul 2021 22:51:40 +0200 Subject: [PATCH 156/336] check reducibility of polynomial with correct base ring --- src/sage/rings/tests.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/tests.py b/src/sage/rings/tests.py index dba88216b8a..d0209bee5a8 100644 --- a/src/sage/rings/tests.py +++ b/src/sage/rings/tests.py @@ -122,21 +122,31 @@ def relative_number_field(n=2, maxdeg=2): sage: import sage.rings.tests sage: sage.rings.tests.relative_number_field(3) Number Field in aaa with defining polynomial x^2 - 79*x - 53 over its base field + + TESTS: + + Check that :trac:`32117` is fixed:: + + sage: set_random_seed(3030) + sage: from sage.rings.tests import relative_number_field + sage: _ = relative_number_field(3) """ from sage.all import ZZ K = absolute_number_field(maxdeg) n -= 1 var = 'aa' R = ZZ['x'] + R1 = K['x'] while n >= 1: while True: f = R.random_element(degree=ZZ.random_element(x=1,y=maxdeg),x=-100,y=100) if f.degree() <= 0: continue f = f * f.denominator() # bug trac #4781 f = f + R.gen()**maxdeg # make monic - if f.is_irreducible(): + if R1(f).is_irreducible(): break K = K.extension(f,var) + R1 = K['x'] var += 'a' n -= 1 return K From c31fb4baf8cefdff7a9864e774941caae697e603 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 13:54:35 -0700 Subject: [PATCH 157/336] Chart: Make coord restrictions hashable by using frozenset in place of list --- src/sage/manifolds/chart.py | 60 ++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 22af63c1385..c6eb1863493 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -264,7 +264,8 @@ class Chart(UniqueRepresentation, SageObject): @staticmethod def __classcall__(cls, domain, coordinates='', - calc_method=None, names=None, **coordinate_options): + calc_method=None, names=None, + coord_restrictions=None, **coordinate_options): r""" Normalize init args and implement unique representation behavior. @@ -289,8 +290,11 @@ def __classcall__(cls, domain, coordinates='', try: return domain._charts_by_coord[coord_string] except KeyError: - self = super().__classcall__(cls, domain, coordinates, - calc_method, **coordinate_options) + # Make coord_restrictions hashable + coord_restrictions = cls._normalize_coord_restrictions(coord_restrictions) + self = super().__classcall__(cls, domain, coordinates, calc_method, + coord_restrictions=coord_restrictions, + **coordinate_options) domain._charts_by_coord[coord_string] = self return self @@ -338,7 +342,7 @@ def __init__(self, domain, coordinates, calc_method, periods=None, coord_restric self._xx = coordinates # # Additional restrictions on the coordinates - self._restrictions = [] # to be set with method add_restrictions() + self._restrictions = list(coord_restrictions) # # The chart is added to the domain's atlas, as well as to all the # atlases of the domain's supersets; moreover the first defined chart @@ -431,6 +435,45 @@ def _parse_coordinates(cls, domain, coordinates): period_list.append(period) return tuple(xx_list), dict(periods=tuple(period_list)) + @staticmethod + def _normalize_coord_restrictions(restrictions): + r""" + Rewrite ``restrictions`` as a ``frozenset``, representing a logical "and", of other clauses. + + Also replace ``list``s by ``frozenset``s, making the result hashable. + + EXAMPLES:: + + sage: from sage.manifolds.chart import Chart + sage: var("x y z") + (x, y, z) + sage: Chart._normalize_coord_restrictions(None) + frozenset() + sage: Chart._normalize_coord_restrictions(x > y) + frozenset({x > y}) + sage: Chart._normalize_coord_restrictions((x != 0, y != 0)) + frozenset({(x != 0, y != 0)}) + sage: Chart._normalize_coord_restrictions([x > y, (x != 0, y != 0), z^2 < x]) + frozenset({(x != 0, y != 0), x > y, z^2 < x}) + + """ + def normalize(r): + if isinstance(r, tuple): # or + return tuple(normalize(x) for x in r) + elif isinstance(r, (list, set, frozenset)): # and + return frozenset(normalize(x) for x in r) + else: + return r + + if restrictions is None: + return frozenset() + + if not isinstance(restrictions, (list, set, frozenset)): + # case of a single condition or conditions to be combined by "or" + restrictions = [restrictions] + + return normalize(restrictions) + def _repr_(self): r""" String representation of the object. @@ -690,10 +733,7 @@ def add_restrictions(self, restrictions): False """ - if not isinstance(restrictions, list): - # case of a single condition or conditions to be combined by "or" - restrictions = [restrictions] - self._restrictions.extend(restrictions) + self._restrictions.extend(self._normalize_coord_restrictions(restrictions)) def restrict(self, subset, restrictions=None): r""" @@ -877,7 +917,7 @@ def _check_restrictions(self, restrict, substitutions): combine = combine or self._check_restrictions(cond, substitutions) return combine - elif isinstance(restrict, list): # case of 'and' conditions + elif isinstance(restrict, (list, set, frozenset)): # case of 'and' conditions combine = True for cond in restrict: combine = combine and self._check_restrictions(cond, @@ -2256,7 +2296,7 @@ def valid_coordinates_numerical(self, *coordinates): # case fast callable has to be computed from operator import lt, gt - if not isinstance(self._restrictions, list): + if not isinstance(self._restrictions, (list, set, frozenset)): if isinstance(self._restrictions, tuple): self._restrictions = [self._restrictions] elif isinstance(self._restrictions, Expression): From 91c4c98dd8173f9d7255eda3b246c3100cc984d4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 14:27:43 -0700 Subject: [PATCH 158/336] TopologicalManifold.chart: Add keyword arg coord_restrictions --- src/sage/manifolds/manifold.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 9c8dbe727a8..64949efea1a 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -1429,7 +1429,8 @@ def is_manifestly_coordinate_domain(self): """ return bool(self._covering_charts) - def chart(self, coordinates='', names=None, calc_method=None): + def chart(self, coordinates='', names=None, calc_method=None, + coord_restrictions=None): r""" Define a chart, the domain of which is the manifold. @@ -1569,7 +1570,8 @@ def chart(self, coordinates='', names=None, calc_method=None): if calc_method is None: calc_method = self._calculus_method return self._structure.chart(self, coordinates=coordinates, - names=names, calc_method=calc_method) + names=names, calc_method=calc_method, + coord_restrictions=coord_restrictions) def is_open(self): """ From 30bc60dffdadae7db53364edd7f650d1a3c24d49 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 14:28:09 -0700 Subject: [PATCH 159/336] Chart: Handle lambda-quoted coord_restrictions --- src/sage/manifolds/chart.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index c6eb1863493..1e914fc2518 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -84,6 +84,22 @@ class Chart(UniqueRepresentation, SageObject): If no period and no LaTeX spelling are to be set for any coordinate, the argument ``coordinates`` can be omitted when the shortcut operator ``<,>`` is used to declare the chart (see examples below). + - ``coord_restrictions``: Additional restrictions on the coordinates. + A restriction can be any symbolic equality or inequality involving + the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items + of the list (or set or frozenset) ``restrictions`` are combined + with the ``and`` operator; if some restrictions are to be combined with + the ``or`` operator instead, they have to be passed as a tuple in some + single item of the list (or set or frozenset) ``restrictions``. For example:: + + coord_restrictions=[x > y, (x != 0, y != 0), z^2 < x] + + means ``(x > y) and ((x != 0) or (y != 0)) and (z^2 < x)``. + If the list ``restrictions`` contains only one item, this + item can be passed as such, i.e. writing ``x > y`` instead + of the single element list ``[x > y]``. If the chart variables have + not been declared as variables yet, ``coord_restrictions`` must + be ``lambda``-quoted. - ``names`` -- (default: ``None``) unused argument, except if ``coordinates`` is not provided; it must then be a tuple containing the coordinate symbols (this is guaranteed if the shortcut operator @@ -255,6 +271,15 @@ class Chart(UniqueRepresentation, SageObject): sage: X(p) == p.coord(X) True + Setting additional coordinate restrictions:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart(coord_restrictions=lambda x,y: abs(x) > 1) + sage: X.valid_coordinates(2+i, 1) + True + sage: X.valid_coordinates(i, 1) + False + .. SEEALSO:: :class:`sage.manifolds.chart.RealChart` for charts on topological @@ -290,6 +315,9 @@ def __classcall__(cls, domain, coordinates='', try: return domain._charts_by_coord[coord_string] except KeyError: + if callable(coord_restrictions): + # lambda-quoted + coord_restrictions = coord_restrictions(*coordinates) # Make coord_restrictions hashable coord_restrictions = cls._normalize_coord_restrictions(coord_restrictions) self = super().__classcall__(cls, domain, coordinates, calc_method, From b5820a9aabf558768caccfcbbfaec0f679cb5d26 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 14:48:24 -0700 Subject: [PATCH 160/336] Remove most uses of Chart.add_restrictions in doctests --- src/sage/manifolds/chart.py | 11 ++++------- src/sage/manifolds/continuous_map.py | 3 +-- src/sage/manifolds/continuous_map_image.py | 12 ++++-------- src/sage/manifolds/differentiable/chart.py | 6 +++--- .../differentiable/levi_civita_connection.py | 4 ++-- src/sage/manifolds/differentiable/manifold.py | 4 ++-- src/sage/manifolds/manifold.py | 4 ++-- src/sage/manifolds/topological_submanifold.py | 3 +-- 8 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 1e914fc2518..373a1505647 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -858,8 +858,7 @@ def valid_coordinates(self, *coordinates, **kwds): EXAMPLES:: sage: M = Manifold(2, 'M', field='complex', structure='topological') - sage: X. = M.chart() - sage: X.add_restrictions([abs(x)<1, y!=0]) + sage: X. = M.chart(coord_restrictions=lambda x,y: [abs(x)<1, y!=0]) sage: X.valid_coordinates(0, i) True sage: X.valid_coordinates(i, 1) @@ -876,8 +875,7 @@ def valid_coordinates(self, *coordinates, **kwds): sage: var('a') # the parameter is a symbolic variable a - sage: Y. = M.chart() - sage: Y.add_restrictions(abs(v) = M.chart(coord_restrictions=lambda u,v: abs(v) = U.chart() - sage: c_cartU.add_restrictions((y!=0, x<0)) + sage: c_cartU. = U.chart(coord_restrictions=lambda x,y,z: (y!=0, x<0)) sage: U.atlas() [Chart (U, (r, th, ph)), Chart (U, (x, y, z))] sage: M.atlas() @@ -1670,7 +1667,7 @@ class RealChart(Chart): Note that, as an example, the following would have meant `y \neq 0` *and* `x < 0`:: - c_cartU.add_restrictions([y!=0, x<0]) + c_cartU. = U.chart(coord_restrictions=lambda x,y,z: [y!=0, x<0]) Chart grids can be drawn in 2D or 3D graphics thanks to the method :meth:`plot`. diff --git a/src/sage/manifolds/continuous_map.py b/src/sage/manifolds/continuous_map.py index d1cf5049f76..eb7b264f117 100644 --- a/src/sage/manifolds/continuous_map.py +++ b/src/sage/manifolds/continuous_map.py @@ -837,8 +837,7 @@ def image(self, subset=None, inverse=None): sage: M = Manifold(2, 'M', structure="topological") sage: N = Manifold(1, 'N', ambient=M, structure="topological") sage: CM. = M.chart() - sage: CN. = N.chart() - sage: CN.add_restrictions([u > -1, u < 1]) + sage: CN. = N.chart(coord_restrictions=lambda u: [u > -1, u < 1]) sage: Phi = N.continuous_map(M, {(CN,CM): [u, u^2]}, name='Phi') sage: Phi.image() Image of the Continuous map Phi diff --git a/src/sage/manifolds/continuous_map_image.py b/src/sage/manifolds/continuous_map_image.py index 467124551d1..303117f7978 100644 --- a/src/sage/manifolds/continuous_map_image.py +++ b/src/sage/manifolds/continuous_map_image.py @@ -48,8 +48,7 @@ def __init__(self, map, inverse=None, name=None, latex_name=None, domain_subset= sage: M = Manifold(2, 'M', structure="topological") sage: N = Manifold(1, 'N', ambient=M, structure="topological") sage: CM. = M.chart() - sage: CN. = N.chart() - sage: CN.add_restrictions([u > -1, u < 1]) + sage: CN. = N.chart(coord_restrictions=lambda u: [u > -1, u < 1]) sage: Phi = N.continuous_map(M, {(CN,CM): [u, 1 + u^2]}, name='Phi') sage: Phi_inv = M.continuous_map(N, {(CM, CN): [x]}, name='Phi_inv') sage: Phi_N = Phi.image(inverse=Phi_inv) @@ -81,8 +80,7 @@ def _repr_(self): sage: M = Manifold(2, 'M', structure="topological") sage: N = Manifold(1, 'N', ambient=M, structure="topological") sage: CM. = M.chart() - sage: CN. = N.chart() - sage: CN.add_restrictions([u > -1, u < 1]) + sage: CN. = N.chart(coord_restrictions=lambda u: [u > -1, u < 1]) sage: Phi = N.continuous_map(M, {(CN,CM): [u, 1 + u^2]}, name='Phi') sage: Phi.image() # indirect doctest Image of the Continuous map Phi @@ -114,8 +112,7 @@ def _an_element_(self): sage: M = Manifold(2, 'M', structure="topological") sage: N = Manifold(1, 'N', ambient=M, structure="topological") sage: CM. = M.chart() - sage: CN. = N.chart() - sage: CN.add_restrictions([u > -1, u < 1]) + sage: CN. = N.chart(coord_restrictions=lambda u: [u > -1, u < 1]) sage: Phi = N.continuous_map(M, {(CN,CM): [u, 1 + u^2]}, name='Phi') sage: Phi_N = Phi.image() sage: p = Phi_N.an_element(); p # indirect doctest @@ -134,8 +131,7 @@ def __contains__(self, point): sage: M = Manifold(2, 'M', structure="topological") sage: N = Manifold(1, 'N', ambient=M, structure="topological") sage: CM. = M.chart() - sage: CN. = N.chart() - sage: CN.add_restrictions([u > -1, u < 1]) + sage: CN. = N.chart(coord_restrictions=lambda u: [u > -1, u < 1]) sage: Phi = N.continuous_map(M, {(CN,CM): [u, 1 + u^2]}, name='Phi') sage: Phi_inv = M.continuous_map(N, {(CM, CN): [x]}, name='Phi_inv') sage: Phi_N = Phi.image(inverse=Phi_inv) diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 803f8d7ea38..5466062424a 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -908,9 +908,9 @@ class RealDiffChart(DiffChart, RealChart): `\{y=0, x\geq 0\}`, we must have `y\not=0` or `x<0` on U. Accordingly, we set:: - sage: c_cartU. = U.chart() - sage: c_cartU.add_restrictions((y!=0, x<0)) # the tuple (y!=0, x<0) means y!=0 or x<0 - sage: # c_cartU.add_restrictions([y!=0, x<0]) would have meant y!=0 AND x<0 + sage: c_cartU. = U.chart(coord_restrictions=lambda x,y,z: (y!=0, x<0)) + ....: # the tuple (y!=0, x<0) means y!=0 or x<0 + ....: # [y!=0, x<0] would have meant y!=0 AND x<0 sage: U.atlas() [Chart (U, (r, th, ph)), Chart (U, (x, y, z))] sage: M.atlas() diff --git a/src/sage/manifolds/differentiable/levi_civita_connection.py b/src/sage/manifolds/differentiable/levi_civita_connection.py index 24851381732..0593398a478 100644 --- a/src/sage/manifolds/differentiable/levi_civita_connection.py +++ b/src/sage/manifolds/differentiable/levi_civita_connection.py @@ -628,8 +628,8 @@ def riemann(self, name=None, latex_name=None): metric of the hyperbolic plane (Poincaré disk model):: sage: M = Manifold(2, 'M', start_index=1) - sage: X. = M.chart('x:(-1,1) y:(-1,1)') # Cartesian coord. on the Poincaré disk - sage: X.add_restrictions(x^2+y^2<1) + sage: X. = M.chart('x:(-1,1) y:(-1,1)', coord_restrictions=lambda x,y: x^2+y^2<1) + ....: # Cartesian coord. on the Poincaré disk sage: g = M.metric('g') sage: g[1,1], g[2,2] = 4/(1-x^2-y^2)^2, 4/(1-x^2-y^2)^2 sage: nab = g.connection() diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index a6243f0ba65..37d4eeffa76 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -1017,8 +1017,8 @@ def diffeomorphism(self, codomain=None, coord_functions=None, chart1=None, sage: M = Manifold(2, 'M') # the open unit disk sage: forget() # for doctests only - sage: c_xy. = M.chart('x:(-1,1) y:(-1,1)') # Cartesian coord on M - sage: c_xy.add_restrictions(x^2+y^2<1) + sage: c_xy. = M.chart('x:(-1,1) y:(-1,1)', coord_restrictions=lambda x,y: x^2+y^2<1) + ....: # Cartesian coord on M sage: N = Manifold(2, 'N') # R^2 sage: c_XY. = N.chart() # canonical coordinates on R^2 sage: Phi = M.diffeomorphism(N, [x/sqrt(1-x^2-y^2), y/sqrt(1-x^2-y^2)], diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 64949efea1a..6dfa44f91d9 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -2349,8 +2349,8 @@ def homeomorphism(self, codomain, coord_functions=None, chart1=None, sage: forget() # for doctests only sage: M = Manifold(2, 'M', structure='topological') # the open unit disk - sage: c_xy. = M.chart('x:(-1,1) y:(-1,1)') # Cartesian coord on M - sage: c_xy.add_restrictions(x^2+y^2<1) + sage: c_xy. = M.chart('x:(-1,1) y:(-1,1)', coord_restrictions=lambda x,y: x^2+y^2<1) + ....: # Cartesian coord on M sage: N = Manifold(2, 'N', structure='topological') # R^2 sage: c_XY. = N.chart() # canonical coordinates on R^2 sage: Phi = M.homeomorphism(N, [x/sqrt(1-x^2-y^2), y/sqrt(1-x^2-y^2)], diff --git a/src/sage/manifolds/topological_submanifold.py b/src/sage/manifolds/topological_submanifold.py index d6942bca7db..fe39794324a 100644 --- a/src/sage/manifolds/topological_submanifold.py +++ b/src/sage/manifolds/topological_submanifold.py @@ -881,8 +881,7 @@ def as_subset(self): sage: M = Manifold(2, 'M', structure="topological") sage: N = Manifold(1, 'N', ambient=M, structure="topological") sage: CM. = M.chart() - sage: CN. = N.chart() - sage: CN.add_restrictions([u > -1, u < 1]) + sage: CN. = N.chart(coord_restrictions=lambda u: [u > -1, u < 1]) sage: phi = N.continuous_map(M, {(CN,CM): [u, u^2]}) sage: N.set_embedding(phi) sage: N From 1e482307cd15ce4f40cfdfd1d477db3693fd5ab0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 15:48:38 -0700 Subject: [PATCH 161/336] RealChart._tighten_bounds: Factor out from RealChart.add_restrictions, call it from __init__ too --- src/sage/manifolds/chart.py | 45 ++++++++++++++++++++++------------ src/sage/manifolds/manifold.py | 2 +- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 373a1505647..05a22f8e24c 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -315,7 +315,7 @@ def __classcall__(cls, domain, coordinates='', try: return domain._charts_by_coord[coord_string] except KeyError: - if callable(coord_restrictions): + if callable(coord_restrictions) and not isinstance(coord_restrictions, Expression): # lambda-quoted coord_restrictions = coord_restrictions(*coordinates) # Make coord_restrictions hashable @@ -370,7 +370,7 @@ def __init__(self, domain, coordinates, calc_method, periods=None, coord_restric self._xx = coordinates # # Additional restrictions on the coordinates - self._restrictions = list(coord_restrictions) + self._restrictions = sorted(coord_restrictions) # # The chart is added to the domain's atlas, as well as to all the # atlases of the domain's supersets; moreover the first defined chart @@ -1694,6 +1694,7 @@ def __init__(self, domain, coordinates, calc_method=None, bounds=None, periods=N super().__init__(domain, coordinates, calc_method=calc_method, periods=periods, coord_restrictions=coord_restrictions) self._bounds = bounds + self._tighten_bounds() self._fast_valid_coordinates = None @classmethod @@ -1990,7 +1991,7 @@ def add_restrictions(self, restrictions): sage: A = M.open_subset('A') # annulus 1/2 < r < 1 sage: X_A = X.restrict(A, x^2+y^2 > 1/4) sage: X_A._restrictions - [x^2 + y^2 < 1, x^2 + y^2 > (1/4)] + [x^2 + y^2 > (1/4), x^2 + y^2 < 1] sage: X_A.valid_coordinates(0,1/3) False sage: X_A.valid_coordinates(2/3,1/3) @@ -2007,20 +2008,32 @@ def add_restrictions(self, restrictions): sage: X_U.coord_range() x: (-oo, 0); y: (1/2, +oo) + """ + super().add_restrictions(restrictions) + self._tighten_bounds() + + def _tighten_bounds(self): + """ + Update coordinate bounds from the coordinate restrictions + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') # the open unit disc + sage: X. = M.chart() + sage: U = M.open_subset('U') + sage: X_U = X.restrict(U, restrictions=[x<0, y>1/2]) + sage: X_U.coord_range() + x: (-oo, 0); y: (1/2, +oo) + """ import operator - if not isinstance(restrictions, list): - # case of a single condition or conditions to be combined by "or" - restrictions = [restrictions] - self._restrictions.extend(restrictions) - # Update of the coordinate bounds from the restrictions: bounds = list(self._bounds) # convert to a list for modifications new_restrictions = [] for restrict in self._restrictions: restrict_used = False # determines whether restrict is used # to set some coordinate bound - if not isinstance(restrict, (tuple, list)): # case of combined - # conditions excluded + if not isinstance(restrict, (tuple, list, set, frozenset)): # case of combined + # conditions excluded operands = restrict.operands() left = operands[0] right = operands[1] @@ -2137,13 +2150,13 @@ def restrict(self, subset, restrictions=None): for coord in self._xx: coordinates += repr(coord) + ' ' res = type(self)(subset, coordinates, - calc_method=self._calc_method._current) - res._bounds = self._bounds + calc_method=self._calc_method._current, + bounds=self._bounds, + # The coordinate restrictions are added + # to the result chart and possibly + # transformed into coordinate bounds: + coord_restrictions=restrictions) res._restrictions.extend(self._restrictions) - # The coordinate restrictions are added to the result chart and - # possibly transformed into coordinate bounds: - if restrictions is not None: - res.add_restrictions(restrictions) # Update of supercharts and subcharts: res._supercharts.update(self._supercharts) for schart in self._supercharts: diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 6dfa44f91d9..8979be0c5b9 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -669,7 +669,7 @@ def _an_element_(self): sage: p in V True sage: p.coord() - (-pi - 1, 2) + (-pi - 1, 0) """ from sage.rings.infinity import Infinity From 3a5217cd449ae5b03e26c10994bc6f2f7944470e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 16:46:13 -0700 Subject: [PATCH 162/336] Chart._normalize_coord_restrictions: Move resolution of lambdas here --- src/sage/manifolds/chart.py | 55 +++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 05a22f8e24c..1de89b42f2d 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -315,11 +315,8 @@ def __classcall__(cls, domain, coordinates='', try: return domain._charts_by_coord[coord_string] except KeyError: - if callable(coord_restrictions) and not isinstance(coord_restrictions, Expression): - # lambda-quoted - coord_restrictions = coord_restrictions(*coordinates) # Make coord_restrictions hashable - coord_restrictions = cls._normalize_coord_restrictions(coord_restrictions) + coord_restrictions = cls._normalize_coord_restrictions(coordinates, coord_restrictions) self = super().__classcall__(cls, domain, coordinates, calc_method, coord_restrictions=coord_restrictions, **coordinate_options) @@ -369,8 +366,8 @@ def __init__(self, domain, coordinates, calc_method, periods=None, coord_restric "{} elements".format(self._manifold.dim())) self._xx = coordinates # - # Additional restrictions on the coordinates - self._restrictions = sorted(coord_restrictions) + # Additional restrictions on the coordinates. + self._restrictions = sorted(coord_restrictions, key=str) # # The chart is added to the domain's atlas, as well as to all the # atlases of the domain's supersets; moreover the first defined chart @@ -464,24 +461,23 @@ def _parse_coordinates(cls, domain, coordinates): return tuple(xx_list), dict(periods=tuple(period_list)) @staticmethod - def _normalize_coord_restrictions(restrictions): + def _normalize_coord_restrictions(coordinates, coord_restrictions): r""" - Rewrite ``restrictions`` as a ``frozenset``, representing a logical "and", of other clauses. + Rewrite ``coord_restrictions`` as a ``frozenset``, representing a logical "and", of other clauses. Also replace ``list``s by ``frozenset``s, making the result hashable. EXAMPLES:: sage: from sage.manifolds.chart import Chart - sage: var("x y z") - (x, y, z) - sage: Chart._normalize_coord_restrictions(None) + sage: coordinates = var("x y z") + sage: Chart._normalize_coord_restrictions(coordinates, None) frozenset() - sage: Chart._normalize_coord_restrictions(x > y) + sage: Chart._normalize_coord_restrictions(coordinates, x > y) frozenset({x > y}) - sage: Chart._normalize_coord_restrictions((x != 0, y != 0)) + sage: Chart._normalize_coord_restrictions(coordinates, (x != 0, y != 0)) frozenset({(x != 0, y != 0)}) - sage: Chart._normalize_coord_restrictions([x > y, (x != 0, y != 0), z^2 < x]) + sage: Chart._normalize_coord_restrictions(coordinates, [x > y, (x != 0, y != 0), z^2 < x]) frozenset({(x != 0, y != 0), x > y, z^2 < x}) """ @@ -493,14 +489,18 @@ def normalize(r): else: return r - if restrictions is None: + if coord_restrictions is None: return frozenset() - if not isinstance(restrictions, (list, set, frozenset)): + if callable(coord_restrictions) and not isinstance(coord_restrictions, Expression): + # lambda-quoted + coord_restrictions = coord_restrictions(*coordinates) + + if not isinstance(coord_restrictions, (list, set, frozenset)): # case of a single condition or conditions to be combined by "or" - restrictions = [restrictions] + coord_restrictions = [coord_restrictions] - return normalize(restrictions) + return normalize(coord_restrictions) def _repr_(self): r""" @@ -821,13 +821,13 @@ def restrict(self, subset, restrictions=None): coordinates = "" for coord in self._xx: coordinates += repr(coord) + ' ' + res_coord_restrictions = set(self._restrictions) + res_coord_restrictions.update(self._normalize_coord_restrictions(self._xx, restrictions)) res = type(self)(subset, coordinates, - calc_method=self._calc_method._current) - res._restrictions.extend(self._restrictions) - # The coordinate restrictions are added to the result chart and - # possibly transformed into coordinate bounds: - if restrictions is not None: - res.add_restrictions(restrictions) + calc_method=self._calc_method._current, + # The coordinate restrictions are added + # to the result chart + coord_restrictions=res_coord_restrictions) # Update of supercharts and subcharts: res._supercharts.update(self._supercharts) for schart in self._supercharts: @@ -1991,7 +1991,7 @@ def add_restrictions(self, restrictions): sage: A = M.open_subset('A') # annulus 1/2 < r < 1 sage: X_A = X.restrict(A, x^2+y^2 > 1/4) sage: X_A._restrictions - [x^2 + y^2 > (1/4), x^2 + y^2 < 1] + [x^2 + y^2 < 1, x^2 + y^2 > (1/4)] sage: X_A.valid_coordinates(0,1/3) False sage: X_A.valid_coordinates(2/3,1/3) @@ -2149,14 +2149,15 @@ def restrict(self, subset, restrictions=None): coordinates = "" for coord in self._xx: coordinates += repr(coord) + ' ' + res_coord_restrictions = set(self._restrictions) + res_coord_restrictions.update(self._normalize_coord_restrictions(self._xx, restrictions)) res = type(self)(subset, coordinates, calc_method=self._calc_method._current, bounds=self._bounds, # The coordinate restrictions are added # to the result chart and possibly # transformed into coordinate bounds: - coord_restrictions=restrictions) - res._restrictions.extend(self._restrictions) + coord_restrictions=res_coord_restrictions) # Update of supercharts and subcharts: res._supercharts.update(self._supercharts) for schart in self._supercharts: From 4c8e6d3325e51fa97e92e93aefb5ecd099d7f8b6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 16:46:51 -0700 Subject: [PATCH 163/336] OpenInterval: Remove use of add_restrictions --- .../manifolds/differentiable/examples/real_line.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/sage/manifolds/differentiable/examples/real_line.py b/src/sage/manifolds/differentiable/examples/real_line.py index cd9efabee5f..f0b7f21b2e9 100644 --- a/src/sage/manifolds/differentiable/examples/real_line.py +++ b/src/sage/manifolds/differentiable/examples/real_line.py @@ -367,8 +367,6 @@ def __init__(self, lower, upper, ambient_interval=None, coordinate = 't' else: coordinate = names[0] - self._canon_chart = self.chart(coordinates=coordinate) - t = self._canon_chart[start_index] else: if lower < ambient_interval.lower_bound(): raise ValueError("the lower bound is smaller than that of " @@ -378,20 +376,19 @@ def __init__(self, lower, upper, ambient_interval=None, + "the containing interval") self.declare_subset(ambient_interval) ambient_interval._top_subsets.add(self) - t = ambient_interval.canonical_coordinate() if lower != minus_infinity: if upper != infinity: - restrictions = [t > lower, t < upper] + restrictions = lambda t: [t > lower, t < upper] else: - restrictions = t > lower + restrictions = lambda t: t > lower else: if upper != infinity: - restrictions = t < upper + restrictions = lambda t: t < upper else: restrictions = None if ambient_interval is None: - if restrictions is not None: - self._canon_chart.add_restrictions(restrictions) + self._canon_chart = self.chart(coordinates=coordinate, + coord_restrictions=restrictions) else: self._canon_chart = ambient_interval.canonical_chart().restrict(self, restrictions=restrictions) From f8c537d55c119b5c58fb859650f73e2a09617555 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 16:47:20 -0700 Subject: [PATCH 164/336] IntegratedCurve: Remove use of add_restrictions in doctests --- .../differentiable/integrated_curve.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index 95fbdd99086..ebfc19f9274 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -1469,17 +1469,13 @@ def solve_across_charts(self, charts=None, step=None, solution_key=None, maps:: sage: M = Manifold(2, 'M', structure="Riemannian") - sage: C. = M.chart() - sage: P. = M.chart() + sage: C. = M.chart(coord_restrictions=lambda x,y: x**2+y**2 < 3**2) + sage: P. = M.chart(coord_restrictions=lambda r, th: r > 2) sage: P_to_C = P.transition_map(C,(r*cos(th), r*sin(th))) sage: C_to_P = C.transition_map(P,(sqrt(x**2+y**2), atan2(y,x))) - Let us also add restrictions on those charts, to avoid any - singularity. We have to make sure that the charts still intersect. - Here the intersection is the donut region `2 < r < 3`:: - - sage: P.add_restrictions(r > 2) - sage: C.add_restrictions(x**2+y**2 < 3**2) + Here we added restrictions on those charts, to avoid any + singularity. The intersection is the donut region `2 < r < 3`. We still have to define the metric. This is done in the Cartesian frame. The metric in the polar frame is computed automatically:: @@ -1574,14 +1570,12 @@ def solve_across_charts(self, charts=None, step=None, solution_key=None, .. PLOT:: M = Manifold(2, 'M', structure="Riemannian") - C= M.chart(names = ("x", "y")) + C= M.chart(names = ("x", "y"), coord_restrictions=lambda x,y: x**2+y**2 < 3**2) x, y = C[:] - P = M.chart(names = ("r", "ph")) + P = M.chart(names = ("r", "th"), coord_restrictions=lambda r,th: r > 2) r, th = P[:] P_to_C = P.transition_map(C,(r*cos(th), r*sin(th))) C_to_P = C.transition_map(P,(sqrt(x**2+y**2), atan2(y,x))) - P.add_restrictions(r > 2) - C.add_restrictions(x**2+y**2 < 3**2) g = M.metric() g[0,0,C] = 1 g[1,1,C] = 1 From 939dcd5c3884adad6c757b0ce6cb0840f26b73d6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 16:47:45 -0700 Subject: [PATCH 165/336] Chart.add_restrictions: Deprecate --- src/sage/manifolds/chart.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 1de89b42f2d..92067571f47 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -730,6 +730,9 @@ def add_restrictions(self, restrictions): r""" Add some restrictions on the coordinates. + This is deprecated; provide the restrictions at the time of creating + the chart. + INPUT: - ``restrictions`` -- list of restrictions on the @@ -755,13 +758,19 @@ def add_restrictions(self, restrictions): sage: M = Manifold(2, 'M', field='complex', structure='topological') sage: X. = M.chart() sage: X.add_restrictions(abs(x) > 1) + doctest:warning... + DeprecationWarning: Chart.add_restrictions is deprecated; provide the + restrictions at the time of creating the chart + See https://trac.sagemath.org/32102 for details. sage: X.valid_coordinates(2+i, 1) True sage: X.valid_coordinates(i, 1) False """ - self._restrictions.extend(self._normalize_coord_restrictions(restrictions)) + from sage.misc.superseded import deprecation + deprecation(32102, "Chart.add_restrictions is deprecated; provide the restrictions at the time of creating the chart") + self._restrictions.extend(self._normalize_coord_restrictions(self._xx, restrictions)) def restrict(self, subset, restrictions=None): r""" @@ -1954,6 +1963,9 @@ def add_restrictions(self, restrictions): r""" Add some restrictions on the coordinates. + This is deprecated; provide the restrictions at the time of creating + the chart. + INPUT: - ``restrictions`` -- list of restrictions on the @@ -1981,6 +1993,10 @@ def add_restrictions(self, restrictions): sage: M = Manifold(2, 'M', structure='topological') # the open unit disc sage: X. = M.chart() sage: X.add_restrictions(x^2+y^2<1) + doctest:warning... + DeprecationWarning: Chart.add_restrictions is deprecated; provide the + restrictions at the time of creating the chart + See https://trac.sagemath.org/32102 for details. sage: X.valid_coordinates(0,2) False sage: X.valid_coordinates(0,1/3) From a745649024059791d35da56b1376fc0f0ef6e0c0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Jul 2021 17:18:26 -0700 Subject: [PATCH 166/336] Chart.codomain, _restrict_set: Handle set/frozenset as ANDs in anticipation of #32102 --- src/sage/manifolds/chart.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 7865a92647e..b310335f3f9 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -842,7 +842,7 @@ def _check_restrictions(self, restrict, substitutions): if isinstance(restrict, tuple): # case of 'or' conditions return any(self._check_restrictions(cond, substitutions) for cond in restrict) - elif isinstance(restrict, list): # case of 'and' conditions + elif isinstance(restrict, (list, set, frozenset)): # case of 'and' conditions return all(self._check_restrictions(cond, substitutions) for cond in restrict) # Case of a single condition: @@ -867,8 +867,9 @@ def codomain(self): else: return ambient - def _restrict_set(self, universe, restrict): + def _restrict_set(self, universe, coord_restrictions): """ + Return a set corresponding to coordinate restrictions. EXAMPLES:: @@ -892,21 +893,21 @@ def _restrict_set(self, universe, restrict): { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : y < 0 } and { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : x > 0 } """ - if isinstance(restrict, tuple): # case of 'or' conditions - A = self._restrict_set(universe, restrict[0]) - if len(restrict) == 1: + if isinstance(coord_restrictions, tuple): # case of 'or' conditions + A = self._restrict_set(universe, coord_restrictions[0]) + if len(coord_restrictions) == 1: return A else: - return A.union(self._restrict_set(universe, restrict[1:])) - elif isinstance(restrict, list): # case of 'and' conditions - A = self._restrict_set(universe, restrict[0]) - if len(restrict) == 1: + return A.union(self._restrict_set(universe, coord_restrictions[1:])) + elif isinstance(coord_restrictions, (list, set, frozenset)): # case of 'and' conditions + A = self._restrict_set(universe, coord_restrictions[0]) + if len(coord_restrictions) == 1: return A else: - return A.intersection(self._restrict_set(universe, restrict[1:])) + return A.intersection(self._restrict_set(universe, coord_restrictions[1:])) # Case of a single condition: from sage.sets.condition_set import ConditionSet - return ConditionSet(universe, restrict, vars=self._xx) + return ConditionSet(universe, coord_restrictions, vars=self._xx) def transition_map(self, other, transformations, intersection_name=None, restrictions1=None, restrictions2=None): From c3ff0453bbe92dd51b956ce3ff10d8f0ccb76e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 4 Jul 2021 09:01:36 +0200 Subject: [PATCH 167/336] full flake8 cleanup of berkovich_cp_element --- .../schemes/berkovich/berkovich_cp_element.py | 376 +++++++++--------- 1 file changed, 191 insertions(+), 185 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index cae8feb751d..9c730a31626 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -21,17 +21,17 @@ AUTHORS: - - Alexander Galarraga (2020-06-22): initial implementation +- Alexander Galarraga (2020-06-22): initial implementation """ -#***************************************************************************** +# ***************************************************************************** # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.structure.element import Element from sage.symbolic.expression import is_Expression @@ -45,12 +45,14 @@ from sage.rings.integer_ring import ZZ from sage.rings.infinity import Infinity + class Berkovich_Element(Element): """ The parent class for any element of a Berkovich space """ pass + class Berkovich_Element_Cp(Berkovich_Element): r""" The abstract parent class for any element of Berkovich space over `\CC_p`. @@ -85,14 +87,15 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= from sage.rings.fraction_field_element import FractionFieldElement_1poly_field self._type = None - #if radius is a list or a tuple, this is a type 4 point + # if radius is a list or a tuple, this is a type 4 point if isinstance(radius, list) or isinstance(radius, tuple): if error_check: if not (isinstance(center, list) or isinstance(center, tuple)): raise TypeError("center was passed a list but radius was not a list") if len(radius) != len(center): - raise ValueError("the same number of centers and radii must be specified to create " + \ - "a type IV point") + raise ValueError("the same number of centers and radii " + "must be specified to create " + "a type IV point") self._center_lst = list(center) self._radius_lst = list(radius) self._prec = len(self._radius_lst) @@ -104,39 +107,39 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= if not error_check: return - #is_FunctionFieldElement calls .parent + # is_FunctionFieldElement calls .parent elif hasattr(center, "parent") and hasattr(radius, 'parent'): from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial if is_MPolynomial(center): try: center = center.univariate_polynomial() - except: - raise TypeError('center was %s, a multivariable polynomial' %center) + except AttributeError: + raise TypeError('center was %s, a multivariable polynomial' % center) - #check if the radius and the center are functions + # check if the radius and the center are functions center_func_check = is_FunctionFieldElement(center) or is_Polynomial(center) or\ isinstance(center, FractionFieldElement_1poly_field) or is_Expression(center) radius_func_check = is_FunctionFieldElement(radius) or is_Polynomial(radius) or\ isinstance(radius, FractionFieldElement_1poly_field) or is_Expression(radius) if center_func_check: - #check that both center and radii are supported univariate function + # check that both center and radii are supported univariate function center_expr_check = False radius_expr_check = False if error_check: if is_Expression(center): if len(center.variables()) != 1: - raise ValueError("an expression with %s " %(len(center.variables())) + \ - "variables cannot define the centers approximating a type IV point") + raise ValueError("an expression with %s " % (len(center.variables())) + + "variables cannot define the centers approximating a type IV point") else: - #we do this since .subs is currently buggy for polynomials but not expressions + # we do this since .subs is currently buggy for polynomials but not expressions center_expr_check = True if not radius_func_check: raise TypeError("center was passed a function but radius was not a function") if is_Expression(radius): if len(radius.variables()) != 1: - raise ValueError("an expression with %s " %(len(radius.variables())) + \ - "variables cannot define the radii approximating a type IV point") + raise ValueError("an expression with %s " % (len(radius.variables())) + + "variables cannot define the radii approximating a type IV point") else: radius_expr_check = True else: @@ -154,15 +157,15 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= x = self._center_func.variables()[0] if radius_expr_check: y = self._radius_func.variables()[0] - for i in range(1,self._prec+1): + for i in range(1, self._prec + 1): if center_expr_check: - #we use .subs for expressions to avoid deprecation - center_lst.append(self._center_func.subs({x:i})) + # we use .subs for expressions to avoid deprecation + center_lst.append(self._center_func.subs({x: i})) else: - #.subs for polynomials is currently buggy + # .subs for polynomials is currently buggy center_lst.append(self._center_func(i)) if radius_expr_check: - radius_lst.append(self._radius_func.subs({y:i})) + radius_lst.append(self._radius_func.subs({y: i})) else: radius_lst.append(self._radius_func(i)) self._center_lst = center_lst @@ -177,57 +180,57 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= for i in range(len(self._center_lst)): center = self._center_lst[i] radius = self._radius_lst[i] - #make sure the center is a point of projective space and not the point at infinity + # make sure the center is a point of projective space and not the point at infinity if not isinstance(center, SchemeMorphism_point_projective_field): try: center = (self._base_space)(center) except (TypeError, ValueError): - raise TypeError('could not convert %s to %s' %(center, self._base_space)) + raise TypeError('could not convert %s to %s' % (center, self._base_space)) if self._base_type == 'padic field': if not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): try: center = (self._base_space)(center) except (TypeError, ValueError): - raise ValueError("could not convert %s to %s" %(center, self._base_space)) + raise ValueError("could not convert %s to %s" % (center, self._base_space)) else: # center is padic, not but an element of a scheme over a padic field. # we convert to scheme over a padic field center = ProjectiveSpace(center.scheme().base_ring().fraction_field(), 1)(center) if center.scheme().base_ring().prime() != self._p: - raise ValueError("center must be an element of " + \ - "%s not %s" %self._base_space, center.scheme()) + raise ValueError("center must be an element of " + + "%s not %s" % self._base_space, center.scheme()) else: if center not in self._base_space: try: center = (self._base_space)(center) except (TypeError, ValueError): - raise ValueError('could not convert %s to %s' %(center, self._base_space)) + raise ValueError('could not convert %s to %s' % (center, self._base_space)) if center.scheme().ambient_space() != center.scheme(): - raise ValueError("the center of a point of Berkovich space over " + \ - "P^1(Cp(%s)) must be a point of Cp not %s" %(self._p,center.scheme())) - if center == (center.scheme())((1,0)): - raise ValueError("the center of a disk approximating a type IV point of Berkovich " + \ - "space cannot be centered at %s" %((center.scheme())((1,0)))) - #since we are over a field, we can normalize coordinates. all code assumes normalized coordinates + raise ValueError("the center of a point of Berkovich space over " + + "P^1(Cp(%s)) must be a point of Cp not %s" % (self._p, center.scheme())) + if center == (center.scheme())((1, 0)): + raise ValueError("the center of a disk approximating a type IV point of Berkovich " + + "space cannot be centered at %s" % ((center.scheme())((1, 0)))) + # since we are over a field, we can normalize coordinates. all code assumes normalized coordinates center.normalize_coordinates() - #make sure the radius coerces into the reals + # make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): radius = RR(radius) elif RR.has_coerce_map_from(radius.parent()): radius = RR(radius) else: - raise TypeError("the radius of a disk approximating a type IV point" + \ - "must coerce into the real numbers, %s does not coerce" %(radius)) + raise TypeError("the radius of a disk approximating a type IV point" + + "must coerce into the real numbers, %s does not coerce" % (radius)) if i != 0: - #check containment for the sequence of disks - previous_center = self._center_lst[i-1] - previous_radius = self._radius_lst[i-1] + # check containment for the sequence of disks + previous_center = self._center_lst[i - 1] + previous_radius = self._radius_lst[i - 1] dist = self._custom_abs(center[0] - previous_center[0]) if previous_radius < radius or dist > previous_radius: - raise ValueError("sequence of disks does not define a type IV point as " + \ - "containment is not proper") + raise ValueError("sequence of disks does not define a type IV point as " + + "containment is not proper") self._center_lst[i] = center self._radius_lst[i] = radius return @@ -236,25 +239,25 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= center = self._center_lst[i] radius = self._radius_lst[i] if self._base_type == 'padic field': - #make sure the center is in Cp + # make sure the center is in Cp if not isinstance(center, pAdicGenericElement): try: center = (self._base_space)(center) except (TypeError, ValueError): - raise TypeError("could not convert %s to %s" %(center, self._base_space)) + raise TypeError("could not convert %s to %s" % (center, self._base_space)) elif not is_pAdicField(center.parent()): - #center is padic, not but an element of a padic field. we convert to padic field + # center is padic, not but an element of a padic field. we convert to padic field center = (center.parent().fraction_field())(center) if (center.parent()).prime() != self._p: - raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) + raise ValueError("center in %s, should be in %s") % (center.parent(), self._base_space) else: - #make sure the center is in the appropriate number field + # make sure the center is in the appropriate number field if center.parent() == self._base_space: try: center = (self._base_space)(center) except (TypeError, ValueError): - raise ValueError('could not convert %s to %s' %(center, self._base_space)) - #make sure the radius coerces into the reals + raise ValueError('could not convert %s to %s' % (center, self._base_space)) + # make sure the radius coerces into the reals if not is_RealNumber(radius): if is_Expression(radius): radius = RR(radius) @@ -262,38 +265,38 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= radius = RR(radius) self._radius_lst[i] = radius else: - raise ValueError("the radius of a disk approximating a type IV point must " + \ - "coerce into the real numbers, %s does not coerce" %(radius)) + raise ValueError("the radius of a disk approximating a type IV point must " + + "coerce into the real numbers, %s does not coerce" % (radius)) if i != 0: - #check containment for the sequence of disks - previous_center = self._center_lst[i-1] - previous_radius = self._radius_lst[i-1] + # check containment for the sequence of disks + previous_center = self._center_lst[i - 1] + previous_radius = self._radius_lst[i - 1] dist = self._custom_abs(center - previous_center) if previous_radius < radius or dist > previous_radius: - raise ValueError("sequence of disks does not define a type IV point as " + \ - "containment is not proper") + raise ValueError("sequence of disks does not define a type IV point as " + + "containment is not proper") self._center_lst[i] = center self._radius_lst[i] = radius return else: - raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ - "Berkovich_Element_Cp directly" ) + raise ValueError("bad value %s passed to space_type. Do not initialize " % (space_type) + + "Berkovich_Element_Cp directly") - #the point must now be type 1, 2, or 3, so we check that the center is of the appropriate type + # the point must now be type 1, 2, or 3, so we check that the center is of the appropriate type if error_check: if space_type == "projective": if not isinstance(center, SchemeMorphism_point_projective_field): try: center = (self._base_space)(center) except (ValueError, TypeError): - raise TypeError("could not convert %s to %s" %(center, self._base_space)) + raise TypeError("could not convert %s to %s" % (center, self._base_space)) if self._base_type == 'padic field': if not is_pAdicField(center.scheme().base_ring()): if not isinstance(center.scheme().base_ring(), pAdicBaseGeneric): try: center = (self._base_space)(center) except (TypeError, ValueError): - raise ValueError("could not convert %s to %s" %(center, self._base_space)) + raise ValueError("could not convert %s to %s" % (center, self._base_space)) else: # center is padic, not but an element of a scheme over a padic field. # we convert to scheme over a padic field @@ -301,48 +304,48 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= try: center = field_scheme(center) except (TypeError, ValueError): - raise ValueError('could not convert %s to %s' %center, field_scheme) + raise ValueError('could not convert %s to %s' % center, field_scheme) if center.scheme().base_ring().prime() != self._p: - raise ValueError("center must be an element of " + \ - "%s not %s" %self._base_space, center.scheme()) + raise ValueError("center must be an element of " + + "%s not %s" % self._base_space, center.scheme()) else: if center not in self._base_space: try: center = (self._base_space)(center) except (TypeError, ValueError): - raise ValueError('could not convert %s to %s' %(center, self._base_space)) + raise ValueError('could not convert %s to %s' % (center, self._base_space)) if not(center.scheme().ambient_space() is center.scheme()): - raise ValueError("the center of a point of projective Berkovich space cannot be " + \ - "a point of %s" %(center.scheme())) - #since we are over a field, we normalize coordinates + raise ValueError("the center of a point of projective Berkovich space cannot be " + + "a point of %s" % (center.scheme())) + # since we are over a field, we normalize coordinates center.normalize_coordinates() elif space_type == 'affine': if self._base_type == 'padic field': - #make sure the center is in Cp + # make sure the center is in Cp if not isinstance(center, pAdicGenericElement): try: center = (self._base_space)(center) except (TypeError, ValueError): - raise TypeError("could not convert %s to %s" %(center, self._base_space)) + raise TypeError("could not convert %s to %s" % (center, self._base_space)) elif not is_pAdicField(center.parent()): - #center is padic, not but an element of a padic field. we convert to padic field + # center is padic, not but an element of a padic field. we convert to padic field center = (center.parent().fraction_field())(center) if (center.parent()).prime() != self._p: - raise ValueError("center in %s, should be in %s") %(center.parent(), self._base_space) + raise ValueError("center in %s, should be in %s") % (center.parent(), self._base_space) else: - #make sure the center is in the appropriate number field + # make sure the center is in the appropriate number field if not(center.parent() == self._base_space): try: center = (self._base_space)(center) except (TypeError, ValueError): - raise ValueError('could not convert %s to %s' %(center, self._base_space)) + raise ValueError('could not convert %s to %s' % (center, self._base_space)) else: - raise ValueError("bad value %s passed to space_type. Do not initialize "%(space_type) + \ - "Berkovich_Element_Cp directly") + raise ValueError("bad value %s passed to space_type. Do not initialize " % (space_type) + + "Berkovich_Element_Cp directly") self._center = center - #since this point is not type IV, these are None + # since this point is not type IV, these are None self._center_func = None self._center_lst = None self._radius_lst = None @@ -353,25 +356,25 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= self._radius = 0 self._power = None return - #In order to simplify our representation, type II and III points cannot be centered at infinity + # In order to simplify our representation, type II and III points cannot be centered at infinity if space_type == "projective": - #TODO use involution map to allow for infinity to be passed in as center + # TODO use involution map to allow for infinity to be passed in as center if center[1] == 0: raise ValueError('type II and III points can not be centered at infinity') - if power != None: + if power is not None: if error_check: try: power = QQ(power) except TypeError: raise TypeError("power must convert to rationals") - if radius != None: + if radius is not None: if radius != RR(self._p**power): raise ValueError("conflicting inputs for power and radius") self._power = power self._radius = RR(self._p**power) self._type = 2 return - if radius != None: + if radius is not None: if is_Expression(radius): try: power = QQ(radius.log(self._p).expand_log()) @@ -382,8 +385,8 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= self._radius = radius except TypeError: if len(radius.variables()) == 1: - raise ValueError('radius univariate function but center is constant. ' + \ - 'this does not define a type IV point') + raise ValueError('radius univariate function but center is constant. ' + + 'this does not define a type IV point') raise TypeError("symbolic radius must be a real number") if (not is_RealNumber(radius)) and power is None: if RR.has_coerce_map_from(radius.parent()): @@ -392,7 +395,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= raise TypeError("radius must coerce into real numbers") else: self._radius = radius - if power != None: + if power is not None: self._power = power self._type = 2 return @@ -433,8 +436,8 @@ def _custom_abs(self, x): if x.valuation(self._ideal) == Infinity: return 0 if self._ideal in QQ: - return self.prime()**(-1*x.valuation(self._ideal)) - return self.prime()**(-1*x.valuation(self._ideal)/self._ideal.absolute_ramification_index()) + return self.prime()**(-x.valuation(self._ideal)) + return self.prime()**(-x.valuation(self._ideal) / self._ideal.absolute_ramification_index()) def center_function(self): """ @@ -513,7 +516,7 @@ def precision(self): sage: d.precision == d.prec True """ - if self._type in [1,2,3]: + if self._type in [1, 2, 3]: raise AttributeError("type I, II, and III points do not have a precision") return self._prec @@ -565,7 +568,7 @@ def power(self): sage: Q2.power() 1.26185950714291 """ - if self._type in [1,4]: + if self._type in [1, 4]: raise AttributeError("type I and IV points do not have a power") return self._power @@ -658,7 +661,7 @@ def diameter(self, basepoint=Infinity): x = R.gens()[0] if is_Expression(self._radius_func): radius_func_variable = self._radius_func.variables()[0] - radius_expr = self._radius_func.subs({radius_func_variable:x}) + radius_expr = self._radius_func.subs({radius_func_variable: x}) else: radius_expr = self._radius_func(x) from sage.symbolic.ring import SymbolicRing as SR @@ -666,7 +669,7 @@ def diameter(self, basepoint=Infinity): return radius_expr.limit(x="oo") return self._radius if not isinstance(basepoint, Berkovich_Element_Cp): - raise TypeError('basepoint must be a point of Berkovich space, not %s' %basepoint) + raise TypeError('basepoint must be a point of Berkovich space, not %s' % basepoint) if basepoint.parent() != self.parent(): raise ValueError('basepoint must be a point of the same Berkovich space') return self.Hsia_kernel(self, basepoint) @@ -708,7 +711,7 @@ def path_distance_metric(self, other): 0 """ if not isinstance(other, type(self)): - raise TypeError('other must be a point of Berkovich space. other was %s' %other) + raise TypeError('other must be a point of Berkovich space. other was %s' % other) if self.parent() != other.parent(): raise ValueError("other must be a point of the same Berkovich space") if self.type_of_point() == 1 or other.type_of_point() == 1: @@ -716,7 +719,7 @@ def path_distance_metric(self, other): return 0 else: return RR(Infinity) - return 2*self.join(other).diameter().log(self.prime()) \ + return 2 * self.join(other).diameter().log(self.prime()) \ - self.diameter().log(self.prime()) \ - other.diameter().log(other.prime()) @@ -759,18 +762,18 @@ def Hsia_kernel(self, other, basepoint): """ if not isinstance(other, type(self)): - raise TypeError('other must be a point of Berkovich space. other was %s' %other) + raise TypeError('other must be a point of Berkovich space. other was %s' % other) if self.parent() != other.parent(): raise ValueError("other must be a point of the same Berkovich space") if not isinstance(basepoint, type(self)): - raise TypeError('basepoint must be a point of Berkovich space. basepoint was %s' %basepoint) + raise TypeError('basepoint must be a point of Berkovich space. basepoint was %s' % basepoint) if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich space") if basepoint.type_of_point() == 1: if self == basepoint or other == basepoint: return RR(Infinity) - return self.spherical_kernel(other)/ \ - (self.spherical_kernel(basepoint)*other.spherical_kernel(basepoint)) + return self.spherical_kernel(other) / \ + (self.spherical_kernel(basepoint) * other.spherical_kernel(basepoint)) def small_metric(self, other): r""" @@ -813,14 +816,14 @@ def small_metric(self, other): 1.75000000000000 """ if not isinstance(other, Berkovich_Element_Cp): - raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) + raise TypeError('other must be a point of affine Berkovich space. other was %s' % other) if self.parent() != other.parent(): raise ValueError('other must be a point of the same Berkovich space') gauss = self.parent()(RR(0), RR(1)) g_greater_than_s = gauss.gt(self) g_greater_than_o = gauss.gt(other) if g_greater_than_s and g_greater_than_o: - return 2*self.join(other, gauss).diameter() - self.diameter() - other.diameter() + return 2 * self.join(other, gauss).diameter() - self.diameter() - other.diameter() if not g_greater_than_s: new_self = self.involution_map() else: @@ -829,7 +832,7 @@ def small_metric(self, other): new_other = other.involution_map() else: new_other = other - return 2*new_self.join(new_other, gauss).diameter() \ + return 2 * new_self.join(new_other, gauss).diameter() \ - new_self.diameter() - new_other.diameter() def potential_kernel(self, other, basepoint): @@ -867,16 +870,16 @@ def potential_kernel(self, other, basepoint): 0.369070246428543 """ if not isinstance(other, type(self)): - raise TypeError('other must be a point of a Berkovich space, not %s' %other) + raise TypeError('other must be a point of a Berkovich space, not %s' % other) if other.parent() != self.parent(): raise ValueError('other must be a point of the same Berkovich space') if not isinstance(basepoint, type(self)): - raise TypeError('basepoint must be a point of Berkovich line, not %s' %basepoint) + raise TypeError('basepoint must be a point of Berkovich line, not %s' % basepoint) if basepoint.parent() != self.parent(): raise ValueError('basepoint must be a point of the same Berkovich space') return basepoint.path_distance_metric(self.join(other, basepoint)) - def spherical_kernel(self,other): + def spherical_kernel(self, other): r""" The spherical kernel of this point with ``other``. @@ -905,7 +908,7 @@ def spherical_kernel(self,other): 0 """ if not isinstance(other, type(self)): - raise TypeError('other must be a point of Berkovich space, not %s' %other) + raise TypeError('other must be a point of Berkovich space, not %s' % other) if other.parent() != self.parent(): raise ValueError('other must be a point of the same Berkovich space') gauss_point = self.parent()(ZZ(0), ZZ(1)) @@ -913,7 +916,7 @@ def spherical_kernel(self,other): dist = gauss_point.path_distance_metric(w) if dist == Infinity: return 0 - return self.prime()**(-1*dist) + return self.prime()**(-dist) def Hsia_kernel_infinity(self, other): r""" @@ -1042,21 +1045,21 @@ def _repr_(self): return "Type I point centered at " + format(self._center) elif self._type == 2: return "Type II point centered at " \ - + format(self._center) \ - + " of radius %s^%s" %(self._p, self._power) + + format(self._center) \ + + " of radius %s^%s" % (self._p, self._power) elif self._type == 3: return "Type III point centered at " \ - + format(self._center) + " of radius " \ - + format(self._radius) + + format(self._center) + " of radius " \ + + format(self._radius) else: - if self._center_func != None and self._radius_func != None: - return "Type IV point of precision %s " %self._prec + \ + if self._center_func is not None and self._radius_func is not None: + return "Type IV point of precision %s " % self._prec + \ "with centers given by %s and radii given by %s"\ - %(self._center_func, self._radius_func) + % (self._center_func, self._radius_func) else: - return "Type IV point of precision %s, approximated " %self._prec + \ + return "Type IV point of precision %s, approximated " % self._prec + \ "by disks centered at %s ... with radii %s ..." \ - %(self._center_lst[:min(self._prec, 2)], self._radius_lst[:min(self._prec, 2)]) + % (self._center_lst[:min(self._prec, 2)], self._radius_lst[:min(self._prec, 2)]) def _latex_(self): r""" @@ -1072,16 +1075,17 @@ def _latex_(self): """ from sage.misc.latex import latex if self._type == 1: - text = r"the point %s of } \Bold{C}_%s" %(self._center, self._p) - elif self._type in [2,3]: + text = r"the point %s of } \Bold{C}_%s" % (self._center, self._p) + elif self._type in [2, 3]: text = r"the disk centered at %s of radius %s in } \Bold{C}_%s" \ - %(self._center, self._radius, self._p) + % (self._center, self._radius, self._p) else: - text = "the sequence of disks with centers %s } " %self._center_lst[:2] + \ - r"\ldots \text{ and radii %s } \ldots" %self._radius_lst[:2] - return r"\text{type %s Point of }" %(self._type) \ + text = "the sequence of disks with centers %s } " % self._center_lst[:2] + \ + r"\ldots \text{ and radii %s } \ldots" % self._radius_lst[:2] + return r"\text{type %s Point of }" % (self._type) \ + latex(self.parent()) + r"\text{equivalent to " + text + class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): r""" Element class of the Berkovich affine line over `\CC_p`. @@ -1245,6 +1249,7 @@ class Berkovich_Element_Cp_Affine(Berkovich_Element_Cp): sage: TestSuite(Q5).run() """ + def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): """ Initialization function. @@ -1255,20 +1260,20 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check sage: A(5, 1) Type II point centered at 5 + O(17^20) of radius 17^0 """ - #we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space - #unless we are passed a point of projective Berkovich space + # we call Berkovich_Element_Cp constructor which is shared with projective Berkovich space + # unless we are passed a point of projective Berkovich space Element.__init__(self, parent) self._p = parent.prime() self._base_space = parent.base() self._base_type = parent._base_type self._ideal = parent._ideal - #if this is a point of projective Berkovich space, we raise an error + # if this is a point of projective Berkovich space, we raise an error if isinstance(center, Berkovich_Element_Cp_Projective): raise TypeError('use as_affine_point to convert to affine Berkovich space') - Berkovich_Element_Cp.__init__(self, parent=parent, center=center, radius=radius, power=power, \ - prec=prec, space_type="affine", error_check=error_check) + Berkovich_Element_Cp.__init__(self, parent=parent, center=center, radius=radius, power=power, + prec=prec, space_type="affine", error_check=error_check) def as_projective_point(self): r""" @@ -1457,7 +1462,7 @@ def lt(self, other): False """ if not isinstance(other, Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) + raise TypeError('other must be a point of a projective Berkovich space, but was %s' % other) if self.parent() != other.parent(): raise ValueError('other must be a point of the same projective Berkovich space') @@ -1529,7 +1534,7 @@ def gt(self, other): True """ if not isinstance(other, Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) + raise TypeError('other must be a point of a projective Berkovich space, but was %s' % other) if self.parent() != other.parent(): raise ValueError('other must be a point of the same projective Berkovich space') @@ -1548,7 +1553,7 @@ def gt(self, other): def join(self, other, basepoint=Infinity): """ - Computes the join of this point and ``other`` with respect to ``basepoint``. + Compute the join of this point and ``other`` with respect to ``basepoint``. The join is first point that lies on the intersection of the path from this point to ``basepoint`` and the path from ``other`` to @@ -1600,9 +1605,9 @@ def join(self, other, basepoint=Infinity): sage: Q1.join(Q7, Q2) Type III point centered at 2 + O(3^20) of radius 2.00000000000000 """ - #we error check and then pass to projective space to do the join + # we error check and then pass to projective space to do the join if not isinstance(other, Berkovich_Element_Cp_Affine): - raise TypeError('other must be a point of affine Berkovich space. other was %s' %other) + raise TypeError('other must be a point of affine Berkovich space. other was %s' % other) if self.parent() != other.parent(): raise ValueError('other must be a point of the same affine Berkovich space') if self.type_of_point() == 4 or other.type_of_point() == 4: @@ -1615,7 +1620,7 @@ def join(self, other, basepoint=Infinity): return proj_self.join(proj_other).as_affine_point() if not isinstance(basepoint, Berkovich_Element_Cp_Affine): - raise TypeError('basepoint must a point of affine Berkovich space. basepoint was %s' %basepoint) + raise TypeError('basepoint must a point of affine Berkovich space. basepoint was %s' % basepoint) if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same affine Berkovich space") if basepoint.type_of_point() == 4: @@ -1623,7 +1628,6 @@ def join(self, other, basepoint=Infinity): proj_basepoint = basepoint.as_projective_point() return proj_self.join(proj_other, proj_basepoint).as_affine_point() - def involution_map(self): r""" Return the image of this point under the involution map. @@ -1691,7 +1695,7 @@ def involution_map(self): if self.type_of_point() == 1: if self.center() == 0: raise ValueError("involution map not defined on affine type I point centered at 0") - return self.parent()(1/self.center()) + return self.parent()(1 / self.center()) zero = self.parent()(ZZ(0)) radius = self.radius() @@ -1702,8 +1706,8 @@ def involution_map(self): if self.type_of_point() == 2: power = self.power() return self.parent()(ZZ(0), power=-power) - return self.parent()(ZZ(0), RR(1/radius)) - return self.parent()(1/self.center(), RR(radius / (self._custom_abs(self.center())**2))) + return self.parent()(ZZ(0), RR(1 / radius)) + return self.parent()(1 / self.center(), RR(radius / (self._custom_abs(self.center())**2))) new_center_lst = [] new_radius_lst = [] @@ -1713,17 +1717,17 @@ def involution_map(self): if zero_check: continue else: - new_center = 1/self.center()[i] - new_radius = self.radius()[i]/(self._custom_abs(self.center()[i])**2) + new_center = 1 / self.center()[i] + new_radius = self.radius()[i] / (self._custom_abs(self.center()[i])**2) new_center_lst.append(new_center) new_radius_lst.append(new_radius) - if len(new_center_lst) == 0: + if not new_center_lst: raise ValueError('precision of type IV is not high enough to define image') return self.parent()(new_center_lst, new_radius_lst, error_check=False) def contained_in_interval(self, start, end): """ - Checks if this point is an element of the interval [``start``, ``end``]. + Check if this point is an element of the interval [``start``, ``end``]. INPUT: @@ -1751,11 +1755,11 @@ def contained_in_interval(self, start, end): True """ if not isinstance(start, Berkovich_Element_Cp_Affine): - raise TypeError("start must be a point of affine Berkovich space. start was %s" %start) + raise TypeError("start must be a point of affine Berkovich space. start was %s" % start) if start.parent() != self.parent(): raise ValueError("start must be a point of the same Berkovich space as this point") if not isinstance(end, Berkovich_Element_Cp_Affine): - raise TypeError("end must be a point of affine Berkovich space. end was %s" %end) + raise TypeError("end must be a point of affine Berkovich space. end was %s" % end) if end.parent() != self.parent(): raise ValueError("end must be a point of the same Berkovich space as this point") @@ -1764,6 +1768,7 @@ def contained_in_interval(self, start, end): proj_end = end.as_projective_point() return proj_self.contained_in_interval(proj_start, proj_end) + class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): r""" Element class of the Berkovich projective line over `\CC_p`. @@ -1881,6 +1886,7 @@ class Berkovich_Element_Cp_Projective(Berkovich_Element_Cp): sage: Q1 = B(3) sage: TestSuite(Q1).run() """ + def __init__(self, parent, center, radius=None, power=None, prec=20, error_check=True): """ Initialization function. @@ -1892,20 +1898,20 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, error_check sage: P(0,1) Type II point centered at (0 : 1 + O(7^20)) of radius 7^0 """ - #if we are given a point of Affine Berkovich Space, we do the conversion - #otherwise we call the Berkovich_Element_Cp constructor with space_type="projective" + # if we are given a point of Affine Berkovich Space, we do the conversion + # otherwise we call the Berkovich_Element_Cp constructor with space_type="projective" Element.__init__(self, parent) self._p = parent.prime() self._base_space = parent.base() self._base_type = parent._base_type self._ideal = parent._ideal - #conversion from Affine points is handled in this constructor + # conversion from Affine points is handled in this constructor if isinstance(center, Berkovich_Element_Cp_Affine): raise TypeError('use as_projective_point to convert to projective Berkovich space') - Berkovich_Element_Cp.__init__(self, parent=parent, center=center, radius=radius, power=power, \ - prec=prec, space_type="projective", error_check=error_check) + Berkovich_Element_Cp.__init__(self, parent=parent, center=center, radius=radius, power=power, + prec=prec, space_type="projective", error_check=error_check) def as_affine_point(self): """ @@ -2003,13 +2009,13 @@ def __eq__(self, other): return self.center() == other.center() elif stype == otype and stype == 4: raise NotImplementedError("equality for type IV points not implemented") - elif stype in [2,3] and otype in [2,3]: + elif stype in [2, 3] and otype in [2, 3]: if self.radius() != other.radius(): return False scent = self.center()[0] ocent = other.center()[0] center_dist = self._custom_abs(scent - ocent) - return center_dist <= self.radius() + return center_dist <= self.radius() else: return False @@ -2114,7 +2120,7 @@ def lt(self, other): False """ if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) + raise TypeError('other must be a point of a projective Berkovich space, but was %s' % other) if self.parent() != other.parent(): raise ValueError('other must be a point of the same projective Berkovich space') @@ -2122,7 +2128,7 @@ def lt(self, other): return False # infinity is maximal with respect to the standard partial order - infinity = self.parent()((1,0)) + infinity = self.parent()((1, 0)) if self == infinity: return False if other == infinity: @@ -2204,14 +2210,14 @@ def gt(self, other): False """ if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich space, but was %s' %other) + raise TypeError('other must be a point of a projective Berkovich space, but was %s' % other) if self.parent() != other.parent(): raise ValueError('other must be a point of the same projective Berkovich space') if self == other: return False # infinity is maximal with respect to the standard partial order - infinity = self.parent()((1,0)) + infinity = self.parent()((1, 0)) if self == infinity: return True if other == infinity: @@ -2229,7 +2235,7 @@ def gt(self, other): def join(self, other, basepoint=Infinity): """ - Computes the join of this point and ``other``, with respect to ``basepoint``. + Compute the join of this point and ``other``, with respect to ``basepoint``. The join is first point that lies on the intersection of the path from this point to ``basepoint`` and the path from ``other`` to @@ -2300,29 +2306,29 @@ def join(self, other, basepoint=Infinity): Type II point centered at (0 : 1) of radius 3^0 """ if not isinstance(other, Berkovich_Element_Cp_Projective): - raise TypeError('other must be a point of a projective Berkovich line, instead was %s' %other) + raise TypeError('other must be a point of a projective Berkovich line, instead was %s' % other) if other.parent() != self.parent(): raise ValueError('other must be a point of the same projective Berkovich line') - #if either self or other is type IV, we use the last disk in the approximation + # if either self or other is type IV, we use the last disk in the approximation if self.type_of_point() == 4: new_center = self.center()[-1] new_radius = self.radius()[-1] return self.parent()(new_center, new_radius).join(other) - if other.type_of_point() == 4: + if other.type_of_point() == 4: new_center = other.center()[-1] new_radius = other.radius()[-1] return self.join(self.parent()(new_center, new_radius)) - #we deal with the point at infinity as a special case - infty = self.parent()((1,0)) + # we deal with the point at infinity as a special case + infty = self.parent()((1, 0)) if basepoint == Infinity or basepoint == infty: if self == infty or other == infty: return infty dist = self._custom_abs(self.center()[0] - other.center()[0]) maximum = max(dist, self.radius(), other.radius()) - #optimize for when self or other are type II + # optimize for when self or other are type II if maximum == self.radius() and self.type_of_point() == 2: return self.parent()(self.center(), power=self.power()) if maximum == other.radius() and other.type_of_point() == 2: @@ -2330,11 +2336,11 @@ def join(self, other, basepoint=Infinity): return self.parent()(self.center(), maximum) if not isinstance(basepoint, Berkovich_Element_Cp_Projective): - raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' %basepoint) + raise TypeError('basepoint must be a point of a projective Berkovich line, instead was %s' % basepoint) if basepoint.parent() != self.parent(): raise ValueError("basepoint must be a point of the same Berkovich projective line") - #if the basepoint is type IV, we use the last disk in the approximation + # if the basepoint is type IV, we use the last disk in the approximation if basepoint.type_of_point() == 4: new_center = other.center()[-1] new_radius = other.radius()[-1] @@ -2352,32 +2358,32 @@ def join(self, other, basepoint=Infinity): s_ge_o = self.gt(other) or self == other s_lt_o = self.lt(other) - #we deal with all the cases where self and other are not comparable first - if not (s_lt_o or s_ge_o): + # we deal with all the cases where self and other are not comparable first + if not (s_lt_o or s_ge_o): if not (b_ge_o or b_lt_o): if not (b_ge_s or b_lt_s): - #case where none of the points are comparable + # case where none of the points are comparable dist_b_s = self._custom_abs(self.center()[0] - basepoint.center()[0]) dist_b_o = self._custom_abs(other.center()[0] - basepoint.center()[0]) - return self.parent()(basepoint.center(), \ - min(max(dist_b_o, other.radius(), basepoint.radius()), \ - max(dist_b_s, self.radius(), basepoint.radius()))) + return self.parent()(basepoint.center(), + min(max(dist_b_o, other.radius(), basepoint.radius()), + max(dist_b_s, self.radius(), basepoint.radius()))) - #case where self and basepoint are comparable + # case where self and basepoint are comparable else: if b_ge_s: return basepoint else: return self - #case where other and basepoint are comparable + # case where other and basepoint are comparable else: if b_ge_o: return basepoint else: return other - #now the cases where self > other + # now the cases where self > other elif s_ge_o: if not (b_ge_s or b_lt_s): return self @@ -2388,7 +2394,7 @@ def join(self, other, basepoint=Infinity): if b_lt_o: return other - #join is symmetric, so we flip self and other so that self > other + # join is symmetric, so we flip self and other so that self > other else: return other.join(self, basepoint) @@ -2457,7 +2463,7 @@ def involution_map(self): 2*3^14 + 2*3^15 + 2*3^18 + 2*3^19 + 2*3^22 + 2*3^23 + O(3^24) : 1 + O(3^20))] ... with radii [0.00152415790275873, 0.00137174211248285] ... """ - infty = self.parent()((1,0)) + infty = self.parent()((1, 0)) zero = self.parent()(0) if self.type_of_point() == 1: @@ -2465,16 +2471,16 @@ def involution_map(self): return zero if self == zero: return infty - return self.parent()(1/self.center()[0]) + return self.parent()(1 / self.center()[0]) - if self.type_of_point() in [2,3]: + if self.type_of_point() in [2, 3]: zero_contained_in_self = self.gt(zero) if zero_contained_in_self: if self.type_of_point() == 2: power = self.power() return self.parent()(ZZ(0), power=-power) - return self.parent()(ZZ(0), 1/self.radius()) - return self.parent()(1/self.center()[0], self.radius()/(self._custom_abs(self.center()[0])**2)) + return self.parent()(ZZ(0), 1 / self.radius()) + return self.parent()(1 / self.center()[0], self.radius() / (self._custom_abs(self.center()[0])**2)) new_center_lst = [] new_radius_lst = [] @@ -2484,17 +2490,17 @@ def involution_map(self): if zero_check: continue else: - new_center = 1/self.center()[i][0] - new_radius = self.radius()[i]/(self._custom_abs(self.center()[i][0])**2) + new_center = 1 / self.center()[i][0] + new_radius = self.radius()[i] / (self._custom_abs(self.center()[i][0])**2) new_center_lst.append(new_center) new_radius_lst.append(new_radius) - if len(new_center_lst) == 0: + if not new_center_lst: raise ValueError('precision of type IV is not high enough to define image') return self.parent()(new_center_lst, new_radius_lst) def contained_in_interval(self, start, end): """ - Checks if this point is an element of the interval [``start``, ``end``]. + Check if this point is an element of the interval [``start``, ``end``]. INPUT: @@ -2561,22 +2567,22 @@ def contained_in_interval(self, start, end): if end.parent() != self.parent(): raise ValueError("start must be a point of the same Berkovich space as this point") - #we treat infinity as a special case + # we treat infinity as a special case infty = self.parent()((1, 0)) zero = self.parent()(ZZ(0)) if self == infty: if start == zero or end == zero: return end == infty or start == infty - return (self.involution_map()).contained_in_interval(start.involution_map(),\ - end.involution_map()) + return (self.involution_map()).contained_in_interval(start.involution_map(), + end.involution_map()) if start == infty or end == infty: if self == zero: return end == zero or start == zero if start == zero or end == zero: gauss = self.parent()(ZZ(0), ZZ(1)) return self.contained_in_interval(start, gauss) or self.contained_in_interval(gauss, end) - return self.involution_map().contained_in_interval(start.involution_map(), \ - end.involution_map()) + return self.involution_map().contained_in_interval(start.involution_map(), + end.involution_map()) join = start.join(end) j_ge_s = join.gt(self) or join == self s_ge_start = self.gt(start) or self == start From e5e5b6834c3a6f7bd60898c9fc7627c826638d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 4 Jul 2021 11:39:38 +0200 Subject: [PATCH 168/336] trac 32106 detail --- src/sage/misc/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 2b9567cd0b6..d477d34beff 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -793,9 +793,9 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i '{0}x{0}'.format(density), '-trim', filename + '.' + suffix, filename + '.png'] - e = False # it is possible to get through the following commands # without running a program, so in that case we force error + e = False # our standard way of calling programs here; change this if we want # finer-grained analysis of the return code. Think of the output as From e6a697a3eb5d8a12feda9d3920b4830b618943ce Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 4 Jul 2021 14:36:27 +0200 Subject: [PATCH 169/336] truely ignore ignored bounds for ZZ.random_element --- src/sage/rings/integer_ring.pyx | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 2d4aa4eb8af..02560020601 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -709,8 +709,43 @@ cdef class IntegerRing_class(PrincipalIdealDomain): sage: ZZ.random_element(11.0, distribution="gaussian") 5 + TESTS: + + Check that :trac:`32124` is fixed:: + + sage: ZZ.random_element(5, -5, distribution="1/n").parent() is ZZ + True + sage: ZZ.random_element(5, -5, distribution="gaussian").parent() is ZZ + True + sage: ZZ.random_element(5, -5, distribution="mpz_rrandomb").parent() is ZZ + True + + sage: ZZ.random_element(-10, -5, distribution="mpz_rrandomb") + Traceback (most recent call last): + ... + TypeError: x must be > 0 + sage: ZZ.random_element(-10, -5, distribution="gaussian") + Traceback (most recent call last): + ... + TypeError: x must be > 0 + + Checking error messages:: + + sage: ZZ.random_element(-3) + Traceback (most recent call last): + ... + TypeError: x must be > 0 + sage: ZZ.random_element(4, 2) + Traceback (most recent call last): + ... + TypeError: x must be < y """ cdef Integer z = Integer.__new__(Integer) + if distribution == "1/n": + x = None + y = None + elif distribution == "mpz_rrandomb" or distribution == "gaussian": + y = None if x is not None and y is None and x <= 0: raise TypeError("x must be > 0") if x is not None and y is not None and x >= y: From efebf22b51c5ba3c15c52cca9910cc246c74051b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 4 Jul 2021 17:50:07 +0200 Subject: [PATCH 170/336] refresh some annotations using future style --- src/sage/combinat/knutson_tao_puzzles.py | 14 ++++++++------ src/sage/modular/etaproducts.py | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py index 5dd7d2b46ed..39a36751b88 100644 --- a/src/sage/combinat/knutson_tao_puzzles.py +++ b/src/sage/combinat/knutson_tao_puzzles.py @@ -37,6 +37,8 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** +from __future__ import annotations + from sage.plot.graphics import Graphics from sage.plot.polygon import polygon from sage.plot.line import line @@ -301,7 +303,7 @@ def __repr__(self) -> str: self['north'], self['south_east']) - def clockwise_rotation(self) -> 'NablaPiece': + def clockwise_rotation(self) -> NablaPiece: r""" Rotate the Nabla piece by 120 degree clockwise. @@ -320,7 +322,7 @@ def clockwise_rotation(self) -> 'NablaPiece': south_east=self['north'], south_west=self['south_east']) - def half_turn_rotation(self) -> 'DeltaPiece': + def half_turn_rotation(self) -> DeltaPiece: r""" Rotate the Nabla piece by 180 degree. @@ -423,7 +425,7 @@ def __repr__(self) -> str: self['south'], self['north_east']) - def clockwise_rotation(self) -> 'DeltaPiece': + def clockwise_rotation(self) -> DeltaPiece: r""" Rotate the Delta piece by 120 degree clockwise. @@ -442,7 +444,7 @@ def clockwise_rotation(self) -> 'DeltaPiece': north_west=self['south'], north_east=self['north_west']) - def half_turn_rotation(self) -> 'NablaPiece': + def half_turn_rotation(self) -> NablaPiece: r""" Rotate the Delta piece by 180 degree. @@ -559,7 +561,7 @@ def __iter__(self): yield self._north_piece yield self._south_piece - def north_piece(self) -> 'DeltaPiece': + def north_piece(self) -> DeltaPiece: r""" Return the north piece. @@ -574,7 +576,7 @@ def north_piece(self) -> 'DeltaPiece': """ return self._north_piece - def south_piece(self) -> 'NablaPiece': + def south_piece(self) -> NablaPiece: r""" Return the south piece. diff --git a/src/sage/modular/etaproducts.py b/src/sage/modular/etaproducts.py index 4a90b2cecfe..770d690f143 100644 --- a/src/sage/modular/etaproducts.py +++ b/src/sage/modular/etaproducts.py @@ -30,7 +30,7 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # *************************************************************************** - +from __future__ import annotations from sage.arith.misc import divisors, prime_divisors, euler_phi, is_square, gcd from sage.categories.groups import Groups @@ -453,7 +453,7 @@ def _repr_(self) -> str: """ return "Group of eta products on X_0(%s)" % self.level() - def one(self) -> 'EtaGroupElement': + def one(self) -> EtaGroupElement: r""" Return the identity element of ``self``. @@ -618,7 +618,7 @@ def reduce_basis(self, long_etas): Element = EtaGroupElement -def EtaProduct(level, dic) -> 'EtaGroupElement': +def EtaProduct(level, dic) -> EtaGroupElement: r""" Create an :class:`EtaGroupElement` object representing the function `\prod_{d | N} \eta(q^d)^{r_d}`. From 1c1d67cf2c9908f8a231096685a31370a5073688 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Jul 2021 10:51:12 -0700 Subject: [PATCH 171/336] ConditionSet: Fix up for CallableSymbolicExpression inputs --- src/sage/sets/condition_set.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index 96d2ae182ea..8f12c6c94cf 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -141,7 +141,7 @@ def __classcall_private__(cls, universe, *predicates, vars=None, names=None, cat if is_CallableSymbolicExpression(predicate): if names is None: names = tuple(str(var) for var in predicate.args()) - elif len(names) != predicates.args(): + elif len(names) != len(predicate.args()): raise TypeError('mismatch in number of arguments') if vars is None: vars = predicate.args() From 89a230a3eb55402584f419c37720222501f7b2f1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Jul 2021 10:52:37 -0700 Subject: [PATCH 172/336] ConditionSet: Sort/uniq the symbolic conditions --- src/sage/sets/condition_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index 8f12c6c94cf..38c851e0ad0 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -155,7 +155,7 @@ def __classcall_private__(cls, universe, *predicates, vars=None, names=None, cat else: other_predicates.append(predicate) - predicates = callable_symbolic_predicates + other_predicates + predicates = sorted(set(callable_symbolic_predicates)) + other_predicates if not other_predicates and not callable_symbolic_predicates: if names is None and category is None: @@ -400,7 +400,7 @@ def _sympy_(self): False sage: Interval = ConditionSet(RR, x >= -7, x <= 4, vars=[x]); Interval - { x ∈ Real Field with 53 bits of precision : x >= -7, x <= 4 } + { x ∈ Real Field with 53 bits of precision : x <= 4, x >= -7 } sage: Interval._sympy_() ConditionSet(x, (x >= -7) & (x <= 4), SageSet(Real Field with 53 bits of precision)) From 28a51bb362092bfd69b12b4761070e1a0f73ca15 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Jul 2021 11:00:12 -0700 Subject: [PATCH 173/336] ConditionSet.intersection: New --- src/sage/sets/condition_set.py | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index 38c851e0ad0..ae535c5fbb3 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -436,3 +436,40 @@ def _sympy_(self): except TypeError: # Fall back to creating a wrapper return super()._sympy_() + + def intersection(self, X): + r""" + Return the intersection of ``self`` and ``X``. + + EXAMPLES:: + + sage: (ZZ^2).rename("ZZ^2"); (QQ^2).rename("QQ^2") + sage: in_small_oblong(x, y) = x^2 + 3 * y^2 <= 42 + sage: SmallOblongUniverse = ConditionSet(QQ^2, in_small_oblong) + sage: SmallOblongUniverse + { (x, y) ∈ QQ^2 : x^2 + 3*y^2 <= 42 } + sage: parity_check(x, y) = abs(sin(pi/2*(x + y))) < 1/1000 + sage: EvenUniverse = ConditionSet(ZZ^2, parity_check); EvenUniverse + { (x, y) ∈ ZZ^2 : abs(sin(1/2*pi*x + 1/2*pi*y)) < (1/1000) } + sage: SmallOblongUniverse & EvenUniverse + { (x, y) ∈ Free module of degree 2 and rank 2 over Integer Ring + Echelon basis matrix: + [1 0] + [0 1] : x^2 + 3*y^2 <= 42, abs(sin(1/2*pi*x + 1/2*pi*y)) < (1/1000) } + + Combining two ``ConditionSet``s with different formal variables works correctly. + The formal variables of the intersection are taken from ``self``:: + + sage: SmallMirrorUniverse = ConditionSet(QQ^2, in_small_oblong, vars=(y, x)) + sage: SmallMirrorUniverse + { (y, x) ∈ QQ^2 : 3*x^2 + y^2 <= 42 } + sage: SmallOblongUniverse & SmallMirrorUniverse + { (x, y) ∈ QQ^2 : x^2 + 3*y^2 <= 42 } + sage: SmallMirrorUniverse & SmallOblongUniverse + { (y, x) ∈ QQ^2 : 3*x^2 + y^2 <= 42 } + """ + if isinstance(X, ConditionSet): + return ConditionSet(self.ambient().intersection(X.ambient()), + *(self._predicates + X._predicates), + vars=self.arguments()) + return super().intersection(X) From 22bcaecaba631c265bc4a7c1d5ebedf401ba9b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 4 Jul 2021 08:24:06 +0200 Subject: [PATCH 174/336] various details in combinat (removal of #py2) --- src/sage/combinat/combinatorial_map.py | 34 ++++++------------- .../non_symmetric_macdonald_polynomials.py | 16 ++------- src/sage/combinat/set_partition_ordered.py | 4 +-- .../combinat/species/composition_species.py | 5 ++- src/sage/combinat/subset.py | 19 ++--------- src/sage/combinat/tutorial.py | 7 +--- src/sage/combinat/words/suffix_trees.py | 2 +- 7 files changed, 20 insertions(+), 67 deletions(-) diff --git a/src/sage/combinat/combinatorial_map.py b/src/sage/combinat/combinatorial_map.py index 945ec225d4e..bc255d9527f 100644 --- a/src/sage/combinat/combinatorial_map.py +++ b/src/sage/combinat/combinatorial_map.py @@ -18,9 +18,7 @@ By default, this decorator is a no-op: it returns the decorated method as is:: - sage: MyPermutation.reverse # py2 - - sage: MyPermutation.reverse # py3 + sage: MyPermutation.reverse See :func:`combinatorial_map_wrapper` for the various options this @@ -95,14 +93,10 @@ def combinatorial_map_trivial(f=None, order=None, name=None): ....: ''' ....: # ... code ... - sage: MyPermutation.reverse # py2 - - sage: MyPermutation.reverse # py3 + sage: MyPermutation.reverse - sage: MyPermutation.descent_set # py2 - - sage: MyPermutation.descent_set # py3 + sage: MyPermutation.descent_set """ if f is None: @@ -182,9 +176,7 @@ def combinatorial_map_wrapper(f=None, order=None, name=None): The method ``major_index`` defined about is not a combinatorial map:: - sage: MyPermutation.major_index # py2 - - sage: MyPermutation.major_index # py3 + sage: MyPermutation.major_index But one can define a function that turns ``major_index`` into a combinatorial map:: @@ -255,8 +247,7 @@ def __repr__(self): EXAMPLES:: sage: sage.combinat.combinatorial_map.combinatorial_map = sage.combinat.combinatorial_map.combinatorial_map_wrapper - sage: from imp import reload # py2 - sage: from importlib import reload # py3 + sage: from importlib import reload sage: _ = reload(sage.combinat.permutation) sage: p = Permutation([1,3,2,4]) sage: p.left_tableau.__repr__() @@ -271,8 +262,7 @@ def _sage_src_lines_(self): EXAMPLES:: sage: sage.combinat.combinatorial_map.combinatorial_map = sage.combinat.combinatorial_map.combinatorial_map_wrapper - sage: from imp import reload # py2 - sage: from importlib import reload # py3 + sage: from importlib import reload sage: _ = reload(sage.combinat.permutation) sage: p = Permutation([1,3,2,4]) sage: cm = p.left_tableau; cm @@ -293,8 +283,7 @@ def __get__(self, inst, cls=None): EXAMPLES:: sage: sage.combinat.combinatorial_map.combinatorial_map = sage.combinat.combinatorial_map.combinatorial_map_wrapper - sage: from imp import reload # py2 - sage: from importlib import reload # py3 + sage: from importlib import reload sage: _ = reload(sage.combinat.permutation) sage: p = Permutation([1,3,2,4]) sage: p.left_tableau #indirect doctest @@ -310,8 +299,7 @@ def __call__(self, *args, **kwds): EXAMPLES:: sage: sage.combinat.combinatorial_map.combinatorial_map = sage.combinat.combinatorial_map.combinatorial_map_wrapper - sage: from imp import reload # py2 - sage: from importlib import reload # py3 + sage: from importlib import reload sage: _ = reload(sage.combinat.permutation) sage: p = Permutation([1,3,2,4]) sage: cm = type(p).left_tableau; cm @@ -337,8 +325,7 @@ def unbounded_map(self): EXAMPLES:: sage: sage.combinat.combinatorial_map.combinatorial_map = sage.combinat.combinatorial_map.combinatorial_map_wrapper - sage: from imp import reload # py2 - sage: from importlib import reload # py3 + sage: from importlib import reload sage: _ = reload(sage.combinat.permutation) sage: from sage.combinat.permutation import Permutation sage: pi = Permutation([1,3,2]) @@ -403,8 +390,7 @@ def combinatorial_maps_in_class(cls): EXAMPLES:: sage: sage.combinat.combinatorial_map.combinatorial_map = sage.combinat.combinatorial_map.combinatorial_map_wrapper - sage: from imp import reload # py2 - sage: from importlib import reload # py3 + sage: from importlib import reload sage: _ = reload(sage.combinat.permutation) sage: from sage.combinat.combinatorial_map import combinatorial_maps_in_class sage: p = Permutation([1,3,2,4]) diff --git a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py index ee8ddec7ddb..f1dc9f608b4 100644 --- a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +++ b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py @@ -3,7 +3,7 @@ AUTHORS: -- Anne Schilling and Nicolas M. Thiery (2013): initial version +- Anne Schilling and Nicolas M. Thiéry (2013): initial version ACKNOWLEDGEMENTS: @@ -218,19 +218,7 @@ class NonSymmetricMacdonaldPolynomials(CherednikOperatorsEigenvectors): Lazy family (...)_{i in Coroot lattice of the Root system of type ['C', 2, 1]} sage: alphacheck = Y.keys().simple_roots() sage: Y1 = Y[alphacheck[1]] - sage: Y1(x) # py2 - ((q1^3+q1^2*q2)/(-q2^3))*B[-e[0] - 2*e[1] - e['delta'] + e['deltacheck']] - + ((q1^3+2*q1^2*q2+q1*q2^2)/(-q2^3))*B[-e[0] - e['delta'] + e['deltacheck']] - + ((q1^3+q1^2*q2)/(-q2^3))*B[e[0] - 2*e[1] - 2*e['delta'] + e['deltacheck']] - + ((q1^3+q1^2*q2)/(-q2^3))*B[e[0] - 2*e[1] - e['delta'] + e['deltacheck']] - + ((q1^3+2*q1^2*q2+q1*q2^2)/(-q2^3))*B[e[0] - 2*e['delta'] + e['deltacheck']] - + ((q1^3+q1^2*q2)/(-q2^3))*B[e[0] - e['delta'] + e['deltacheck']] + ((q1^2+2*q1*q2+q2^2)/(-q1*q2))*B[e[0] + e['deltacheck']] - + ((q1^3+q1^2*q2)/(-q2^3))*B[2*e[0] - e[1] - 2*e['delta'] + e['deltacheck']] - + ((-q1^2-q1*q2)/(-q2^2))*B[2*e[0] - e[1] - e['delta'] + e['deltacheck']] + (q1^3/(-q2^3))*B[3*e[0] - 2*e[1] - 3*e['delta'] + e['deltacheck']] - + ((q1^3+q1^2*q2)/(-q2^3))*B[3*e[0] - 3*e['delta'] + e['deltacheck']] - + ((q1^3+2*q1^2*q2+q1*q2^2)/(-q2^3))*B[-e[1] - e['delta'] + e['deltacheck']] - + ((-q1^2-2*q1*q2-q2^2)/(-q2^2))*B[-e[1] + e['deltacheck']] + ((q1+q2)/(-q2))*B[e[1] + e['deltacheck']] - sage: Y1(x) # py3 + sage: Y1(x) ((q1^2+2*q1*q2+q2^2)/(-q1*q2))*B[e[0] + e['deltacheck']] + ((-q1^2-2*q1*q2-q2^2)/(-q2^2))*B[-e[1] + e['deltacheck']] + ((-q1^2-q1*q2)/(-q2^2))*B[2*e[0] - e[1] - e['delta'] diff --git a/src/sage/combinat/set_partition_ordered.py b/src/sage/combinat/set_partition_ordered.py index 4b8512e3274..16a453285ad 100644 --- a/src/sage/combinat/set_partition_ordered.py +++ b/src/sage/combinat/set_partition_ordered.py @@ -882,9 +882,7 @@ class OrderedSetPartitions(UniqueRepresentation, Parent): :: sage: OS = OrderedSetPartitions("cat") - sage: OS # py2 - Ordered set partitions of {'a', 'c', 't'} - sage: OS # py3 random + sage: OS # random Ordered set partitions of {'a', 't', 'c'} sage: sorted(OS.list(), key=str) [[{'a', 'c', 't'}], diff --git a/src/sage/combinat/species/composition_species.py b/src/sage/combinat/species/composition_species.py index 5a3a2cebb8f..efc77ce508b 100644 --- a/src/sage/combinat/species/composition_species.py +++ b/src/sage/combinat/species/composition_species.py @@ -62,7 +62,7 @@ def transport(self, perm): f, gs = self._list pi = self._partition.transport(perm) f = f.change_labels(pi._list) - g = [g.change_labels(part) for g,part in zip(gs, pi)] + g = [g.change_labels(part) for g, part in zip(gs, pi)] # BUG HERE ? return self.__class__(self, self._labels, pi, f, gs) def change_labels(self, labels): @@ -80,7 +80,6 @@ def change_labels(self, labels): EXAMPLES:: - sage: p = PermutationGroupElement((2,3)) sage: E = species.SetSpecies(); C = species.CycleSpecies() sage: L = E(C) sage: S = L.structures(['a','b','c']).list() @@ -92,7 +91,7 @@ def change_labels(self, labels): f, gs = self._list pi = self._partition.change_labels(labels) f = f.change_labels(list(pi)) - g = [g.change_labels(part) for g,part in zip(gs, pi)] + g = [g.change_labels(part) for g, part in zip(gs, pi)] return self.__class__(self, labels, pi, f, g) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 057e5a36a56..924cf7c8432 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -135,13 +135,7 @@ def Subsets(s, k=None, submultiset=False): sage: S3 = Subsets(S2) sage: S3.cardinality() 115792089237316195423570985008687907853269984665640564039457584007913129639936 - sage: S3.unrank(14123091480) # py2 - {{{1, 3}, {1, 2, 3}, {2}, {1}}, - {{2}, {1, 2, 3}, {}, {1, 2}}, - {}, - {{2}, {1, 2, 3}, {}, {3}, {1, 2}}, - {{1, 2, 3}, {}, {1}}, {{2}, {2, 3}, {}, {1, 2}}} - sage: S3.unrank(14123091480) # py3 #random + sage: S3.unrank(14123091480) # random {{{2}, {1, 2, 3}, {1, 2}, {3}, {}}, {{1, 2, 3}, {2}, {1}, {1, 3}}, {{}, {2}, {2, 3}, {1, 2}}, @@ -152,9 +146,7 @@ def Subsets(s, k=None, submultiset=False): sage: T = Subsets(S2, 10) sage: T.cardinality() 278826214642518400 - sage: T.unrank(1441231049) # py2 - {{{3}, {1, 2}, {}, {2, 3}, {1}, {1, 3}, ..., {{2, 3}, {}}, {{}}} - sage: T.unrank(1441231049) # py3 # random + sage: T.unrank(1441231049) # random {{{1, 2, 3}, {2}, {2, 3}}, {{3}, {1, 3}, ..., {3}, {1}, {}, {1, 3}}} """ if k is not None: @@ -197,12 +189,7 @@ class Subsets_s(Parent): Subsets of Subsets of Subsets of Finite Field of size 3 sage: S.cardinality() 115792089237316195423570985008687907853269984665640564039457584007913129639936 - sage: S.unrank(3149254230) # py2 - {{{1, 2}, {0, 1, 2}, {0, 2}, {0, 1}}, - {{1, 2}, {}, {0, 2}, {1}, {0, 1, 2}, {2}}, - {{1, 2}, {0}}, {{1, 2}, {0, 1}, {0, 1, 2}, {1}}, - {{0, 2}, {1}}} - sage: S.unrank(3149254230) # py3 #random + sage: S.unrank(3149254230) # random {{{1}, {0, 2}}, {{0, 1, 2}, {0, 1}, {1}, {1, 2}}, {{2}, {1, 2}, {0, 1, 2}, {0, 2}, {1}, {}}, {{1, 2}, {0}}, diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index a5bb1f31de2..41f199664ad 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -673,12 +673,7 @@ overflows, and would not permit the return of {Infinity} for infinite sets:: - sage: len(S) #py2 - Traceback (most recent call last): - ... - OverflowError: Python int too large to convert to C long - - sage: len(S) #py3 + sage: len(S) Traceback (most recent call last): ... OverflowError: cannot fit 'int' into an index-sized integer diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index 351bfa7e04e..3eebe3efe08 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -1759,7 +1759,7 @@ def walk_chain(u, v, l, start): if final_state[0] == 'explicit': parent = final_state[1] transition = self._find_transition(parent,self._letters[start]) - if transition != None: + if transition is not None: child = transition[1] successful = True depth = 1 From f43b3587eb7bacd0d6af43dcf85867746aaeb5eb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Jul 2021 16:48:00 -0700 Subject: [PATCH 175/336] Chart._init_coordinates: Fix up use of domain --- src/sage/manifolds/chart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 6e26408b720..a68e059a0d8 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -386,10 +386,10 @@ def _init_coordinates(self, coord_list): for prop in coord_properties[1:]: prop1 = prop.strip() if prop1[0:6] == 'period': - if domain.base_field_type() in ['real', 'complex']: + if self._manifold.base_field_type() in ['real', 'complex']: period = SR(prop1[7:]) else: - period = domain.base_field()(prop1[7:]) + period = self._manifold.base_field()(prop1[7:]) self._periods[coord_index + self._sindex] = period else: # prop1 is the coordinate's LaTeX symbol From d9cff76316fec2da8e4f9ae20eabebef5b4eeb4b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Jul 2021 19:21:53 -0700 Subject: [PATCH 176/336] Function._sympy_, SympyConverter.__call__: New --- src/sage/symbolic/expression_conversions.py | 18 ++++++++++++++++++ src/sage/symbolic/function.pyx | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index d6681cb8366..63cef49e0b9 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -18,6 +18,7 @@ import operator as _operator from sage.rings.rational_field import QQ from sage.symbolic.ring import SR +from sage.symbolic.callable import is_CallableSymbolicExpression from sage.functions.all import exp from sage.symbolic.operators import arithmetic_operators, relation_operators, FDerivativeOperator, add_vararg, mul_vararg from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_gaussian @@ -693,6 +694,23 @@ def __init__(self): from sage.interfaces.sympy import sympy_init sympy_init() + def __call__(self, ex=None): + """ + EXAMPLES:: + + sage: from sage.symbolic.expression_conversions import SympyConverter + sage: s = SympyConverter() + sage: f(x, y) = x^2 + y^2; f + (x, y) |--> x^2 + y^2 + sage: s(f) + Lambda((x, y), x**2 + y**2) + """ + if is_CallableSymbolicExpression(ex): + from sympy import Symbol, Lambda + return Lambda(tuple(Symbol(str(arg)) for arg in ex.arguments()), + super().__call__(ex)) + return super().__call__(ex) + def pyobject(self, ex, obj): """ EXAMPLES:: diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index ff3c51c7680..125d228f646 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -129,6 +129,7 @@ from sage.libs.pynac.pynac cimport * from sage.rings.integer cimport smallInteger from sage.structure.sage_object cimport SageObject from sage.structure.element cimport Element, parent +from sage.misc.lazy_attribute import lazy_attribute from .expression cimport new_Expression_from_GEx, Expression from .ring import SR @@ -750,6 +751,24 @@ cdef class Function(SageObject): """ return self._conversions.get('sympy', self._name) + @lazy_attribute + def _sympy_(self): + """ + EXAMPLES:: + + sage: cos._sympy_() + cos + sage: _(0) + 1 + """ + f = self._sympy_init_() + import sympy + if getattr(sympy, f, None): + def return_sympy(): + return getattr(sympy, f) + return return_sympy + return NotImplemented + def _maxima_init_(self, I=None): """ EXAMPLES:: From b736c9693ba218571ef3d0d7f46623a4a303e3a6 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 5 Jul 2021 10:55:41 +0200 Subject: [PATCH 177/336] more natural treating of small cases for random tree --- src/sage/graphs/generators/random.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index d73b7ab64c0..f0807286d64 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -1322,7 +1322,9 @@ def RandomTree(n): Graph on 1 vertex """ from sage.misc.prandom import randint - g = Graph() + g = Graph(n) + if n <= 1: + return g # create random Prufer code code = [ randint(0,n-1) for i in range(n-2) ] @@ -1336,8 +1338,6 @@ def RandomTree(n): for k in code: count[k] += 1 - g.add_vertices(range(n)) - for s in code: for x in range(n): if count[x] == 0: @@ -1347,10 +1347,9 @@ def RandomTree(n): g.add_edge(x,s) count[s] -= 1 - if n > 1: - # Adding as an edge the last two available vertices - last_edge = [ v for v in range(n) if count[v] != -1 ] - g.add_edge(last_edge) + # Adding as an edge the last two available vertices + last_edge = [ v for v in range(n) if count[v] != -1 ] + g.add_edge(last_edge) return g From 1d021c1b83ddfe31847bc595008496ff40e18b53 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 5 Jul 2021 10:58:38 +0200 Subject: [PATCH 178/336] fix orbit lenght of gyration --- src/sage/combinat/alternating_sign_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index c3b690f5bdd..7dc60852255 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -604,7 +604,7 @@ def gyration(self): [0 0 1 0] sage: a0 = a = AlternatingSignMatrices(5).random_element() - sage: for i in range(10): + sage: for i in range(20): ....: a = a.gyration() sage: a == a0 True From d81dd568b7a5ea356c5bfc686f0738b4f892e66b Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 5 Jul 2021 12:00:44 +0200 Subject: [PATCH 179/336] register translations of expressions at class init --- src/sage/interfaces/fricas.py | 151 +++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 67 deletions(-) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 0d8539087e7..30c888949a0 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -207,7 +207,7 @@ from sage.rings.rational_field import QQ from sage.rings.infinity import infinity from sage.misc.lazy_import import lazy_import -lazy_import('sage.libs.pynac.pynac', ['symbol_table']) +lazy_import('sage.libs.pynac.pynac', ['symbol_table', 'register_symbol']) lazy_import('sage.calculus.var', ['var', 'function']) lazy_import('sage.symbolic.constants', ['I', 'e', 'pi']) @@ -332,6 +332,8 @@ def _start(self): self.eval(FRICAS_LINENUMBER_OFF_CODE, reformat=False) for line in FRICAS_HELPER_CODE: self.eval(line, reformat=False) + # register translations between SymbolicRing and FriCAS Expression + self._register_symbols() def _install_hints(self): """ @@ -573,6 +575,81 @@ def _check_errors(self, line, output): if FRICAS_ERROR_IN_LIBRARY_CODE in output: raise RuntimeError("An error occurred when FriCAS evaluated '%s':\n%s" % (line, output)) + @staticmethod + def _register_symbols(): + """ + Register translations between elements of the ``SymbolicRing`` and FriCAS ``Expression`` domain. + + This method is called from :meth:`_start`, to work around a + circular import problem involving ``pi``. + + """ + from sage.calculus.functional import diff + from sage.functions.log import dilog, lambert_w + from sage.functions.trig import sin, cos, tan, cot, sec, csc + from sage.functions.hyperbolic import tanh, sinh, cosh, coth, sech, csch + from sage.functions.other import abs + from sage.functions.gamma import gamma + from sage.misc.functional import symbolic_sum, symbolic_prod + register_symbol(pi, {'fricas': 'pi'}) # pi is also a function in fricas + register_symbol(cos, {'fricas': 'cos'}) + register_symbol(sin, {'fricas': 'sin'}) + register_symbol(tan, {'fricas': 'tan'}) + register_symbol(cot, {'fricas': 'cot'}) + register_symbol(sec, {'fricas': 'sec'}) + register_symbol(csc, {'fricas': 'csc'}) + register_symbol(tanh, {'fricas': 'tanh'}) + register_symbol(sinh, {'fricas': 'sinh'}) + register_symbol(cosh, {'fricas': 'cosh'}) + register_symbol(coth, {'fricas': 'coth'}) + register_symbol(sech, {'fricas': 'sech'}) + register_symbol(csch, {'fricas': 'csch'}) + register_symbol(gamma, {'fricas': 'Gamma'}) + register_symbol(lambda x, y: x + y, {'fricas': '+'}) + register_symbol(lambda x, y: x - y, {'fricas': '-'}) + register_symbol(lambda x, y: x * y, {'fricas': '*'}) + register_symbol(lambda x, y: x / y, {'fricas': '/'}) + register_symbol(lambda x, y: x ** y, {'fricas': '^'}) + register_symbol(lambda f, x: diff(f, x), {'fricas': 'D'}) + register_symbol(lambda x, y: x + y * I, {'fricas': 'complex'}) + register_symbol(lambda x: dilog(1 - x), {'fricas': 'dilog'}) + register_symbol(lambda z: lambert_w(z), {'fricas': 'lambertW'}) + register_symbol(abs, {'fricas': 'abs'}) + # construct occurs in the InputForm of hypergeometricF + register_symbol(lambda *x: x, {'fricas': 'construct'}) + # the following is a hack to deal with + # integrate(sin((x^2+1)/x),x)::INFORM giving + # (integral (sin (/ (+ (^ x 2) 1) x)) (:: x Symbol)) + register_symbol(lambda x, y: x, {'fricas': '::'}) + + def _convert_eval(f, a, b): + # it might be that FriCAS also returns a two-argument + # eval, where the second argument is a list of equations, + # in which case this function needs to be adapted + return f.subs({a: b}) + + register_symbol(_convert_eval, {'fricas': 'eval'}) + + def _convert_sum(x, y): + v, seg = y.operands() + a, b = seg.operands() + return symbolic_sum(x, v, a, b) + + def _convert_prod(x, y): + v, seg = y.operands() + a, b = seg.operands() + return symbolic_prod(x, v, a, b) + + register_symbol(_convert_sum, {'fricas': 'sum'}) + register_symbol(_convert_prod, {'fricas': 'product'}) + + def explicitly_not_implemented(*args): + raise NotImplementedError("the translation of the FriCAS Expression '%s' to sage is not yet implemented" % args) + + register_symbol(lambda *args: explicitly_not_implemented("rootOfADE"), {'fricas': 'rootOfADE'}) + register_symbol(lambda *args: explicitly_not_implemented("rootOfRec"), {'fricas': 'rootOfRec'}) + + def set(self, var, value): """ Set a variable to a value in FriCAS. @@ -1401,6 +1478,7 @@ def _parse_string(s, start=0): S.append(s[a:b]) return "".join(S), b + @staticmethod def _sage_expression(fricas_InputForm): r""" @@ -1613,72 +1691,11 @@ def _sage_expression(fricas_InputForm): gamma(3, 2) """ - from sage.libs.pynac.pynac import register_symbol - from sage.calculus.functional import diff - from sage.functions.log import dilog, lambert_w - from sage.functions.trig import sin, cos, tan, cot, sec, csc - from sage.functions.hyperbolic import tanh, sinh, cosh, coth, sech, csch - from sage.functions.other import abs - from sage.functions.gamma import gamma - from sage.misc.functional import symbolic_sum, symbolic_prod - register_symbol(pi, {'fricas': 'pi'}) # pi is also a function in fricas - register_symbol(cos, {'fricas': 'cos'}) - register_symbol(sin, {'fricas': 'sin'}) - register_symbol(tan, {'fricas': 'tan'}) - register_symbol(cot, {'fricas': 'cot'}) - register_symbol(sec, {'fricas': 'sec'}) - register_symbol(csc, {'fricas': 'csc'}) - register_symbol(tanh, {'fricas': 'tanh'}) - register_symbol(sinh, {'fricas': 'sinh'}) - register_symbol(cosh, {'fricas': 'cosh'}) - register_symbol(coth, {'fricas': 'coth'}) - register_symbol(sech, {'fricas': 'sech'}) - register_symbol(csch, {'fricas': 'csch'}) - register_symbol(gamma, {'fricas': 'Gamma'}) - register_symbol(lambda x, y: x + y, {'fricas': '+'}) - register_symbol(lambda x, y: x - y, {'fricas': '-'}) - register_symbol(lambda x, y: x * y, {'fricas': '*'}) - register_symbol(lambda x, y: x / y, {'fricas': '/'}) - register_symbol(lambda x, y: x ** y, {'fricas': '^'}) - register_symbol(lambda f, x: diff(f, x), {'fricas': 'D'}) - register_symbol(lambda x, y: x + y * I, {'fricas': 'complex'}) - register_symbol(lambda x: dilog(1 - x), {'fricas': 'dilog'}) - register_symbol(lambda z: lambert_w(z), {'fricas': 'lambertW'}) - register_symbol(abs, {'fricas': 'abs'}) - # construct occurs in the InputForm of hypergeometricF - register_symbol(lambda *x: x, {'fricas': 'construct'}) - # the following is a hack to deal with - # integrate(sin((x^2+1)/x),x)::INFORM giving - # (integral (sin (/ (+ (^ x 2) 1) x)) (:: x Symbol)) - register_symbol(lambda x, y: x, {'fricas': '::'}) - - def _convert_eval(f, a, b): - # it might be that FriCAS also returns a two-argument - # eval, where the second argument is a list of equations, - # in which case this function needs to be adapted - return f.subs({a: b}) - - register_symbol(_convert_eval, {'fricas': 'eval'}) - - def _convert_sum(x, y): - v, seg = y.operands() - a, b = seg.operands() - return symbolic_sum(x, v, a, b) - - def _convert_prod(x, y): - v, seg = y.operands() - a, b = seg.operands() - return symbolic_prod(x, v, a, b) - - register_symbol(_convert_sum, {'fricas': 'sum'}) - register_symbol(_convert_prod, {'fricas': 'product'}) - - def explicitly_not_implemented(*args): - raise NotImplementedError("the translation of the FriCAS Expression '%s' to sage is not yet implemented" % args) - - register_symbol(lambda *args: explicitly_not_implemented("rootOfADE"), {'fricas': 'rootOfADE'}) - register_symbol(lambda *args: explicitly_not_implemented("rootOfRec"), {'fricas': 'rootOfRec'}) - + # a FriCAS expressions may contain implicit references to a + # rootOf expression within itself, as for example in the + # result of integrate(1/(1+x^5), x). Each algebraic number + # appearing in the expression is only introduced once and + # assigned a variable (usually of the form %%...). rootOf = dict() # (variable, polynomial) rootOf_ev = dict() # variable -> (complex) algebraic number From 516384bfd8c2c40a0bf928c2a74f1c4d3f8921c9 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 5 Jul 2021 12:04:05 +0200 Subject: [PATCH 180/336] add doctest --- src/sage/interfaces/fricas.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 30c888949a0..bc203a22080 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1478,7 +1478,6 @@ def _parse_string(s, start=0): S.append(s[a:b]) return "".join(S), b - @staticmethod def _sage_expression(fricas_InputForm): r""" @@ -1690,6 +1689,24 @@ def _sage_expression(fricas_InputForm): sage: fricas.Gamma(3, 2).sage() # optional - fricas gamma(3, 2) + + Check that :trac:`32133` is fixed:: + + sage: var("y") + sage: f = fricas.zerosOf (y^4 + y + 1, y); f # optional - fricas + +-----------------------------+ + | 2 2 + \|- 3 %y1 - 2 %y0 %y1 - 3 %y0 - %y1 - %y0 + [%y0, %y1, --------------------------------------------, + 2 + +-----------------------------+ + | 2 2 + - \|- 3 %y1 - 2 %y0 %y1 - 3 %y0 - %y1 - %y0 + ----------------------------------------------] + 2 + + sage: f[1].sage() # optional - fricas + """ # a FriCAS expressions may contain implicit references to a # rootOf expression within itself, as for example in the From aa5276a4b970484fd4ff7b72076db9a1d2bf0815 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Mon, 5 Jul 2021 12:57:32 +0200 Subject: [PATCH 181/336] trac #32131: fix cutwidth for single edge graph --- src/sage/graphs/graph_decompositions/cutwidth.pyx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_decompositions/cutwidth.pyx b/src/sage/graphs/graph_decompositions/cutwidth.pyx index d4c1414d50a..2028a7fb3d3 100644 --- a/src/sage/graphs/graph_decompositions/cutwidth.pyx +++ b/src/sage/graphs/graph_decompositions/cutwidth.pyx @@ -358,6 +358,15 @@ def cutwidth(G, algorithm="exponential", cut_off=0, solver=None, verbose=False): Traceback (most recent call last): ... ValueError: the specified cut off parameter must be an integer + + Cutwidth of a graph with one edge (trac:`32131`):: + + sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth + sage: G = Graph([(0, 1)]) + sage: cutwidth(G, algorithm="exponential") + (1, [0, 1]) + sage: cutwidth(G, algorithm="MILP", solver='GLPK') + (1, [0, 1]) """ from sage.graphs.graph import Graph @@ -491,7 +500,7 @@ def cutwidth_dyn(G, lower_bound=0): cdef list order try: - for k in range(lower_bound, G.size()): + for k in range(lower_bound, G.size() + 1): for i in range(g.n): sig_check() if exists(g, neighborhoods, 0, 0, i, k) <= k: From 8488bf4b870e85c198f8b134b17359560de076b4 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Mon, 5 Jul 2021 13:07:40 +0200 Subject: [PATCH 182/336] trac #32131: fix method for non connected graphs --- src/sage/graphs/graph_decompositions/cutwidth.pyx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/cutwidth.pyx b/src/sage/graphs/graph_decompositions/cutwidth.pyx index 2028a7fb3d3..055d05ad9d1 100644 --- a/src/sage/graphs/graph_decompositions/cutwidth.pyx +++ b/src/sage/graphs/graph_decompositions/cutwidth.pyx @@ -367,6 +367,16 @@ def cutwidth(G, algorithm="exponential", cut_off=0, solver=None, verbose=False): (1, [0, 1]) sage: cutwidth(G, algorithm="MILP", solver='GLPK') (1, [0, 1]) + + Cutwidth of a disconnected graph:: + + sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth + sage: G = Graph(5) + sage: G.add_edge(2, 3) + sage: cutwidth(G, algorithm="exponential") + (1, [2, 3, 0, 1, 4]) + sage: cutwidth(G, algorithm="MILP", solver='GLPK') + (1, [2, 3, 0, 1, 4]) """ from sage.graphs.graph import Graph @@ -403,10 +413,10 @@ def cutwidth(G, algorithm="exponential", cut_off=0, solver=None, verbose=False): else: # We have a connected graph and we call the desired algorithm if algorithm == "exponential": - cwH, LH = cutwidth_dyn(G, lower_bound=this_cut_off) + cwH, LH = cutwidth_dyn(H, lower_bound=this_cut_off) elif algorithm == "MILP": - cwH, LH = cutwidth_MILP(G, lower_bound=this_cut_off, solver=solver, verbose=verbose) + cwH, LH = cutwidth_MILP(H, lower_bound=this_cut_off, solver=solver, verbose=verbose) else: raise ValueError('algorithm "{}" has not been implemented yet, please contribute'.format(algorithm)) From ccd88447a03bbc1e3425efb413ccca9a0533f608 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 5 Jul 2021 22:02:01 +1000 Subject: [PATCH 183/336] Fixing typos with quantum Clifford algebra. --- src/doc/en/reference/references/index.rst | 4 ++-- src/sage/algebras/quantum_clifford.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index f58cabaa203..5151415bf1b 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2732,7 +2732,7 @@ REFERENCES: Press (2002). .. [Hayashi1990] \T. Hayashi. `q`-*analogues of Clifford and Weyl algebras - - spinor and oscillator representationsof quantum enveloping + spinor and oscillator representations of quantum enveloping algebras*. Comm. Math. Phys. **127** (1990) pp. 129-144. .. [HC2006] Mark van Hoeij and John Cremona, Solving Conics over @@ -3574,7 +3574,7 @@ REFERENCES: .. [Kwon2014] Jae-Hoon Kwon. `q`-*deformed Clifford algebra and level zero fundamental representations of quantum affine algebras*. - J. ALgebra, **399** (2014), pp. 927--947. + J. Algebra, **399** (2014), pp. 927--947. :doi:`10.1016/j.jalgebra.2013.10.026`. .. [KX1998] \S. König and C. Xi. diff --git a/src/sage/algebras/quantum_clifford.py b/src/sage/algebras/quantum_clifford.py index 88abe30b518..933277e3564 100644 --- a/src/sage/algebras/quantum_clifford.py +++ b/src/sage/algebras/quantum_clifford.py @@ -13,7 +13,7 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** from sage.misc.cachefunc import cached_method @@ -99,7 +99,7 @@ class QuantumCliffordAlgebra(CombinatorialFreeModule): (q^2+1)*w0*w1 - q^2*w0*w1^3, (q^2+1)*w1*w2 - q^2*w1*w2^3, (q^2+1)*w0*w1 - q^2*w0^3*w1, (q^2+1)*w1*w2 - q^2*w1^3*w2) - We check that `k_i` and `k_i^{-1}` are invereses:: + We check that `k_i` and `k_i^{-1}` are inverses:: sage: k1 * k1i 1 @@ -432,7 +432,7 @@ def product_on_basis(self, m1, m2): # we know p1[i] != p2[i] if non-zero, so their sum is -1, 0, 1 p[i] = p1[i] + p2[i] - supported.append(self._n-1) # To get betweent the last support and the end + supported.append(self._n-1) # To get between the last support and the end # Get the sign of moving \psi_i and \psi_i^{\dagger} into position for i in reversed(range(1, len(supported))): if i % 2 != 0: From 5298d89f3ff43d4af30c97633e97b63a7bbbe882 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Mon, 5 Jul 2021 20:11:42 +0200 Subject: [PATCH 184/336] trac #32131: correct trac link syntax --- src/sage/graphs/graph_decompositions/cutwidth.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_decompositions/cutwidth.pyx b/src/sage/graphs/graph_decompositions/cutwidth.pyx index 055d05ad9d1..0a8678b4a2b 100644 --- a/src/sage/graphs/graph_decompositions/cutwidth.pyx +++ b/src/sage/graphs/graph_decompositions/cutwidth.pyx @@ -359,7 +359,7 @@ def cutwidth(G, algorithm="exponential", cut_off=0, solver=None, verbose=False): ... ValueError: the specified cut off parameter must be an integer - Cutwidth of a graph with one edge (trac:`32131`):: + Cutwidth of a graph with one edge (:trac:`32131`):: sage: from sage.graphs.graph_decompositions.cutwidth import cutwidth sage: G = Graph([(0, 1)]) From 5a6495784023709ee8c3f9febf2ba664e70a4dc5 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 5 Jul 2021 22:02:19 +0200 Subject: [PATCH 185/336] compute with minimal polynomials whenever possible --- src/sage/interfaces/fricas.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index bc203a22080..16563a8e044 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1693,7 +1693,8 @@ def _sage_expression(fricas_InputForm): Check that :trac:`32133` is fixed:: sage: var("y") - sage: f = fricas.zerosOf (y^4 + y + 1, y); f # optional - fricas + y + sage: f = fricas.zerosOf(y^4 + y + 1, y); f # optional - fricas +-----------------------------+ | 2 2 \|- 3 %y1 - 2 %y0 %y1 - 3 %y0 - %y1 - %y0 @@ -1706,6 +1707,7 @@ def _sage_expression(fricas_InputForm): 2 sage: f[1].sage() # optional - fricas + -1/2*sqrt(1/3)*sqrt((3*(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(2/3) + 4)/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) + 1/2*sqrt(-(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3) + 6*sqrt(1/3)/sqrt((3*(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(2/3) + 4)/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) - 4/3/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) """ # a FriCAS expressions may contain implicit references to a @@ -1740,20 +1742,24 @@ def convert_rootOf(x, y): assert False, "circular dependency in rootOf expression" # substitute known roots poly = poly.subs(rootOf_ev) - evars = [v for v in rvars if v not in rootOf] # extraneous variables - assert set(evars) == set(poly.variables()).difference([var]) + evars = set(poly.variables()).difference([var]) del rootOf[var] if evars: - # we just need any root per FriCAS specification + # we just need any root per FriCAS specification - + # however, if there are extra variables, we cannot + # use QQbar.any_root rootOf_ev[var] = poly.roots(var, multiplicities=False)[0] else: R = PolynomialRing(QQbar, "x") - # PolynomialRing does not accept variable names with leading underscores + # PolynomialRing does not accept variable names with + # leading underscores poly = R(poly.subs({var: R.gen()})) # we just need any root per FriCAS specification - rootOf_ev[var] = poly.roots(multiplicities=False)[0].radical_expression() + rootOf_ev[var] = poly.any_root() - return ex.subs(rootOf_ev) + return ex.subs({var: (val.radical_expression() + if val.parent() is QQbar else val) + for var, val in rootOf_ev.items()}) def _sage_(self): r""" From 0be7871fd408a9fd19848a71f04cb05a62ecc26b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 6 Jul 2021 12:37:55 +1000 Subject: [PATCH 186/336] Some last little details. --- src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx | 2 +- src/sage/rings/padics/padic_ext_element.pyx | 2 +- src/sage/rings/padics/padic_generic.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index b88ca008866..7a4434aee90 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -1012,7 +1012,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. - The \emph{rows} of this matrix give the images of each of the `x^i`. + The **rows** of this matrix give the images of each of the `x^i`. The entries of the matrices are ``IntegerMod`` elements, defined modulo ``p^(self.absprec() / e)``. diff --git a/src/sage/rings/padics/padic_ext_element.pyx b/src/sage/rings/padics/padic_ext_element.pyx index fcf74c412e3..6bd46612385 100644 --- a/src/sage/rings/padics/padic_ext_element.pyx +++ b/src/sage/rings/padics/padic_ext_element.pyx @@ -309,7 +309,7 @@ cdef class pAdicExtElement(pAdicGenericElement): INPUT: - ``pos`` -- boolean; if ``True``, all integers will be in the range - `[0,p-1]`, otherwise they will be in the range `[(1-p)/2, p/2]`. + `[0,p-1]`, otherwise they will be in the range `[(1-p)/2, p/2]` OUTPUT: diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index b099764f8ae..4c74bdb8a65 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -342,7 +342,7 @@ def residue_system(self): def _fraction_field_key(self, print_mode=None): r""" - Changes ``print_mode`` from a dictionary to a tuple and raises + Change ``print_mode`` from a dictionary to a tuple, raising a deprecation warning if it is present. EXAMPLES:: From d2e8c8f402167c5ecc0fe0ee6514ce9e21fa6bd1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Jul 2021 21:59:46 -0700 Subject: [PATCH 187/336] symbolic_expression: Update doctest output --- src/sage/calculus/all.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/calculus/all.py b/src/sage/calculus/all.py index 2aefda65f56..7d183422f16 100644 --- a/src/sage/calculus/all.py +++ b/src/sage/calculus/all.py @@ -100,7 +100,9 @@ def symbolic_expression(x): sage: f = symbolic_expression(lambda z: z^2 + 1); f z |--> z^2 + 1 sage: f.parent() + Callable function ring with argument z sage: f(7) + 50 If ``x`` is a list or tuple of functions, or if ``x`` is a function that returns a list or tuple, create a callable symbolic vector:: From a890c4d0b800bc014f2ccdcca7a87a71b280a6b8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 6 Jul 2021 09:17:20 +0200 Subject: [PATCH 188/336] one more test from modular --- src/sage/modular/modform/numerical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/modform/numerical.py b/src/sage/modular/modform/numerical.py index 5cad646379d..0d467ef2c0b 100644 --- a/src/sage/modular/modform/numerical.py +++ b/src/sage/modular/modform/numerical.py @@ -214,7 +214,7 @@ def _eigenvectors(self): sage: evalues = [(matrix((n._hecke_matrix*evectors).column(i))/matrix(evectors.column(i)))[0, 0] ....: for i in range(evectors.ncols())] sage: diff = n._hecke_matrix*evectors - evectors*diagonal_matrix(evalues) - sage: sum([abs(diff[i,j]) for i in range(5) for j in range(3)]) < 1.0e-9 + sage: sum(abs(a) for a in diff.list()) < 1.0e-9 True """ verbose('Finding eigenvector basis') From 89fd4b20f1e4c08b3dd245a90d75a3a3782085e5 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 6 Jul 2021 09:14:01 +0200 Subject: [PATCH 189/336] one more missing test from groups --- src/sage/groups/perm_gps/permgroup.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 2022bb69f7b..e5548753715 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -2893,8 +2893,12 @@ def as_finitely_presented_group(self, reduced=False): sage: S = SymmetricGroup(6) sage: perm_ls = [S.random_element() for i in range(3)] sage: G = PermutationGroup(perm_ls) - sage: G.as_finitely_presented_group().as_permutation_group().is_isomorphic(G) - True + sage: while True: + ....: try: + ....: assert G.as_finitely_presented_group().as_permutation_group().is_isomorphic(G) # sometimes results in GAP error (see :trac:`32141`) + ....: break + ....: except ValueError: + ....: pass `D_9` is the only non-Abelian group of order 18 with an automorphism group of order 54 [TW1980]_:: From f1e7ef884b03cf3ad6a7fe09b0b8892acc818063 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 6 Jul 2021 13:43:01 +0200 Subject: [PATCH 190/336] Trac #30272: remove set_name_comp + fix doctest --- .../manifolds/differentiable/mixed_form.py | 85 ++++++------------- 1 file changed, 26 insertions(+), 59 deletions(-) diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index 357cf115c3a..48807f2b30f 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -47,7 +47,10 @@ class MixedForm(AlgebraElement): \forall x\in M, \quad a(x) \in \bigoplus^n_{k=0} \Lambda^k\left( T_{\varphi(x)}^* N \right), where `\Lambda^k(T^*_{\varphi(x)} N)` is the `k`-th exterior power of the - dual of the tangent space `T_{\varphi(x)} N`. + dual of the tangent space `T_{\varphi(x)} N`. Thus, a mixed differential + form `a` consists of homogeneous components `a_i`, `i=0,1, \dots, n`, where + the `i`-th homogeneous component represents a differential form of + degree `i`. The standard case of a mixed form *on* `M` corresponds to `M=N` with `\varphi = \mathrm{Id}_M`. @@ -78,8 +81,8 @@ class MixedForm(AlgebraElement): Graded algebra Omega^*(M) of mixed differential forms on the 2-dimensional differentiable manifold M - The most straightforward way to define the `i`-th homogeneous components - of a mixed form is via :meth:`set_comp` or ``A[i]``:: + The default way to specify the `i`-th homogeneous component + of a mixed form is by accessing it via ``A[i]`` or using :meth:`set_comp`:: sage: A = M.mixed_form(name='A') sage: A[0].set_expr(x) # scalar field @@ -88,7 +91,7 @@ class MixedForm(AlgebraElement): sage: A.display() # display names A = A_0 + A_1 + A_2 sage: A.display_expansion() # display expansion in basis - A = [x] + [x*y dx] + [x*y^2 dx/\dy] + A = x + x*y dx + x*y^2 dx∧dy Another way to define the homogeneous components is using predefined differential forms:: @@ -106,18 +109,19 @@ class MixedForm(AlgebraElement): The components of a mixed form ``B`` can then be set as follows:: - sage: A[:] = [f, omega, eta]; A.display() # display names - A = f + omega + eta + sage: B = M.mixed_form(name='B') + sage: B[:] = [f, omega, eta]; B.display() # display names + B = f + omega + eta sage: A.display_expansion() # display in coordinates A = x + x*y dx + x*y^2 dx∧dy - sage: A[0] + sage: B[0] Scalar field f on the 2-dimensional differentiable manifold M sage: B[1] 1-form omega on the 2-dimensional differentiable manifold M sage: B[2] 2-form eta on the 2-dimensional differentiable manifold M - As we can see, the names are applied. However, the differential + As we can see, the names are applied. However note that the differential forms are different instances:: sage: f is B[0] @@ -181,7 +185,7 @@ class MixedForm(AlgebraElement): sage: A = M.mixed_form(name='A') sage: A[0].set_expr(x, c_xy) sage: A[0].display() - A_0: M → R + A_0: M → ℝ on U: (x, y) ↦ x on W: (u, v) ↦ 1/2*u + 1/2*v sage: A[1][0] = y*x; A[1].display(e_xy) @@ -562,7 +566,7 @@ def display(self): disp = display - def set_name(self, name=None, latex_name=None, set_all=True): + def set_name(self, name=None, latex_name=None, apply_to_comp=True): r""" Redefine the string and LaTeX representation of the object. @@ -571,7 +575,7 @@ def set_name(self, name=None, latex_name=None, set_all=True): - ``name`` -- (default: ``None``) name given to the mixed form - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the mixed form; if none is provided, the LaTeX symbol is set to ``name`` - - ``set_all`` -- (default: ``True``) if ``True`` all homogeneous + - ``apply_to_comp`` -- (default: ``True``) if ``True`` all homogeneous components will be renamed accordingly; if ``False`` only the mixed form will be renamed @@ -600,14 +604,16 @@ def set_name(self, name=None, latex_name=None, set_all=True): Setting the argument ``set_all`` to ``False`` prevents the renaming in the homogeneous components:: - sage: F.set_name(name='eta', latex_name=r'\eta', set_all=False) + sage: F.set_name(name='eta', latex_name=r'\eta', apply_to_comp=False) sage: F.display() eta = F_0 + F_1 + F_2 + F_3 + F_4 - .. SEEALSO:: + To rename a homogeneous component individually, we simply access the + homogeneous component and use its + :meth:`~sage.manifolds.differentiable.tensorfield.set_name` method:: - To rename a homogeneous component individually, use - :meth:`set_name_comp`. + sage: F[0].set_name(name='g'); F.display() + eta = g + F_1 + F_2 + F_3 + F_4 """ if name is not None: @@ -616,41 +622,14 @@ def set_name(self, name=None, latex_name=None, set_all=True): self._latex_name = self._name if latex_name is not None: self._latex_name = latex_name - if set_all: + if apply_to_comp: for i in self.irange(): comp_name, comp_latex_name = None, None if self._name is not None: comp_name = f"{self._name}_{i}" if self._latex_name is not None: comp_latex_name = '{' + self._latex_name + '}_{' + str(i) + '}' - self.set_name_comp(i, name=comp_name, - latex_name=comp_latex_name) - - def set_name_comp(self, i, name=None, latex_name=None): - r""" - Redefine the string and LaTeX representation of the `i`-th - homogeneous component of ``self``. - - INPUT: - - - ``name`` -- (default: ``None``) name given to the mixed form - - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the - mixed form; if none is provided, the LaTeX symbol is set to ``name`` - - EXAMPLES:: - - sage: M = Manifold(3, 'M') - sage: F = M.mixed_form(name='F', latex_name=r'\mathcal{F}'); F - Mixed differential form F on the 3-dimensional differentiable - manifold M - sage: F.display() - F = F_0 + F_1 + F_2 + F_3 - sage: F.set_name_comp(0, name='g') - sage: F.display() - F = g + F_1 + F_2 + F_3 - - """ - self[i].set_name(name=name, latex_name=latex_name) + self[i].set_name(name=comp_name, latex_name=comp_latex_name) def __bool__(self): r""" @@ -1199,17 +1178,6 @@ def copy(self, name=None, latex_name=None): sage: A is B False - Notice, that changes in the differential forms usually cause changes in - the original instance. But for the copy of a mixed form, the components - are copied as well:: - - sage: omega[e_xy,0] = y; omega.display() - omega = y dx - sage: A.display_expansion(e_xy) - A = x + y dx - sage: B.display_expansion(e_xy) - x + x dx - """ resu = self._new_instance() resu._comp = [form.copy() for form in self] @@ -1261,9 +1229,8 @@ def __setitem__(self, index, values): for deg, j in zip(range(start, stop, step), range(len(form_list))): dmodule = self._domain.diff_form_module(deg, self._dest_map) form = dmodule(form_list[j]) - self[deg].copy_from(form) - self[deg].set_name(name=form._name, - latex_name=form._latex_name) + self[deg].copy_from(form) # keep the names + self[deg].set_name(name=form._name, latex_name=form._latex_name) self._is_zero = False # a priori def __getitem__(self, deg): @@ -1530,7 +1497,7 @@ def set_comp(self, i): sage: A.display() A = A_0 + A_1 + A_2 sage: A.display_expansion() - A = [x^2] + [-y dx + x dy] + [(x - y) dx/\dy] + A = x^2 - y dx + x dy + (x - y) dx∧dy """ return self[i] From 91fc96fed615f6070a729c143703907dc227819e Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 6 Jul 2021 13:54:00 +0200 Subject: [PATCH 191/336] Trac #30272: minor doctest fix --- src/sage/manifolds/differentiable/mixed_form.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index 48807f2b30f..a0ff3786134 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -112,8 +112,8 @@ class MixedForm(AlgebraElement): sage: B = M.mixed_form(name='B') sage: B[:] = [f, omega, eta]; B.display() # display names B = f + omega + eta - sage: A.display_expansion() # display in coordinates - A = x + x*y dx + x*y^2 dx∧dy + sage: B.display_expansion() # display in coordinates + B = x + x*y dx + x*y^2 dx∧dy sage: B[0] Scalar field f on the 2-dimensional differentiable manifold M sage: B[1] From 2b5535ff600251430fee311881d319fea5e65082 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Jul 2021 06:39:46 -0700 Subject: [PATCH 192/336] sage.calculus.all.symbolic_expression: Add more tests --- src/sage/calculus/all.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sage/calculus/all.py b/src/sage/calculus/all.py index 7d183422f16..5e40c003aa1 100644 --- a/src/sage/calculus/all.py +++ b/src/sage/calculus/all.py @@ -117,6 +117,32 @@ def symbolic_expression(x): (1, 5, 25) sage: f(5).parent() Vector space of dimension 3 over Symbolic Ring + + TESTS: + + Also functions defined using ``def`` can be used, but we do not advertise it as a use case:: + + sage: def sos(x, y): + ....: return x^2 + y^2 + sage: symbolic_expression(sos) + (x, y) |--> x^2 + y^2 + + Functions that take a varying number of arguments or keyword-only arguments are not accepted:: + + sage: def variadic(x, *y): + ....: return x + sage: symbolic_expression(variadic) + Traceback (most recent call last): + ... + TypeError: unable to convert to a symbolic expression + + sage: def function_with_keyword_only_arg(x, *, sign=1): + ....: return sign * x + sage: symbolic_expression(function_with_keyword_only_arg) + Traceback (most recent call last): + ... + TypeError: unable to convert + to a symbolic expression """ from sage.symbolic.expression import Expression from sage.symbolic.ring import SR From da06e055dbb1a07ac50d4b426af656241759f76a Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 8 Jun 2021 21:22:20 -0500 Subject: [PATCH 193/336] Initial commit --- src/sage/algebras/invariants.py | 69 +++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/sage/algebras/invariants.py diff --git a/src/sage/algebras/invariants.py b/src/sage/algebras/invariants.py new file mode 100644 index 00000000000..51f1f92dedc --- /dev/null +++ b/src/sage/algebras/invariants.py @@ -0,0 +1,69 @@ +r""" +Invariant algebras +""" + +# **************************************************************************** +# Copyright (C) 2021 Trevor K. Karn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + + +from sage.misc.cachefunc import cached_method +from sage.categories.finite_dimensional_modules_with_basis import FiniteDimensionalModuleWithBasis + + +class InvariantModule(Parent): + + def __init__(self): + pass + + +class FiniteDimensionalInvariantWithBasis(Parent, CombinatorialFreeModule): + r""" + When a group ``G`` acts on a module ``M``, the invariant algebra is the collection + of elements ``m`` in ``M`` such that ``g*m = m``. + + """ + + def __init__(self, M, G, action_on_basis = None, R = ZZ): + """ + EXAMPLES:: + + """ + + if not isinstance(G, ): + + if not isinstance(M, ): + + if not isinstance(action_on_basis, function): + + self._group = G + self._action_on_basis = action_on_basis if action_on_basis else lambda x: x + self._ambient_module = M + + cat = Modules(R).FiniteDimensional().WithBasis() + + if M.is_graded(): + cat = cat.Graded() + + + + def _repr_(self): + """ + EXAMPLES:: + + sage: V = VectorSpace(QQ,3) + sage: G = CyclicPermutationGroup(3) + sage: action = lambda g,x: x + Cyclic group of order 3 as a permutation group-invariant submodule of Vector space of dimension 3 over Rational Field + + """ + + return f"{self._group}-invariant submodule of {self._ambient_module}" + + \ No newline at end of file From 9b5b6a7844010138d02da1dd7e1230300190e3c6 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 9 Jun 2021 17:03:33 -0500 Subject: [PATCH 194/336] Add __init__ and tests --- src/sage/algebras/invariants.py | 81 +++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/src/sage/algebras/invariants.py b/src/sage/algebras/invariants.py index 51f1f92dedc..484b042305a 100644 --- a/src/sage/algebras/invariants.py +++ b/src/sage/algebras/invariants.py @@ -12,46 +12,56 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +class FiniteDimensionalInvariantWithBasis(CombinatorialFreeModule): + r""" + When a group `G` acts on a module `M`, the invariant algebra is the collection + of elements `m` in `M` such that `g*m = m`. -from sage.misc.cachefunc import cached_method -from sage.categories.finite_dimensional_modules_with_basis import FiniteDimensionalModuleWithBasis - - -class InvariantModule(Parent): - - def __init__(self): - pass - + ..MATH:: -class FiniteDimensionalInvariantWithBasis(Parent, CombinatorialFreeModule): - r""" - When a group ``G`` acts on a module ``M``, the invariant algebra is the collection - of elements ``m`` in ``M`` such that ``g*m = m``. + M^G = \{m \in M : g\cdot m = m} + The current implementation works when `G` is a finitely-generated group, and + when `M` is a finite-dimensional free module. """ - def __init__(self, M, G, action_on_basis = None, R = ZZ): + def __init__(self, M, G, action_on_basis = None, **kwds): """ EXAMPLES:: - - """ - - if not isinstance(G, ): - if not isinstance(M, ): + sage: V = VectorSpace(QQ,3) + sage: G = CyclicPermutationGroup(3) + sage: action = lambda x,g: x + sage: VG = FiniteDimensionalInvariantWithBasis(V,G,action) + Cyclic group of order 3 as a permutation group-invariant submodule of Vector space of dimension 3 over Rational Field + sage: TestSuite(VG).run() + + sage: R. = PolynomialRing(QQ) + sage: G = CyclicPermutationGroup(3) + sage: RG = FiniteDimensionalInvariantWithBasis(V,G,action) + sage: TestSuite(RG).run() - if not isinstance(action_on_basis, function): + """ + # TODO: check assumtions on M + self._ambient_module = M + self._ambient_module_basis = M.basis() + self._base_ring = M.base_ring() + # TODO: add check for G to be finitely generated self._group = G - self._action_on_basis = action_on_basis if action_on_basis else lambda x: x - self._ambient_module = M - cat = Modules(R).FiniteDimensional().WithBasis() + # TODO: Determine if there are any checks to do here. + self._action_on_basis = action_on_basis if action_on_basis else lambda x,g: x*g + + self._basis = M.annihilator_basis(G.gens(), lambda x,g: self._action_on_basis(x,g) - x) - if M.is_graded(): - cat = cat.Graded() + cat = kwds.pop("category", None) + if cat is None: + cat = Modules(self._base_ring).FiniteDimensional().WithBasis() + CombinatorialFreeModule.__init__(self, self._base_ring, self._basis, + category = cat) def _repr_(self): """ @@ -59,11 +69,24 @@ def _repr_(self): sage: V = VectorSpace(QQ,3) sage: G = CyclicPermutationGroup(3) - sage: action = lambda g,x: x - Cyclic group of order 3 as a permutation group-invariant submodule of Vector space of dimension 3 over Rational Field + sage: action = lambda g, x: x + sage: FiniteDimensionalInvariantWithBasis(V, G, action) + (Cyclic group of order 3 as a permutation group)-invariant submodule of Vector space of dimension 3 over Rational Field + + """ + + return f"({self._group})-invariant submodule of {self._ambient_module}" + def base_ring(self): + """ + EXAMPLES:: + + sage: V = VectorSpace(QQ,3) + sage: G = CyclicPermutationGroup(3) + sage: VG = FiniteDimensionalInvariantWithBasis(V,G,lambda x,g: x) + sage: VG.base_ring() + Rational Field """ - return f"{self._group}-invariant submodule of {self._ambient_module}" + return self._base_ring - \ No newline at end of file From 410b4c449045686ad52c32faa33b75eb850127b2 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 10 Jun 2021 21:02:44 -0500 Subject: [PATCH 195/336] Add .invariant_algebra() method to ParentMethods of FiniteDimensionalModuleWithBasis --- .../finite_dimensional_modules_with_basis.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 4696c2d424c..16cfdde2879 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -352,6 +352,45 @@ def echelon_form(self, elements, row_reduced=False, order=None): ret = [self.from_vector(vec, order=order) for vec in mat if vec] return ret + @cached_method + def invariant_algebra(self, A, G, action_on_basis = lambda x,g: x, **kwargs): + r""" + Given a group acting on an algebra, return the algebra + `A^G = \{a \in A : ga = a \forall g \in G\}` + + INPUT:: + + - ``A`` -- a finite-dimensional algebra with a basis + + - ``G`` -- a finitely-generated group + + - ``action_on_basis(x,g)`` -- a function of taking positional arguments + ``x`` and ``g`` which gives the action of ``g`` defined on a basis + element ``x`` of ``A``. Default is the trivial action. + + OUTPUT:: + + - ``invariant`` -- an instance of ``FiniteDimensionalInvariantAlgebra`` + representing `A^G` + + """ + + self._base_ring = A.base_ring() + + self._ambient_algebra = A + + self._ambient_algebra_basis = A.basis() + + self._group = G + + self._action_on_basis = action_on_basis + + self._basis = M.annihilator_basis(self._group.gens(), lambda x,g: self._action_on_basis(x,g) - x) + + return FiniteDimensionalInvariantAlgebra(self._ambient_algebra, + self._G, + self._action_on_basis) + class ElementMethods: def dense_coefficient_list(self, order=None): """ From a238f8148e7fb4b8fee1548d9916aaa55d2bb6d2 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 10 Jun 2021 22:59:58 -0500 Subject: [PATCH 196/336] Rewrite FiniteDimensionalInvariantAlgebra to subclass UniqueRepresentation and SubmoduleWithBasis --- src/sage/algebras/invariants.py | 104 ++++++++++-------- .../finite_dimensional_modules_with_basis.py | 16 +-- 2 files changed, 58 insertions(+), 62 deletions(-) diff --git a/src/sage/algebras/invariants.py b/src/sage/algebras/invariants.py index 484b042305a..78fc2d50d8b 100644 --- a/src/sage/algebras/invariants.py +++ b/src/sage/algebras/invariants.py @@ -12,81 +12,89 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -class FiniteDimensionalInvariantWithBasis(CombinatorialFreeModule): +from sage.misc.cachefunc import cached_method +from sage.modules.with_basis.subquotient import SubmoduleWithBasis +from sage.structure.unique_representation import UniqueRepresentation + +class FiniteDimensionalInvariantAlgebra(UniqueRepresentation, SubmoduleWithBasis): r""" - When a group `G` acts on a module `M`, the invariant algebra is the collection - of elements `m` in `M` such that `g*m = m`. + Construct the `G`-invariant subalgebra of `A`. When a group `G` acts on an algebra + `A`, the invariant algebra is the collection of elements `a` in `A` such that + `g*a = a`. ..MATH:: - M^G = \{m \in M : g\cdot m = m} + A^G = \{a \in A : g\cdot a = a} + + NOTE: The current implementation works when `G` is a finitely-generated group, and + when `A` is a finite-dimensional free module. - The current implementation works when `G` is a finitely-generated group, and - when `M` is a finite-dimensional free module. + TODO:: + Extend when `A` does not have a basis and `G` is a permutation group using: + - https://arxiv.org/abs/0812.3082 + - https://www.dmtcs.org/pdfpapers/dmAA0123.pdf + +>>>>>>> 48a499577f (Rewrite FiniteDimensionalInvariantAlgebra to subclass UniqueRepresentation and SubmoduleWithBasis) """ - def __init__(self, M, G, action_on_basis = None, **kwds): + def __init__(self, A, G, action_on_basis = lambda x, g: x, **kwargs): """ - EXAMPLES:: - sage: V = VectorSpace(QQ,3) - sage: G = CyclicPermutationGroup(3) - sage: action = lambda x,g: x - sage: VG = FiniteDimensionalInvariantWithBasis(V,G,action) - Cyclic group of order 3 as a permutation group-invariant submodule of Vector space of dimension 3 over Rational Field - sage: TestSuite(VG).run() - - sage: R. = PolynomialRing(QQ) - sage: G = CyclicPermutationGroup(3) - sage: RG = FiniteDimensionalInvariantWithBasis(V,G,action) - sage: TestSuite(RG).run() - + TESTS:: + + sage: from sage.algebras.invariants import FiniteDimensionalInvariantAlgebra + sage: A. = PolynomialRing(QQ) + sage: G = SymmetricGroup(3); G.rename('S3') + sage: import operator + sage: AG = FiniteDimensionalInvariantAlgebra(A, G, operator.mul); AG + (S3)-invariant subalgebra of Polynomial Ring over Rational Field + """ - # TODO: check assumtions on M - self._ambient_module = M - self._ambient_module_basis = M.basis() - self._base_ring = M.base_ring() - # TODO: add check for G to be finitely generated self._group = G + self._action_on_basis = action_on_basis - # TODO: Determine if there are any checks to do here. - self._action_on_basis = action_on_basis if action_on_basis else lambda x,g: x*g - - self._basis = M.annihilator_basis(G.gens(), lambda x,g: self._action_on_basis(x,g) - x) + side = kwargs.pop('side','right') + support_order = kwargs.pop('support_order', None) + unitriangular = kwargs.pop('unitriangular', False) + category = kwargs.pop('category', None) - cat = kwds.pop("category", None) + basis = A.annihilator_basis(G.gens(), action = lambda x,g: action_on_basis(x,g) - x, side=side) - if cat is None: - cat = Modules(self._base_ring).FiniteDimensional().WithBasis() + super(SubmoduleWithBasis,self).__init__(basis.keys(), + support_order = support_order, + ambient = A, + unitriangular = unitriangular, + category = category, + **kwargs) - CombinatorialFreeModule.__init__(self, self._base_ring, self._basis, - category = cat) - def _repr_(self): """ EXAMPLES:: - + sage: V = VectorSpace(QQ,3) sage: G = CyclicPermutationGroup(3) - sage: action = lambda g, x: x - sage: FiniteDimensionalInvariantWithBasis(V, G, action) - (Cyclic group of order 3 as a permutation group)-invariant submodule of Vector space of dimension 3 over Rational Field + sage: FiniteDimensionalInvariantAlgebra(V, G) + (Cyclic group of order 3 as a permutation group)-invariant subalgebra of Vector space of dimension 3 over Rational Field """ - return f"({self._group})-invariant submodule of {self._ambient_module}" + return f"({self._group})-invariant subalgebra of {self._ambient_module}" + - def base_ring(self): + def group(self): """ + Return the group `G` whose action ``self`` is invariant under. + EXAMPLES:: - sage: V = VectorSpace(QQ,3) - sage: G = CyclicPermutationGroup(3) - sage: VG = FiniteDimensionalInvariantWithBasis(V,G,lambda x,g: x) - sage: VG.base_ring() - Rational Field - """ + sage: G = SymmetricGroup(3) + sage: A. = PolynomialRing(QQ) + sage: import operator + sage: AG = FiniteDimensionalInvariantAlgebra(G,A,operator.mul) + sage: AG.group() + Symmetric group of order 3! as a permutation group - return self._base_ring + """ + return self._group \ No newline at end of file diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 16cfdde2879..2f9f335d725 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -375,21 +375,9 @@ def invariant_algebra(self, A, G, action_on_basis = lambda x,g: x, **kwargs): """ - self._base_ring = A.base_ring() - - self._ambient_algebra = A + from sage.algebras.invariants import FiniteDimensionalInvariantAlgebra - self._ambient_algebra_basis = A.basis() - - self._group = G - - self._action_on_basis = action_on_basis - - self._basis = M.annihilator_basis(self._group.gens(), lambda x,g: self._action_on_basis(x,g) - x) - - return FiniteDimensionalInvariantAlgebra(self._ambient_algebra, - self._G, - self._action_on_basis) + return FiniteDimensionalInvariantAlgebra(A, G, action_on_basis) class ElementMethods: def dense_coefficient_list(self, order=None): From cddf05da02b656639db72ba2fe11c25eb264e8b8 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 11 Jun 2021 20:38:56 -0500 Subject: [PATCH 197/336] Fix bad merge --- src/sage/algebras/invariants.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/algebras/invariants.py b/src/sage/algebras/invariants.py index 78fc2d50d8b..d17302ba4d8 100644 --- a/src/sage/algebras/invariants.py +++ b/src/sage/algebras/invariants.py @@ -34,7 +34,6 @@ class FiniteDimensionalInvariantAlgebra(UniqueRepresentation, SubmoduleWithBasis - https://arxiv.org/abs/0812.3082 - https://www.dmtcs.org/pdfpapers/dmAA0123.pdf ->>>>>>> 48a499577f (Rewrite FiniteDimensionalInvariantAlgebra to subclass UniqueRepresentation and SubmoduleWithBasis) """ def __init__(self, A, G, action_on_basis = lambda x, g: x, **kwargs): From 51d337c2ec3e22cab5a5304da3b76a28ff16a335 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 11 Jun 2021 20:41:41 -0500 Subject: [PATCH 198/336] Remove @cached_method decorator from .invariant_algebra() --- src/sage/categories/finite_dimensional_modules_with_basis.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 2f9f335d725..69d331c360b 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -352,7 +352,6 @@ def echelon_form(self, elements, row_reduced=False, order=None): ret = [self.from_vector(vec, order=order) for vec in mat if vec] return ret - @cached_method def invariant_algebra(self, A, G, action_on_basis = lambda x,g: x, **kwargs): r""" Given a group acting on an algebra, return the algebra From 71e473c7fd46dbacbd029ba6ae9760572d17a947 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 11 Jun 2021 21:25:51 -0500 Subject: [PATCH 199/336] Move from algebras.invariants to modules.with_basis.invariant --- src/sage/algebras/invariants.py | 99 --------- .../finite_dimensional_modules_with_basis.py | 26 ++- src/sage/modules/with_basis/invariant.py | 196 ++++++++++++++++++ 3 files changed, 214 insertions(+), 107 deletions(-) delete mode 100644 src/sage/algebras/invariants.py create mode 100644 src/sage/modules/with_basis/invariant.py diff --git a/src/sage/algebras/invariants.py b/src/sage/algebras/invariants.py deleted file mode 100644 index d17302ba4d8..00000000000 --- a/src/sage/algebras/invariants.py +++ /dev/null @@ -1,99 +0,0 @@ -r""" -Invariant algebras -""" - -# **************************************************************************** -# Copyright (C) 2021 Trevor K. Karn -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** - -from sage.misc.cachefunc import cached_method -from sage.modules.with_basis.subquotient import SubmoduleWithBasis -from sage.structure.unique_representation import UniqueRepresentation - -class FiniteDimensionalInvariantAlgebra(UniqueRepresentation, SubmoduleWithBasis): - r""" - Construct the `G`-invariant subalgebra of `A`. When a group `G` acts on an algebra - `A`, the invariant algebra is the collection of elements `a` in `A` such that - `g*a = a`. - - ..MATH:: - - A^G = \{a \in A : g\cdot a = a} - - NOTE: The current implementation works when `G` is a finitely-generated group, and - when `A` is a finite-dimensional free module. - - TODO:: - Extend when `A` does not have a basis and `G` is a permutation group using: - - https://arxiv.org/abs/0812.3082 - - https://www.dmtcs.org/pdfpapers/dmAA0123.pdf - - """ - - def __init__(self, A, G, action_on_basis = lambda x, g: x, **kwargs): - """ - - TESTS:: - - sage: from sage.algebras.invariants import FiniteDimensionalInvariantAlgebra - sage: A. = PolynomialRing(QQ) - sage: G = SymmetricGroup(3); G.rename('S3') - sage: import operator - sage: AG = FiniteDimensionalInvariantAlgebra(A, G, operator.mul); AG - (S3)-invariant subalgebra of Polynomial Ring over Rational Field - - """ - - self._group = G - self._action_on_basis = action_on_basis - - side = kwargs.pop('side','right') - support_order = kwargs.pop('support_order', None) - unitriangular = kwargs.pop('unitriangular', False) - category = kwargs.pop('category', None) - - basis = A.annihilator_basis(G.gens(), action = lambda x,g: action_on_basis(x,g) - x, side=side) - - super(SubmoduleWithBasis,self).__init__(basis.keys(), - support_order = support_order, - ambient = A, - unitriangular = unitriangular, - category = category, - **kwargs) - - def _repr_(self): - """ - EXAMPLES:: - - sage: V = VectorSpace(QQ,3) - sage: G = CyclicPermutationGroup(3) - sage: FiniteDimensionalInvariantAlgebra(V, G) - (Cyclic group of order 3 as a permutation group)-invariant subalgebra of Vector space of dimension 3 over Rational Field - - """ - - return f"({self._group})-invariant subalgebra of {self._ambient_module}" - - - def group(self): - """ - Return the group `G` whose action ``self`` is invariant under. - - EXAMPLES:: - - sage: G = SymmetricGroup(3) - sage: A. = PolynomialRing(QQ) - sage: import operator - sage: AG = FiniteDimensionalInvariantAlgebra(G,A,operator.mul) - sage: AG.group() - Symmetric group of order 3! as a permutation group - - """ - - return self._group \ No newline at end of file diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 69d331c360b..75021802f13 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -352,31 +352,41 @@ def echelon_form(self, elements, row_reduced=False, order=None): ret = [self.from_vector(vec, order=order) for vec in mat if vec] return ret - def invariant_algebra(self, A, G, action_on_basis = lambda x,g: x, **kwargs): + def invariant_module(self, A, G, action_on_basis = lambda x,g: x, + side = "left", **kwargs): r""" - Given a group acting on an algebra, return the algebra + Given a group acting on a module, return the module `A^G = \{a \in A : ga = a \forall g \in G\}` INPUT:: - - ``A`` -- a finite-dimensional algebra with a basis + - ``M`` -- a finite-dimensional module with a basis - ``G`` -- a finitely-generated group - ``action_on_basis(x,g)`` -- a function of taking positional arguments ``x`` and ``g`` which gives the action of ``g`` defined on a basis - element ``x`` of ``A``. Default is the trivial action. + element ``x`` of ``M``. Default is the trivial action. OUTPUT:: - - ``invariant`` -- an instance of ``FiniteDimensionalInvariantAlgebra`` - representing `A^G` + - ``invariant`` -- an instance of ``FiniteDimensionalInvariantModule`` + representing `M^G` + + EXAMPLES:: + sage: action = lambda g, m: m.parent()(g._act_on_list_on_position(list(m))) + sage: G = SymmetricGroup(3) + sage: M = FreeModule(ZZ,[0,1,2]) + sage: R = Representation(G, M, action) """ - from sage.algebras.invariants import FiniteDimensionalInvariantAlgebra + from sage.modules.invariants import FiniteDimensionalInvariantModule + from sage.modules.with_basis.representation import Representation + + R = Representation(G, M, action_on_basis, side) - return FiniteDimensionalInvariantAlgebra(A, G, action_on_basis) + return FiniteDimensionalInvariantModule(M, G, R) class ElementMethods: def dense_coefficient_list(self, order=None): diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py new file mode 100644 index 00000000000..7c2e95f6a5f --- /dev/null +++ b/src/sage/modules/with_basis/invariant.py @@ -0,0 +1,196 @@ +r""" +Invariant algebras +""" + +# **************************************************************************** +# Copyright (C) 2021 Trevor K. Karn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.modules.with_basis.subquotient import SubmoduleWithBasis +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.finitely_generated_semigroups import FinitelyGeneratedSemigroups +from sage.categories.groups import Groups + +class FiniteDimensionalInvariantModule(UniqueRepresentation, SubmoduleWithBasis): + r""" + Construct the `G`-invariant submodule of `M`. When a group `G` acts on a module + `M`, the invariant module is the collection of elements `x` in `M` such that + `g*x = x`. + + ..MATH:: + + M^G = \{a \in A : g\cdot a = a} + + NOTE: The current implementation works when `G` is a finitely-generated semigroup, + and when `A` is a finite-dimensional free module with a distinguished basis. + + TODO:: + Extend when `A` does not have a basis and `G` is a permutation group using: + - https://arxiv.org/abs/0812.3082 + - https://www.dmtcs.org/pdfpapers/dmAA0123.pdf + + """ + + def __init__(self, R, *args, **kwargs): + """ + INPUTS:: + + - ``R`` -- an instance of a ``Representation`` of a semigroup `S` + acting on the module `M`. + + OUTPUTS:: + + - ``MS`` -- the invariant algebra of the semigroup action of `S` on `M`, or + equivalently, the isotypic component of the representation of + `S` carried by `M` corresponding to the trivial character. + + EXAMPLES:: + + TESTS:: + + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: M. = PolynomialRing(QQ) + sage: G = SymmetricGroup(3); G.rename('S3') + sage: import operator + sage: MG = FiniteDimensionalInvariantModule(M, G, operator.mul); AG + (S3)-invariant subalgebra of Polynomial Ring over Rational Field + + sage: S = FiniteSemigroups().example(); S + An example of a finite semigroup: the left regular band generated by ('a', 'b', 'c', 'd') + sage: FiniteDimensionalInvariantModule() + + """ + + self._semigroup = R.semigroup() + + if self._semigroup not in FinitelyGeneratedSemigroups(): + raise ValueError(f'{self._semigroup} is not finitely generated') + + if self._semigroup in Groups(): + self._is_invariant_for_group = True + else: + self._is_invariant_for_group = False + + self._ambient_module = R._module + self._on_ambient_basis = R._on_basis + + side = kwargs.pop('side','left') + support_order = kwargs.pop('support_order', None) + unitriangular = kwargs.pop('unitriangular', False) + category = kwargs.pop('category', None) + + if side == 'left': + self._on_invariant_basis = lambda g,x: self._on_ambient_basis(g,x) - x + else: + self._on_invariant_basis = lambda x,g: self._on_ambient_basis(g,x) - x + + basis = self._ambient_module().annihilator_basis( + self._semigroup.gens(), + action = self._on_invariant_basis).keys() + + super(SubmoduleWithBasis,self).__init__(basis, + support_order = support_order, + ambient = self._ambient_module, + unitriangular = unitriangular, + category = category, + *args, + **kwargs) + + def _repr_(self): + """ + EXAMPLES:: + + sage: V = VectorSpace(QQ,3) + sage: G = CyclicPermutationGroup(3) + sage: FiniteDimensionalInvariantAlgebra(V, G) + (Cyclic group of order 3 as a permutation group)-invariant subalgebra of Vector space of dimension 3 over Rational Field + + """ + + return f"({self._semigroup})-invariant submodule of {self._ambient_module}" + + + def semigroup(self): + """ + Return the semigroup `S` whose action ``self`` is invariant under. + + EXAMPLES:: + + sage: G = SymmetricGroup(3) + sage: A. = PolynomialRing(QQ) + sage: import operator + sage: AG = FiniteDimensionalInvariantAlgebra(G,A,operator.mul) + sage: AG.group() + Symmetric group of order 3! as a permutation group + + """ + + return self._semigroup + + def group(self): + """ + When `S` is a group (not just a semigroup), return the group. + + """ + + if not self._is_invariant_for_group: + raise ValueError(f'{self._semigroup} is not a group.') + + return self._semigroup + + + def action_on_basis(self,*args): + + if len(args) == 0: + return self._action_on_basis + + return self._action_on_basis(args) + + class Element(SubmoduleWithBasis.Element): + + # lmul -- self*right + def _lmul_(self, right): + """ + EXAMPLES:: + + sage: #### create invariant module + sage: #### multiply group element + """ + + if right in self.parent().group() and self.side() == 'right': + return self + + return None + + # rmul -- left * self + def _rmul_(self, left): + """ + EXAMPLES:: + + sage: ### create invariant module + sage: ### multiply group element + """ + if left in self.parent().group() and self.side() == 'left': + return self + + return None + + def _acted_upon_(self, scalar, self_on_left = False): + """ + EXAMPLES:: + + sage: + """ + + if scalar in self.parent().group() and self_on_left == (self.side() == 'right'): + + return self + + return None From 7ff0c1f5b645a63f6ce9ec8e3757d1f50b7b0659 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 15 Jun 2021 08:51:53 -0500 Subject: [PATCH 200/336] Add FiniteDiimensionalInvariantModule with no tests/documentation --- src/sage/modules/with_basis/invariant.py | 109 ++++++++++------------- 1 file changed, 46 insertions(+), 63 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 7c2e95f6a5f..1e4601f535b 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -1,5 +1,5 @@ r""" -Invariant algebras +Invariant modules """ # **************************************************************************** @@ -18,21 +18,21 @@ from sage.categories.finitely_generated_semigroups import FinitelyGeneratedSemigroups from sage.categories.groups import Groups -class FiniteDimensionalInvariantModule(UniqueRepresentation, SubmoduleWithBasis): +class FiniteDimensionalInvariantModule(SubmoduleWithBasis): r""" - Construct the `G`-invariant submodule of `M`. When a group `G` acts on a module - `M`, the invariant module is the collection of elements `x` in `M` such that - `g*x = x`. + Construct the `S`-invariant submodule of `M`. When a semigroup `S` acts on a module + `M`, the invariant module is the collection of elements `m` in `M` such that + `s \cdot m = m` for all `s \in S. ..MATH:: - M^G = \{a \in A : g\cdot a = a} + M^S = \{m \in M : s\cdot m = m,\, \forall s \in S \} - NOTE: The current implementation works when `G` is a finitely-generated semigroup, - and when `A` is a finite-dimensional free module with a distinguished basis. + NOTE: The current implementation works when `S` is a finitely-generated semigroup, + and when `M` is a finite-dimensional free module with a distinguished basis. TODO:: - Extend when `A` does not have a basis and `G` is a permutation group using: + Extend when `M` does not have a basis and `S` is a permutation group using: - https://arxiv.org/abs/0812.3082 - https://www.dmtcs.org/pdfpapers/dmAA0123.pdf @@ -42,66 +42,57 @@ def __init__(self, R, *args, **kwargs): """ INPUTS:: - - ``R`` -- an instance of a ``Representation`` of a semigroup `S` + - ``R`` -- an instance of a ``Representation`` of a semigroup `S` acting on the module `M`. OUTPUTS:: - ``MS`` -- the invariant algebra of the semigroup action of `S` on `M`, or - equivalently, the isotypic component of the representation of + equivalently, the isotypic component of the representation of `S` carried by `M` corresponding to the trivial character. EXAMPLES:: + + TESTS:: - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: M. = PolynomialRing(QQ) - sage: G = SymmetricGroup(3); G.rename('S3') - sage: import operator - sage: MG = FiniteDimensionalInvariantModule(M, G, operator.mul); AG - (S3)-invariant subalgebra of Polynomial Ring over Rational Field - - sage: S = FiniteSemigroups().example(); S - An example of a finite semigroup: the left regular band generated by ('a', 'b', 'c', 'd') - sage: FiniteDimensionalInvariantModule() - """ - + self._semigroup = R.semigroup() - - if self._semigroup not in FinitelyGeneratedSemigroups(): + + if self._semigroup not in FinitelyGeneratedSemigroups: raise ValueError(f'{self._semigroup} is not finitely generated') - if self._semigroup in Groups(): - self._is_invariant_for_group = True - else: - self._is_invariant_for_group = False - self._ambient_module = R._module + + if self._ambient_module not in FiniteDimensionalModulesWithBasis: + raise ValueError(f'{self._ambient_module} is not finite dimensional') + self._on_ambient_basis = R._on_basis - side = kwargs.pop('side','left') - support_order = kwargs.pop('support_order', None) + self._side = kwargs.pop('side', R.side()) + unitriangular = kwargs.pop('unitriangular', False) - category = kwargs.pop('category', None) + category = kwargs.pop('category', R.category().Subobjects()) - if side == 'left': - self._on_invariant_basis = lambda g,x: self._on_ambient_basis(g,x) - x + if self._side == 'left': + self._invariant_map = lambda g,x: self._on_ambient_basis(g,x.support()[0]) - x else: - self._on_invariant_basis = lambda x,g: self._on_ambient_basis(g,x) - x + self._invariant_map = lambda x,g: self._on_ambient_basis(x.support()[0],g) - x - basis = self._ambient_module().annihilator_basis( + + basis = self._ambient_module.annihilator_basis( self._semigroup.gens(), - action = self._on_invariant_basis).keys() + action = self._invariant_map, + side = self._side) + - super(SubmoduleWithBasis,self).__init__(basis, - support_order = support_order, - ambient = self._ambient_module, - unitriangular = unitriangular, - category = category, - *args, - **kwargs) + super().__init__(Family(basis), support_order = self._ambient_module._compute_support_order(basis), + ambient = self._ambient_module, # bug - should lift to the representation + unitriangular = unitriangular,#is this right? + category = category, + *args, **kwargs) def _repr_(self): """ @@ -134,18 +125,6 @@ def semigroup(self): return self._semigroup - def group(self): - """ - When `S` is a group (not just a semigroup), return the group. - - """ - - if not self._is_invariant_for_group: - raise ValueError(f'{self._semigroup} is not a group.') - - return self._semigroup - - def action_on_basis(self,*args): if len(args) == 0: @@ -154,7 +133,11 @@ def action_on_basis(self,*args): return self._action_on_basis(args) class Element(SubmoduleWithBasis.Element): - + + def _mul_(self, other): + P = self.parent() + return P.retract(P.lift(self) * P.lift(other)) + # lmul -- self*right def _lmul_(self, right): """ @@ -164,10 +147,10 @@ def _lmul_(self, right): sage: #### multiply group element """ - if right in self.parent().group() and self.side() == 'right': + if right in self.parent()._semigroup and self.parent()._side == 'right': return self - return None + return super()._lmul_(right) # rmul -- left * self def _rmul_(self, left): @@ -177,10 +160,10 @@ def _rmul_(self, left): sage: ### create invariant module sage: ### multiply group element """ - if left in self.parent().group() and self.side() == 'left': + if left in self.parent()._semigroup and self.parent()._side == 'left': return self - return None + return super()._rmul_(left) def _acted_upon_(self, scalar, self_on_left = False): """ @@ -189,7 +172,7 @@ def _acted_upon_(self, scalar, self_on_left = False): sage: """ - if scalar in self.parent().group() and self_on_left == (self.side() == 'right'): + if scalar in self.parent()._semigroup and self_on_left == (self.parent()._side == 'right'): return self From 1c4e871beace0f0150774dfbeca84ff17e3419bd Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 15 Jun 2021 17:55:04 -0500 Subject: [PATCH 201/336] Initial commit of invariant algebra --- .../finite_dimensional_invariant_algebras.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py new file mode 100644 index 00000000000..386ad1ba838 --- /dev/null +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py @@ -0,0 +1,40 @@ +r""" +Invariant modules +""" + +# **************************************************************************** +# Copyright (C) 2021 Trevor K. Karn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + +class FiniteDimensionalInvariantAlgebra(FiniteDimensionalInvariantModule): + + def __init__(self, R, *args, **kwargs): + """ + INPUTS:: + + - ``R`` -- an instance of a ``Representation`` of a semigroup `S` + acting on the algebra `A`. + + OUTPUTS:: + + - ``AS`` -- the invariant algebra of the semigroup action of `S` on `A`, or + equivalently, the isotypic component of the representation of + `S` carried by `A` corresponding to the trivial character. + + EXAMPLES:: + + + + TESTS:: + + """ + + pass \ No newline at end of file From f4b541c7315502d7dff90a550c5dbb28bee12ff8 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 16 Jun 2021 17:24:01 -0500 Subject: [PATCH 202/336] Add tests to invariant.py and change default choice of category in Representation --- src/sage/modules/with_basis/invariant.py | 66 ++++++++++++++----- src/sage/modules/with_basis/representation.py | 11 ++-- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 1e4601f535b..fe3b927beec 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -16,7 +16,9 @@ from sage.modules.with_basis.subquotient import SubmoduleWithBasis from sage.structure.unique_representation import UniqueRepresentation from sage.categories.finitely_generated_semigroups import FinitelyGeneratedSemigroups +from sage.categories.finite_dimensional_modules_with_basis import FiniteDimensionalModulesWithBasis from sage.categories.groups import Groups +from sage.sets.family import Family class FiniteDimensionalInvariantModule(SubmoduleWithBasis): r""" @@ -52,13 +54,37 @@ def __init__(self, R, *args, **kwargs): `S` carried by `M` corresponding to the trivial character. EXAMPLES:: - + sage: G = CyclicPermutationGroup(3) + sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g, m: M.term(g(m)) #cyclically permute coordinates + sage: R = Representation(G, M, action) + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: [I.lift(b) for b in I.basis()] + [M[1] + M[2] + M[3]] + sage: G = CyclicPermutationGroup(3) + sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda m, g: M.term(g(m)) #cyclically permute coordinates + sage: R = Representation(G, M, action, side='right') #same as last but on right + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: [I.lift(b) for b in I.basis()] + [M[1] + M[2] + M[3]] + + sage: G = SymmetricGroup(3) + sage: R = G.regular_representation(QQ) + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: [I.lift(b).to_vector() for b in I.basis()] + [(1, 1, 1, 1, 1, 1)] TESTS:: """ - + self._semigroup_representation = R self._semigroup = R.semigroup() if self._semigroup not in FinitelyGeneratedSemigroups: @@ -81,27 +107,33 @@ def __init__(self, R, *args, **kwargs): else: self._invariant_map = lambda x,g: self._on_ambient_basis(x.support()[0],g) - x - + # Give the intersection of kernels of the map `s*x-x` to determine when + # `s*x = x` for all generators `s` of `S` basis = self._ambient_module.annihilator_basis( self._semigroup.gens(), action = self._invariant_map, side = self._side) - super().__init__(Family(basis), support_order = self._ambient_module._compute_support_order(basis), - ambient = self._ambient_module, # bug - should lift to the representation - unitriangular = unitriangular,#is this right? - category = category, - *args, **kwargs) + super().__init__(Family(basis), + support_order = self._ambient_module._compute_support_order(basis), + ambient = self._semigroup_representation, + unitriangular = unitriangular,#is this right? + category = category, + *args, **kwargs) def _repr_(self): """ EXAMPLES:: - sage: V = VectorSpace(QQ,3) + sage: M = CombinatorialFreeModule(QQ,[1,2,3]) sage: G = CyclicPermutationGroup(3) - sage: FiniteDimensionalInvariantAlgebra(V, G) - (Cyclic group of order 3 as a permutation group)-invariant subalgebra of Vector space of dimension 3 over Rational Field + sage: from sage.modules.with_basis.representation import Representation + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: R = Representation(G,M,lambda g,x: M.term(g(x))) + sage: FiniteDimensionalInvariantModule(R) + (Cyclic group of order 3 as a permutation group)-invariant submodule of + Free module generated by {1, 2, 3} over Rational Field """ @@ -115,10 +147,13 @@ def semigroup(self): EXAMPLES:: sage: G = SymmetricGroup(3) - sage: A. = PolynomialRing(QQ) - sage: import operator - sage: AG = FiniteDimensionalInvariantAlgebra(G,A,operator.mul) - sage: AG.group() + sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') + sage: action = lambda g,x: M.term(g(x)) + sage: from sage.modules.with_basis.representation import Representation + sage: R = Representation(G, M, action) + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: I.semigroup() Symmetric group of order 3! as a permutation group """ @@ -169,7 +204,6 @@ def _acted_upon_(self, scalar, self_on_left = False): """ EXAMPLES:: - sage: """ if scalar in self.parent()._semigroup and self_on_left == (self.parent()._side == 'right'): diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 27fffed163d..839a1eca8f3 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -208,8 +208,10 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): except AttributeError: pass - category = kwargs.pop('category', Modules(module.base_ring()).WithBasis()) - + category = module.category() + if 'WithBasis' not in module.category().axioms(): + raise ValueError(f'{module} must have a basis') + if side not in ["left", "right"]: raise ValueError('side must be "left" or "right"') @@ -218,10 +220,7 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): self._module = module indices = module.basis().keys() - - if 'FiniteDimensional' in module.category().axioms(): - category = category.FiniteDimensional() - + Representation_abstract.__init__(self, semigroup, module.base_ring(), indices, category=category, **module.print_options()) From 6927dd2b981999d799e43f4dd6e841e47358f3b8 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 16 Jun 2021 17:25:32 -0500 Subject: [PATCH 203/336] Fix typos and whitespace issues --- .../finite_dimensional_invariant_algebras.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py index 386ad1ba838..4608a07a67c 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py @@ -1,5 +1,5 @@ r""" -Invariant modules +Invariant algebras """ # **************************************************************************** @@ -13,6 +13,7 @@ # **************************************************************************** from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule +from sage.categories.algebras import Algebras class FiniteDimensionalInvariantAlgebra(FiniteDimensionalInvariantModule): @@ -37,4 +38,12 @@ def __init__(self, R, *args, **kwargs): """ - pass \ No newline at end of file + if R._module not in Algebras: + + raise ValueErrror(f'{R._module} is not an algebra') + + if R._module not in Algebras().FiniteDimensional().WithBasis(): + + raise NotImplementedError(f'{R._module} must be finite-dimensional with a basis') + + super().__init__(R,*args,**kwargs) \ No newline at end of file From cdc04f3985784b64048a82e4048077787e4cf916 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 16 Jun 2021 17:45:48 -0500 Subject: [PATCH 204/336] Initial commit of FiniteDimensionalTwistedInvariantModule --- src/sage/modules/with_basis/invariant.py | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index fe3b927beec..63eced2162a 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -211,3 +211,31 @@ def _acted_upon_(self, scalar, self_on_left = False): return self return None + +class FiniteDimensionalTwistedInvariantModule(FiniteDimensionalInvariantModule): + r""" + Construct the `\chi`-twisted invariant submodule of `M`. When a semigroup `S` acts on a module + `M`, the `\chi`-twisted invariant submodule of `M` is the isotypic component of the representation + `M` corresponding to the irreducible character `\chi`. + + ..MATH:: + + M^S = \{m \in M : s\cdot m = m,\, \forall s \in S \} + + NOTE: The current implementation works when `S` is a finitely-generated semigroup, + and when `M` is a finite-dimensional free module with a distinguished basis. + """ + + def __init__(self, R, character = 'trivial'): + + super.__init__(R) + + if character != 'trivial': + + pass + + def projection(self, element): + """ + Give the projection of element onto self + """ + pass From 4adba098430cb641724ab7345514c8e1360435b8 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 18 Jun 2021 19:02:18 -0500 Subject: [PATCH 205/336] Fix whitespace issues --- src/sage/modules/with_basis/invariant.py | 12 ++++++------ src/sage/modules/with_basis/representation.py | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 63eced2162a..28bc2511d3e 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -63,7 +63,7 @@ def __init__(self, R, *args, **kwargs): sage: I = FiniteDimensionalInvariantModule(R) sage: [I.lift(b) for b in I.basis()] [M[1] + M[2] + M[3]] - + sage: G = CyclicPermutationGroup(3) sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') sage: from sage.modules.with_basis.representation import Representation @@ -86,7 +86,7 @@ def __init__(self, R, *args, **kwargs): """ self._semigroup_representation = R self._semigroup = R.semigroup() - + if self._semigroup not in FinitelyGeneratedSemigroups: raise ValueError(f'{self._semigroup} is not finitely generated') @@ -102,7 +102,7 @@ def __init__(self, R, *args, **kwargs): unitriangular = kwargs.pop('unitriangular', False) category = kwargs.pop('category', R.category().Subobjects()) - if self._side == 'left': + if self._side == 'left': self._invariant_map = lambda g,x: self._on_ambient_basis(g,x.support()[0]) - x else: self._invariant_map = lambda x,g: self._on_ambient_basis(x.support()[0],g) - x @@ -168,11 +168,11 @@ def action_on_basis(self,*args): return self._action_on_basis(args) class Element(SubmoduleWithBasis.Element): - + def _mul_(self, other): P = self.parent() return P.retract(P.lift(self) * P.lift(other)) - + # lmul -- self*right def _lmul_(self, right): """ @@ -197,7 +197,7 @@ def _rmul_(self, left): """ if left in self.parent()._semigroup and self.parent()._side == 'left': return self - + return super()._rmul_(left) def _acted_upon_(self, scalar, self_on_left = False): diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 839a1eca8f3..6570c861b37 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -5,6 +5,7 @@ - Travis Scrimshaw (2015-11-21): Initial version - Siddharth Singh (2020-03-21): Signed Representation +- Trevor Karn (2021-06-18): Add functionality to Representation """ #################################################################################### @@ -208,19 +209,20 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): except AttributeError: pass - category = module.category() - if 'WithBasis' not in module.category().axioms(): - raise ValueError(f'{module} must have a basis') + category = kwargs.pop('category', Modules(module.base_ring()).WithBasis()) if side not in ["left", "right"]: raise ValueError('side must be "left" or "right"') - + self._left_repr = (side == "left") self._on_basis = on_basis self._module = module - + indices = module.basis().keys() + if 'FiniteDimensional' in module.category().axioms(): + category = category.FiniteDimensional() + Representation_abstract.__init__(self, semigroup, module.base_ring(), indices, category=category, **module.print_options()) @@ -342,10 +344,10 @@ def product_by_coercion(self, left, right): ... TypeError: unsupported operand parent(s) for *: 'Representation of The Klein 4 group of order 4, as a permutation - group indexed by Subsets of {0, 1, 2, 3} over Rational Field' and - 'Representation of The Klein 4 group of order 4, as a permutation + group indexed by Subsets of {0, 1, 2, 3} over Rational Field' and + 'Representation of The Klein 4 group of order 4, as a permutation group indexed by Subsets of {0, 1, 2, 3} over Rational Field' - + sage: from sage.categories.algebras import Algebras sage: category = Algebras(QQ).FiniteDimensional().WithBasis() sage: T = Representation(G, E, on_basis, category=category) From 730baaefc39eb7a4c8c972280c2c2733329018cc Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 18 Jun 2021 19:05:45 -0500 Subject: [PATCH 206/336] Add tests and _repr_ to finite_dimensional_invariant_algebras --- .../finite_dimensional_invariant_algebras.py | 87 +++++++++++++++++-- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py index 4608a07a67c..beb5062f000 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py @@ -17,8 +17,8 @@ class FiniteDimensionalInvariantAlgebra(FiniteDimensionalInvariantModule): - def __init__(self, R, *args, **kwargs): - """ + def __init__(self, R, *args, **kwargs): + """ INPUTS:: - ``R`` -- an instance of a ``Representation`` of a semigroup `S` @@ -31,19 +31,88 @@ def __init__(self, R, *args, **kwargs): `S` carried by `A` corresponding to the trivial character. EXAMPLES:: - - + + sage: S = Set({1,2,3}) + sage: L = S.subsets_lattice() + sage: M = L.moebius_algebra(QQ) + sage: A = M.a_realization() + sage: G = SymmetricGroup(3) + sage: on_basis = lambda g,x: A.monomial(Set(g(s) for s in S)) + sage: from sage.modules.with_basis.representation import Representation + sage: R = Representation(G, A, on_basis) + sage: from sage.algebras.finite_dimensional_algebras.finite_dimensional_invariant_algebras import FiniteDimensionalInvariantAlgebra + sage: I = FiniteDimensionalInvariantAlgebra(R) + sage: I.basis() + (E[{}], E[{1}] + E[{2}] + E[{3}], E[{1,2}] + E[{1,3}] + E[{2,3}], E[{1,2,3}]) + sage: e = I.an_element(); e + + sage: G = SymmetricGroup(4) + sage: table = [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])] + sage: A = FiniteDimensionalAlgebra(GF(3), table, names = 'e') + sage: R = Representation(G, A, on_basis) + sage: I = FiniteDimensionalInvariantAlgebra(R) + + sage: G = CyclicPermutationGroup(3) + sage: M = algebras.Exterior(QQ, 'x', 3) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.prod([M.monomial(tuple([g(j+1)-1])) for j in m]) #cyclically permute generators + sage: from sage.categories.algebras import Algebras + sage: R = Representation(G, M, on_basis, category = Algebras(QQ).WithBasis().FiniteDimensional()) + sage: I = FiniteDimensionalInvariantAlgebra(R); I + (Cyclic group of order 3 as a permutation group)-invariant subalgebra of + The exterior algebra of rank 3 over Rational Field + sage: I.basis() + (1, x0 + x1 + x2, x0*x1*x2) TESTS:: - """ + sage: G = CyclicPermutationGroup(3) + sage: M = algebras.Exterior(QQ, 'x', 3) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.prod([M.monomial(tuple([g(j+1)-1])) for j in m]) #cyclically permute generators + sage: from sage.categories.algebras import Algebras + sage: R = Representation(G, M, on_basis, category = Algebras(QQ)) + sage: from sage.algebras.finite_dimensional_algebras.finite_dimensional_invariant_algebras \ + ....: import FiniteDimensionalInvariantAlgebra + sage: I = FiniteDimensionalInvariantAlgebra(R) + Traceback (most recent call last): + ... + ValueError: 'category' keyword argument must be FiniteDimensional - if R._module not in Algebras: + sage: R = Representation(G, M, on_basis, category = Algebras(QQ).FiniteDimensional()) + sage: I = FiniteDimensionalInvariantAlgebra(R) + Traceback (most recent call last): + ... + ValueError: 'category' keyword argument must be WithBasis - raise ValueErrror(f'{R._module} is not an algebra') + """ - if R._module not in Algebras().FiniteDimensional().WithBasis(): + # Check that the representation indeed comes from an algebra + if R._module not in Algebras(): + raise ValueError(f'{R._module} is not an algebra') + if R._module not in Algebras().FiniteDimensional().WithBasis(): raise NotImplementedError(f'{R._module} must be finite-dimensional with a basis') - super().__init__(R,*args,**kwargs) \ No newline at end of file + + if 'category' not in kwargs: + category = Algebras().FiniteDimensional().WithBasis() + kwargs['category'] = category + + else: + if 'FiniteDimensional' not in kwargs['category'].axioms(): + print('FD') + raise ValueError("'category' keyword argument must be FiniteDimensional") + if 'WithBasis' not in kwargs['category'].axioms(): + print('WB') + raise ValueError("'category' keyword argument must be WithBasis") + + super().__init__(R, *args, **kwargs) + + def _repr_(self): + """ + EXAMPLES:: + + """ + + return f"({self._semigroup})-invariant subalgebra of {self._ambient_module}" From f4d5ed6b217bbcd5211c116ab866dde840413c03 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 21 Jun 2021 13:51:11 -0500 Subject: [PATCH 207/336] WIP --- .../finite_dimensional_invariant_algebras.py | 4 - src/sage/algebras/orlik_solomon.py | 80 +++++++++- src/sage/modules/with_basis/invariant.py | 147 +++++++++++------- src/sage/modules/with_basis/representation.py | 4 +- 4 files changed, 176 insertions(+), 59 deletions(-) diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py index beb5062f000..2ec2dd8c7f0 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py @@ -94,17 +94,13 @@ def __init__(self, R, *args, **kwargs): if R._module not in Algebras().FiniteDimensional().WithBasis(): raise NotImplementedError(f'{R._module} must be finite-dimensional with a basis') - if 'category' not in kwargs: category = Algebras().FiniteDimensional().WithBasis() kwargs['category'] = category - else: if 'FiniteDimensional' not in kwargs['category'].axioms(): - print('FD') raise ValueError("'category' keyword argument must be FiniteDimensional") if 'WithBasis' not in kwargs['category'].axioms(): - print('WB') raise ValueError("'category' keyword argument must be WithBasis") super().__init__(R, *args, **kwargs) diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py index 893905af74d..6b6f290ffee 100644 --- a/src/sage/algebras/orlik_solomon.py +++ b/src/sage/algebras/orlik_solomon.py @@ -1,5 +1,10 @@ r""" Orlik-Solomon Algebras + +AUTHORS: + +- William Slofstra, Travis Scrimshaw (2015): Initial version +- Trevor Karn (2021-06-18): OrlikSolomonAlgebraInvariant """ # **************************************************************************** @@ -17,7 +22,8 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.categories.algebras import Algebras from sage.sets.family import Family - +from sage.algebras.finite_dimensional_algebras.finite_dimensional_invariant_algebras import ( + FiniteDimensionalInvariantAlgebra) class OrlikSolomonAlgebra(CombinatorialFreeModule): r""" @@ -445,3 +451,75 @@ def degree_on_basis(self, m): """ return len(m) +class OrlikSolomonAlgebraInvariant(FiniteDimensionalInvariantAlgebra): + r""" + When a group `G` acts on the groundset `E` of a matroid `M = (E,I)`, + it induces an action of `G` by ring automorphisms on the Orlik-Solomon + algebra of `M`. + + INPUT: + + - ``R`` - a ring + - ``M`` - a matroid + - ``G`` - a group (which acts on the groundset of ``M``) + - ``action_on_groundset`` - an action of ``G`` on the groundset of ``M`` + + OUTPUT: + + - ``OSG`` - the ``G``-invariant subalgebra of the Orlik-Solomon algebra + + The action on the groundset should be defined on the groundset without + any attempt to pass it to the Orlik-Solomon algebra. For example, if + the groundset is `\{1,2,...,n\}` there is a natural action of the + symmetric group `S_n` on the groundset. We can encode this in the + """ + + def __init__(self, R, M, G, action_on_groundset, ordering = None): + """ + EXAMPLES:: + + sage: M = matroids.Uniform(2,3) + sage: G = SymmetricGroup(3) + sage: action_on_groundset = lambda g,e: g(e+1)-1 #account for index shift + sage: from sage.algebras.orlik_solomon import OrlikSolomonAlgebraInvariant + sage: I = OrlikSolomonAlgebraInvariant(QQ, M, G, lambda g,x: g(x)); I + Symmetric group of order 3! as a permutation group-invariant + subalgebra of U(2, 3): Matroid of rank 2 on 3 elements with + circuit-closures + {2: {{0, 1, 2}}} + sage: I.basis() + [OS{}, OS{0} + OS{1} + OS{2}] + sage: x = I.an_element(); x + + + """ + from sage.modules.with_basis.representation import Representation + from sage.categories.algebras import Algebras + + self._M = M + self._G = G + self._ambient_orlik_solomon = M.orlik_solomon_algebra(R, ordering = ordering) + + # Convert from an action on the groundset to an action by ring automorphisms + # on the Orlik-Solomon algebra by extending the action to generators indexed + # by the groundset. + OS = self._ambient_orlik_solomon # for readability + on_basis = lambda g,e: OS.prod([OS.monomial((action_on_groundset,)) for j in e]) + + rep = Representation(G, OS, on_basis, + category = Algebras().FiniteDimensional().WithBasis()) + + self._representation = rep + + super()._init_(R) + + ## Overide and handle inputs to redirect to appropriate classes + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + """ + return f"{self._G}-invariant subalgebra of Orlik-Solomon algebra of {self._M}" \ No newline at end of file diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 28bc2511d3e..e1dd86d75d7 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -12,14 +12,16 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.misc.cachefunc import cached_method +## TODO: COMB THORUGH TO MAKE SURE ALL STUFF IS HAPPENING IN REPN NOT IN MODULE + from sage.modules.with_basis.subquotient import SubmoduleWithBasis -from sage.structure.unique_representation import UniqueRepresentation from sage.categories.finitely_generated_semigroups import FinitelyGeneratedSemigroups from sage.categories.finite_dimensional_modules_with_basis import FiniteDimensionalModulesWithBasis from sage.categories.groups import Groups from sage.sets.family import Family +import operator + class FiniteDimensionalInvariantModule(SubmoduleWithBasis): r""" Construct the `S`-invariant submodule of `M`. When a semigroup `S` acts on a module @@ -30,10 +32,61 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): M^S = \{m \in M : s\cdot m = m,\, \forall s \in S \} - NOTE: The current implementation works when `S` is a finitely-generated semigroup, - and when `M` is a finite-dimensional free module with a distinguished basis. + INPUTS: + + - ``R`` -- an instance of a ``Representation`` of a semigroup `S` + acting on the module `M`. + + OUTPUTS: + + - ``MS`` -- the invariant algebra of the semigroup action of `S` on `M`, or + equivalently, the isotypic component of the representation of + `S` carried by `M` corresponding to the trivial character. + + EXAMPLES:: + + sage: G = CyclicPermutationGroup(3) + sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g, m: M.term(g(m)) #cyclically permute coordinates + sage: R = Representation(G, M, action) + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: [I.lift(b) for b in I.basis()] + [M[1] + M[2] + M[3]] + + sage: G = CyclicPermutationGroup(3) + sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g, m: M.term(g(m)) #cyclically permute coordinates + sage: R = Representation(G, M, action, side='right') #same as last but on right + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: g = G.an_element(); g + (1,2,3) + sage: r = R.an_element(); r + 2*M[1] + 2*M[2] + 3*M[3] + sage: R.side() + 'right' + sage: r*g + 3*M[1] + 2*M[2] + 2*M[3] + sage: I = FiniteDimensionalInvariantModule(R) + sage: [I.lift(b) for b in I.basis()] + [M[1] + M[2] + M[3]] + + sage: G = SymmetricGroup(3) + sage: R = G.regular_representation(QQ) + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: [I.lift(b).to_vector() for b in I.basis()] + [(1, 1, 1, 1, 1, 1)] + + .. NOTE:: + + The current implementation works when `S` is a finitely-generated semigroup, + and when `M` is a finite-dimensional free module with a distinguished basis. + + .. TODO:: - TODO:: Extend when `M` does not have a basis and `S` is a permutation group using: - https://arxiv.org/abs/0812.3082 - https://www.dmtcs.org/pdfpapers/dmAA0123.pdf @@ -42,46 +95,11 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): def __init__(self, R, *args, **kwargs): """ - INPUTS:: - - - ``R`` -- an instance of a ``Representation`` of a semigroup `S` - acting on the module `M`. - OUTPUTS:: - - ``MS`` -- the invariant algebra of the semigroup action of `S` on `M`, or - equivalently, the isotypic component of the representation of - `S` carried by `M` corresponding to the trivial character. - - EXAMPLES:: - sage: G = CyclicPermutationGroup(3) - sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') - sage: from sage.modules.with_basis.representation import Representation - sage: action = lambda g, m: M.term(g(m)) #cyclically permute coordinates - sage: R = Representation(G, M, action) - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) - sage: [I.lift(b) for b in I.basis()] - [M[1] + M[2] + M[3]] - - sage: G = CyclicPermutationGroup(3) - sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') - sage: from sage.modules.with_basis.representation import Representation - sage: action = lambda m, g: M.term(g(m)) #cyclically permute coordinates - sage: R = Representation(G, M, action, side='right') #same as last but on right - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) - sage: [I.lift(b) for b in I.basis()] - [M[1] + M[2] + M[3]] + TESTS:: - sage: G = SymmetricGroup(3) - sage: R = G.regular_representation(QQ) - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) - sage: [I.lift(b).to_vector() for b in I.basis()] - [(1, 1, 1, 1, 1, 1)] - TESTS:: """ self._semigroup_representation = R @@ -95,25 +113,39 @@ def __init__(self, R, *args, **kwargs): if self._ambient_module not in FiniteDimensionalModulesWithBasis: raise ValueError(f'{self._ambient_module} is not finite dimensional') - self._on_ambient_basis = R._on_basis + try: + self._on_ambient_basis = R._on_basis + except: + self._on_ambient_basis = operator.mul + + try: + amb_basis = R._on_basis + def _invariant_map(g, x): + return self._amb_basis(g, *x.support()) - x + except AttributeError: + def _invariant_map(g, x): + return g*x - x self._side = kwargs.pop('side', R.side()) unitriangular = kwargs.pop('unitriangular', False) category = kwargs.pop('category', R.category().Subobjects()) - if self._side == 'left': - self._invariant_map = lambda g,x: self._on_ambient_basis(g,x.support()[0]) - x - else: - self._invariant_map = lambda x,g: self._on_ambient_basis(x.support()[0],g) - x + # The left/right multiplication is taken care of + # by self._semigroup_representation, so here + # we can just pass the left multiplication + def _invariant_map(g,x): + return(self._on_ambient_basis(g, *x.support()) - x) + + self._invariant_map = _invariant_map + # Give the intersection of kernels of the map `s*x-x` to determine when # `s*x = x` for all generators `s` of `S` basis = self._ambient_module.annihilator_basis( self._semigroup.gens(), action = self._invariant_map, - side = self._side) - + side = 'left') super().__init__(Family(basis), support_order = self._ambient_module._compute_support_order(basis), @@ -161,12 +193,13 @@ def semigroup(self): return self._semigroup def action_on_basis(self,*args): - if len(args) == 0: return self._action_on_basis return self._action_on_basis(args) + def _test_ + class Element(SubmoduleWithBasis.Element): def _mul_(self, other): @@ -212,13 +245,16 @@ def _acted_upon_(self, scalar, self_on_left = False): return None -class FiniteDimensionalTwistedInvariantModule(FiniteDimensionalInvariantModule): + +# Group action should be lifted on the module + +class FiniteDimensionalTwistedInvariantModule(SubmoduleWithBasis): r""" Construct the `\chi`-twisted invariant submodule of `M`. When a semigroup `S` acts on a module `M`, the `\chi`-twisted invariant submodule of `M` is the isotypic component of the representation `M` corresponding to the irreducible character `\chi`. - ..MATH:: + .. MATH:: M^S = \{m \in M : s\cdot m = m,\, \forall s \in S \} @@ -234,8 +270,15 @@ def __init__(self, R, character = 'trivial'): pass + def projection(self, element): """ - Give the projection of element onto self + Give the projection of element (in `self.module()`) onto self """ pass + + class Element + + _lmul_ + _rmul_ + _acted_upon_ \ No newline at end of file diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 6570c861b37..19ef8bcbcad 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -536,7 +536,7 @@ def _left_on_basis(self, g, m): sage: R = G.regular_representation() sage: R._test_representation() # indirect doctest """ - return self.monomial(g*m) + return self._module.monomial(g*m) def _right_on_basis(self, g, m): """ @@ -548,7 +548,7 @@ def _right_on_basis(self, g, m): sage: R = G.regular_representation(side="right") sage: R._test_representation() # indirect doctest """ - return self.monomial(m*g) + return self._modules.monomial(m*g) class TrivialRepresentation(Representation_abstract): """ From 6ae01b4e98c892cf84215e16a5ed0f40549f4c04 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 21 Jun 2021 20:37:45 -0500 Subject: [PATCH 208/336] Remove invariant algebra class --- .../finite_dimensional_invariant_algebras.py | 114 ------------------ 1 file changed, 114 deletions(-) delete mode 100644 src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py deleted file mode 100644 index 2ec2dd8c7f0..00000000000 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_invariant_algebras.py +++ /dev/null @@ -1,114 +0,0 @@ -r""" -Invariant algebras -""" - -# **************************************************************************** -# Copyright (C) 2021 Trevor K. Karn -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** - -from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule -from sage.categories.algebras import Algebras - -class FiniteDimensionalInvariantAlgebra(FiniteDimensionalInvariantModule): - - def __init__(self, R, *args, **kwargs): - """ - INPUTS:: - - - ``R`` -- an instance of a ``Representation`` of a semigroup `S` - acting on the algebra `A`. - - OUTPUTS:: - - - ``AS`` -- the invariant algebra of the semigroup action of `S` on `A`, or - equivalently, the isotypic component of the representation of - `S` carried by `A` corresponding to the trivial character. - - EXAMPLES:: - - sage: S = Set({1,2,3}) - sage: L = S.subsets_lattice() - sage: M = L.moebius_algebra(QQ) - sage: A = M.a_realization() - sage: G = SymmetricGroup(3) - sage: on_basis = lambda g,x: A.monomial(Set(g(s) for s in S)) - sage: from sage.modules.with_basis.representation import Representation - sage: R = Representation(G, A, on_basis) - sage: from sage.algebras.finite_dimensional_algebras.finite_dimensional_invariant_algebras import FiniteDimensionalInvariantAlgebra - sage: I = FiniteDimensionalInvariantAlgebra(R) - sage: I.basis() - (E[{}], E[{1}] + E[{2}] + E[{3}], E[{1,2}] + E[{1,3}] + E[{2,3}], E[{1,2,3}]) - sage: e = I.an_element(); e - - sage: G = SymmetricGroup(4) - sage: table = [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])] - sage: A = FiniteDimensionalAlgebra(GF(3), table, names = 'e') - sage: R = Representation(G, A, on_basis) - sage: I = FiniteDimensionalInvariantAlgebra(R) - - sage: G = CyclicPermutationGroup(3) - sage: M = algebras.Exterior(QQ, 'x', 3) - sage: from sage.modules.with_basis.representation import Representation - sage: on_basis = lambda g,m: M.prod([M.monomial(tuple([g(j+1)-1])) for j in m]) #cyclically permute generators - sage: from sage.categories.algebras import Algebras - sage: R = Representation(G, M, on_basis, category = Algebras(QQ).WithBasis().FiniteDimensional()) - sage: I = FiniteDimensionalInvariantAlgebra(R); I - (Cyclic group of order 3 as a permutation group)-invariant subalgebra of - The exterior algebra of rank 3 over Rational Field - sage: I.basis() - (1, x0 + x1 + x2, x0*x1*x2) - - TESTS:: - - sage: G = CyclicPermutationGroup(3) - sage: M = algebras.Exterior(QQ, 'x', 3) - sage: from sage.modules.with_basis.representation import Representation - sage: on_basis = lambda g,m: M.prod([M.monomial(tuple([g(j+1)-1])) for j in m]) #cyclically permute generators - sage: from sage.categories.algebras import Algebras - sage: R = Representation(G, M, on_basis, category = Algebras(QQ)) - sage: from sage.algebras.finite_dimensional_algebras.finite_dimensional_invariant_algebras \ - ....: import FiniteDimensionalInvariantAlgebra - sage: I = FiniteDimensionalInvariantAlgebra(R) - Traceback (most recent call last): - ... - ValueError: 'category' keyword argument must be FiniteDimensional - - sage: R = Representation(G, M, on_basis, category = Algebras(QQ).FiniteDimensional()) - sage: I = FiniteDimensionalInvariantAlgebra(R) - Traceback (most recent call last): - ... - ValueError: 'category' keyword argument must be WithBasis - - """ - - # Check that the representation indeed comes from an algebra - if R._module not in Algebras(): - raise ValueError(f'{R._module} is not an algebra') - - if R._module not in Algebras().FiniteDimensional().WithBasis(): - raise NotImplementedError(f'{R._module} must be finite-dimensional with a basis') - - if 'category' not in kwargs: - category = Algebras().FiniteDimensional().WithBasis() - kwargs['category'] = category - else: - if 'FiniteDimensional' not in kwargs['category'].axioms(): - raise ValueError("'category' keyword argument must be FiniteDimensional") - if 'WithBasis' not in kwargs['category'].axioms(): - raise ValueError("'category' keyword argument must be WithBasis") - - super().__init__(R, *args, **kwargs) - - def _repr_(self): - """ - EXAMPLES:: - - """ - - return f"({self._semigroup})-invariant subalgebra of {self._ambient_module}" From 63ddf15f24e44bf9cc34d63b9d59139940a08eec Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 22 Jun 2021 16:50:10 -0500 Subject: [PATCH 209/336] Remove OS changes --- src/sage/algebras/orlik_solomon.py | 80 +------------------ src/sage/modules/with_basis/invariant.py | 97 ++++++++++++++---------- 2 files changed, 56 insertions(+), 121 deletions(-) diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py index 6b6f290ffee..893905af74d 100644 --- a/src/sage/algebras/orlik_solomon.py +++ b/src/sage/algebras/orlik_solomon.py @@ -1,10 +1,5 @@ r""" Orlik-Solomon Algebras - -AUTHORS: - -- William Slofstra, Travis Scrimshaw (2015): Initial version -- Trevor Karn (2021-06-18): OrlikSolomonAlgebraInvariant """ # **************************************************************************** @@ -22,8 +17,7 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.categories.algebras import Algebras from sage.sets.family import Family -from sage.algebras.finite_dimensional_algebras.finite_dimensional_invariant_algebras import ( - FiniteDimensionalInvariantAlgebra) + class OrlikSolomonAlgebra(CombinatorialFreeModule): r""" @@ -451,75 +445,3 @@ def degree_on_basis(self, m): """ return len(m) -class OrlikSolomonAlgebraInvariant(FiniteDimensionalInvariantAlgebra): - r""" - When a group `G` acts on the groundset `E` of a matroid `M = (E,I)`, - it induces an action of `G` by ring automorphisms on the Orlik-Solomon - algebra of `M`. - - INPUT: - - - ``R`` - a ring - - ``M`` - a matroid - - ``G`` - a group (which acts on the groundset of ``M``) - - ``action_on_groundset`` - an action of ``G`` on the groundset of ``M`` - - OUTPUT: - - - ``OSG`` - the ``G``-invariant subalgebra of the Orlik-Solomon algebra - - The action on the groundset should be defined on the groundset without - any attempt to pass it to the Orlik-Solomon algebra. For example, if - the groundset is `\{1,2,...,n\}` there is a natural action of the - symmetric group `S_n` on the groundset. We can encode this in the - """ - - def __init__(self, R, M, G, action_on_groundset, ordering = None): - """ - EXAMPLES:: - - sage: M = matroids.Uniform(2,3) - sage: G = SymmetricGroup(3) - sage: action_on_groundset = lambda g,e: g(e+1)-1 #account for index shift - sage: from sage.algebras.orlik_solomon import OrlikSolomonAlgebraInvariant - sage: I = OrlikSolomonAlgebraInvariant(QQ, M, G, lambda g,x: g(x)); I - Symmetric group of order 3! as a permutation group-invariant - subalgebra of U(2, 3): Matroid of rank 2 on 3 elements with - circuit-closures - {2: {{0, 1, 2}}} - sage: I.basis() - [OS{}, OS{0} + OS{1} + OS{2}] - sage: x = I.an_element(); x - - - """ - from sage.modules.with_basis.representation import Representation - from sage.categories.algebras import Algebras - - self._M = M - self._G = G - self._ambient_orlik_solomon = M.orlik_solomon_algebra(R, ordering = ordering) - - # Convert from an action on the groundset to an action by ring automorphisms - # on the Orlik-Solomon algebra by extending the action to generators indexed - # by the groundset. - OS = self._ambient_orlik_solomon # for readability - on_basis = lambda g,e: OS.prod([OS.monomial((action_on_groundset,)) for j in e]) - - rep = Representation(G, OS, on_basis, - category = Algebras().FiniteDimensional().WithBasis()) - - self._representation = rep - - super()._init_(R) - - ## Overide and handle inputs to redirect to appropriate classes - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - """ - return f"{self._G}-invariant subalgebra of Orlik-Solomon algebra of {self._M}" \ No newline at end of file diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index e1dd86d75d7..75e4b31e64f 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -20,8 +20,6 @@ from sage.categories.groups import Groups from sage.sets.family import Family -import operator - class FiniteDimensionalInvariantModule(SubmoduleWithBasis): r""" Construct the `S`-invariant submodule of `M`. When a semigroup `S` acts on a module @@ -95,12 +93,37 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): def __init__(self, R, *args, **kwargs): """ - - TESTS:: + + sage: G = QQ # a group that is not finitely generated + sage: M = CombinatorialFreeModule(QQ, [1,2,3]) + sage: on_basis = lambda g,m: M.term(m) # trivial rep'n + sage: from sage.modules.with_basis.representation import Representation + sage: R = Representation(G,M,on_basis) + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + Traceback (most recent call last): + ... + ValueError: Rational Field is not finitely generated - - + sage: G = SymmetricGroup(3) + sage: M = PolynomialRing(QQ, ['x1','x2','x3']) + sage: on_basis = lambda g,m: M.prod(M('x'+str(g(Integer(x[-1])))) for x in m.support()) + sage: f = M('x1')*M('x3') + sage: g = G.an_element(); g + (2,3) + sage: on_basis(g,f) + x1*x2 + sage: R = Representation(G, M, on_basis) + sage: I = FiniteDimensionalInvariantModule(R) + Traceback (most recent call last): + ... + ValueError: Multivariate Polynomial Ring in x1, x2, x3 + over Rational Field is not finite-dimensional + + sage: #from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: #I + sage: #TestSuite(I).run() """ self._semigroup_representation = R self._semigroup = R.semigroup() @@ -108,49 +131,39 @@ def __init__(self, R, *args, **kwargs): if self._semigroup not in FinitelyGeneratedSemigroups: raise ValueError(f'{self._semigroup} is not finitely generated') - self._ambient_module = R._module - - if self._ambient_module not in FiniteDimensionalModulesWithBasis: - raise ValueError(f'{self._ambient_module} is not finite dimensional') - - try: - self._on_ambient_basis = R._on_basis - except: - self._on_ambient_basis = operator.mul - - try: - amb_basis = R._on_basis - def _invariant_map(g, x): - return self._amb_basis(g, *x.support()) - x - except AttributeError: - def _invariant_map(g, x): - return g*x - x - - self._side = kwargs.pop('side', R.side()) - - unitriangular = kwargs.pop('unitriangular', False) - category = kwargs.pop('category', R.category().Subobjects()) + if self._semigroup_representation._module not in ( + FiniteDimensionalModulesWithBasis): + raise ValueError( + f'{self._semigroup_representation._module + } is not finite dimensional') # The left/right multiplication is taken care of # by self._semigroup_representation, so here - # we can just pass the left multiplication - def _invariant_map(g,x): - return(self._on_ambient_basis(g, *x.support()) - x) - + # we can just pass the left multiplication. + # This means that the side argument of annihilator_basis + # (see below) will always be side = 'left' + if self._semigroup_representation.side() == 'left': + def _invariant_map(g, x): + return g*x - x + elif self._semigroup_representation.side() == 'right': + def _invariant_map(g, x): + return x*g - x self._invariant_map = _invariant_map + category = kwargs.pop('category', R.category().Subobjects()) + # Give the intersection of kernels of the map `s*x-x` to determine when # `s*x = x` for all generators `s` of `S` - basis = self._ambient_module.annihilator_basis( + basis = self._semigroup_representation.annihilator_basis( self._semigroup.gens(), action = self._invariant_map, side = 'left') super().__init__(Family(basis), - support_order = self._ambient_module._compute_support_order(basis), + support_order = self._semigroup_representation._compute_support_order(basis), ambient = self._semigroup_representation, - unitriangular = unitriangular,#is this right? + unitriangular = False,#is this right? category = category, *args, **kwargs) @@ -162,14 +175,14 @@ def _repr_(self): sage: G = CyclicPermutationGroup(3) sage: from sage.modules.with_basis.representation import Representation sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: R = Representation(G,M,lambda g,x: M.term(g(x))) + sage: R = Representation(G,M,lambda g,x: M.monomial(g(x))) sage: FiniteDimensionalInvariantModule(R) (Cyclic group of order 3 as a permutation group)-invariant submodule of Free module generated by {1, 2, 3} over Rational Field """ - return f"({self._semigroup})-invariant submodule of {self._ambient_module}" + return f"({self._semigroup})-invariant submodule of {self._semigroup_representation._module}" def semigroup(self): @@ -198,7 +211,7 @@ def action_on_basis(self,*args): return self._action_on_basis(args) - def _test_ + # def _test_ class Element(SubmoduleWithBasis.Element): @@ -277,8 +290,8 @@ def projection(self, element): """ pass - class Element + # class Element - _lmul_ - _rmul_ - _acted_upon_ \ No newline at end of file + # _lmul_ + # _rmul_ + # _acted_upon_ \ No newline at end of file From f93c224bf2fac4f7c72ff90538dc9e06ad12f735 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 22 Jun 2021 18:52:26 -0500 Subject: [PATCH 210/336] Modify invariant.py to work only in the Representation (not in the ambient module of the Representation) --- src/sage/modules/with_basis/invariant.py | 85 ++++++++++++++---------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 75e4b31e64f..67752c573af 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -26,7 +26,7 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): `M`, the invariant module is the collection of elements `m` in `M` such that `s \cdot m = m` for all `s \in S. - ..MATH:: + .. MATH:: M^S = \{m \in M : s\cdot m = m,\, \forall s \in S \} @@ -78,6 +78,22 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: [I.lift(b).to_vector() for b in I.basis()] [(1, 1, 1, 1, 1, 1)] + sage: G = CyclicPermutationGroup(3) + sage: M = algebras.Exterior(QQ, 'x', 3) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators + sage: from sage.categories.algebras import Algebras + sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) + sage: I = FiniteDimensionalInvariantModule(R) + sage: [I.lift(b) for b in I.basis()] + [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] + sage: B = I.basis() + sage: m = 3*B[0] + 2*B[1] + 7*B[3] + sage: I.lift(m) + 7*x0*x1*x2 + 2*x0 + 2*x1 + 2*x2 + 3 + sage: m^2 + 42*B[3] + 12*B[1] + 9*B[0] + .. NOTE:: The current implementation works when `S` is a finitely-generated semigroup, @@ -95,47 +111,27 @@ def __init__(self, R, *args, **kwargs): """ TESTS:: - sage: G = QQ # a group that is not finitely generated + sage: G = GroupExp()(QQ) # a group that is not finitely generated sage: M = CombinatorialFreeModule(QQ, [1,2,3]) sage: on_basis = lambda g,m: M.term(m) # trivial rep'n sage: from sage.modules.with_basis.representation import Representation - sage: R = Representation(G,M,on_basis) - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) - Traceback (most recent call last): - ... - ValueError: Rational Field is not finitely generated - - sage: G = SymmetricGroup(3) - sage: M = PolynomialRing(QQ, ['x1','x2','x3']) - sage: on_basis = lambda g,m: M.prod(M('x'+str(g(Integer(x[-1])))) for x in m.support()) - sage: f = M('x1')*M('x3') - sage: g = G.an_element(); g - (2,3) - sage: on_basis(g,f) - x1*x2 sage: R = Representation(G, M, on_basis) + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule sage: I = FiniteDimensionalInvariantModule(R) Traceback (most recent call last): ... - ValueError: Multivariate Polynomial Ring in x1, x2, x3 - over Rational Field is not finite-dimensional + ValueError: Multiplicative form of Rational Field is not finitely generated - sage: #from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: #I - sage: #TestSuite(I).run() """ + self._semigroup_representation = R self._semigroup = R.semigroup() if self._semigroup not in FinitelyGeneratedSemigroups: raise ValueError(f'{self._semigroup} is not finitely generated') - if self._semigroup_representation._module not in ( - FiniteDimensionalModulesWithBasis): - raise ValueError( - f'{self._semigroup_representation._module - } is not finite dimensional') + if self._semigroup_representation._module not in FiniteDimensionalModulesWithBasis: + raise ValueError(f'{self._semigroup_representation._module} is not finite dimensional') # The left/right multiplication is taken care of # by self._semigroup_representation, so here @@ -205,13 +201,28 @@ def semigroup(self): return self._semigroup - def action_on_basis(self,*args): - if len(args) == 0: - return self._action_on_basis + def semigroup_representation(self): + """ + Return the underlying representation of the invariant module. - return self._action_on_basis(args) + EXAMPLES:: - # def _test_ + sage: G = SymmetricGroup(3) + sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') + sage: action = lambda g,x: M.term(g(x)) + sage: from sage.modules.with_basis.representation import Representation + sage: R = Representation(G, M, action); R + Representation of Symmetric group of order 3! as a permutation group indexed by {1, 2, 3} over Rational Field + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: I.semigroup_representation() + Representation of Symmetric group of order 3! as a permutation group indexed by {1, 2, 3} over Rational Field + + """ + + return self._semigroup_representation + + #def _test_ class Element(SubmoduleWithBasis.Element): @@ -228,7 +239,7 @@ def _lmul_(self, right): sage: #### multiply group element """ - if right in self.parent()._semigroup and self.parent()._side == 'right': + if right in self.parent()._semigroup and self.parent()._semigroup_representation._side == 'right': return self return super()._lmul_(right) @@ -241,7 +252,7 @@ def _rmul_(self, left): sage: ### create invariant module sage: ### multiply group element """ - if left in self.parent()._semigroup and self.parent()._side == 'left': + if left in self.parent()._semigroup and self.parent()._semigroup_representation._side == 'left': return self return super()._rmul_(left) @@ -252,14 +263,16 @@ def _acted_upon_(self, scalar, self_on_left = False): """ - if scalar in self.parent()._semigroup and self_on_left == (self.parent()._side == 'right'): + if scalar in self.parent()._semigroup and self_on_left == (self.parent()._semigroup_representation._side == 'right'): return self return None -# Group action should be lifted on the module +# Group action should be lifted on the module because???? + +# Confused about the representatoin vs lift to module business. class FiniteDimensionalTwistedInvariantModule(SubmoduleWithBasis): r""" From 1c8749390f2bff8c9a829a7ddf6f8c5d29d08707 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 22 Jun 2021 19:18:41 -0500 Subject: [PATCH 211/336] Add tests for arithmetic in FiniteDimensionalInvariantModule, fix bug with multiplication of the underlying representation's underlying module's base ring --- src/sage/modules/with_basis/invariant.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 67752c573af..aeb6c179e8c 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -77,6 +77,8 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: I = FiniteDimensionalInvariantModule(R) sage: [I.lift(b).to_vector() for b in I.basis()] [(1, 1, 1, 1, 1, 1)] + sage: [I.lift(3*b).to_vector() for b in I.basis()] + [(3, 3, 3, 3, 3, 3)] sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) @@ -90,9 +92,13 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: B = I.basis() sage: m = 3*B[0] + 2*B[1] + 7*B[3] sage: I.lift(m) - 7*x0*x1*x2 + 2*x0 + 2*x1 + 2*x2 + 3 + 3 + 2*x0 + 7*x0*x1*x2 + 2*x1 + 2*x2 sage: m^2 - 42*B[3] + 12*B[1] + 9*B[0] + 9*B[0] + 12*B[1] + 42*B[3] + sage: I.lift(m+m) + 6 + 4*x0 + 14*x0*x1*x2 + 4*x1 + 4*x2 + sage: I.lift(7*m) + 21 + 14*x0 + 49*x0*x1*x2 + 14*x1 + 14*x2 .. NOTE:: @@ -127,12 +133,11 @@ def __init__(self, R, *args, **kwargs): self._semigroup_representation = R self._semigroup = R.semigroup() + # A check for self._semigroup_representation._module not in FiniteDimensionalModulesWithBasis + # is not required, because a ``Representation`` cannot be built without a basis if self._semigroup not in FinitelyGeneratedSemigroups: raise ValueError(f'{self._semigroup} is not finitely generated') - if self._semigroup_representation._module not in FiniteDimensionalModulesWithBasis: - raise ValueError(f'{self._semigroup_representation._module} is not finite dimensional') - # The left/right multiplication is taken care of # by self._semigroup_representation, so here # we can just pass the left multiplication. @@ -242,6 +247,10 @@ def _lmul_(self, right): if right in self.parent()._semigroup and self.parent()._semigroup_representation._side == 'right': return self + elif right in self.parent()._semigroup_representation._module.base_ring(): + P = self.parent() + return P.retract(P.lift(self)*right) + return super()._lmul_(right) # rmul -- left * self @@ -255,6 +264,10 @@ def _rmul_(self, left): if left in self.parent()._semigroup and self.parent()._semigroup_representation._side == 'left': return self + elif left in self.parent()._semigroup_representation._module.base_ring(): + P = self.parent() + return P.retract(left*P.lift(self)) + return super()._rmul_(left) def _acted_upon_(self, scalar, self_on_left = False): From 1792be1b21f0e3d3bb8d817ba30353533039295b Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 23 Jun 2021 07:51:38 -0500 Subject: [PATCH 212/336] Add tests to invariant.py for full coverage --- src/sage/modules/with_basis/invariant.py | 258 ++++++++++++++++++++--- 1 file changed, 225 insertions(+), 33 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index aeb6c179e8c..d1a10b3f777 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -95,8 +95,12 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): 3 + 2*x0 + 7*x0*x1*x2 + 2*x1 + 2*x2 sage: m^2 9*B[0] + 12*B[1] + 42*B[3] + sage: m+m + 6*B[0] + 4*B[1] + 14*B[3] sage: I.lift(m+m) 6 + 4*x0 + 14*x0*x1*x2 + 4*x1 + 4*x2 + sage: 7*m + 21*B[0] + 14*B[1] + 49*B[3] sage: I.lift(7*m) 21 + 14*x0 + 49*x0*x1*x2 + 14*x1 + 14*x2 @@ -232,51 +236,241 @@ def semigroup_representation(self): class Element(SubmoduleWithBasis.Element): def _mul_(self, other): + """ + EXAMPLES:: + + sage: M = CombinatorialFreeModule(QQ,[1,2,3],prefix='M'); + sage: G = CyclicPermutationGroup(3); G.rename('G') + sage: g = G.an_element(); g + (1,2,3) + sage: from sage.modules.with_basis.representation import Representation + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: R = Representation(G,M,lambda g,x:M.monomial(g(x))); R.rename('R') + sage: I = FiniteDimensionalInvariantModule(R) + sage: B = I.basis() + sage: [I.lift(b) for b in B] + [M[1] + M[2] + M[3]] + sage: v = B[0] + sage: v*v + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for *: 'R' and 'R' + sage: (1/2)*v + 1/2*B[0] + sage: v*(1/2) + 1/2*B[0] + + sage: G = CyclicPermutationGroup(3); G.rename('G') + sage: M = algebras.Exterior(QQ, 'x', 3) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators + sage: from sage.categories.algebras import Algebras + sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional(), side = 'right') + sage: I = FiniteDimensionalInvariantModule(R); I.rename('I') + sage: B = I.basis() + sage: v = B[0] + 2*B[1]; I.lift(v) + 1 + 2*x0 + 2*x1 + 2*x2 + sage: w = B[2]; I.lift(w) + x0*x1 - x0*x2 + x1*x2 + sage: v*w + B[2] + 6*B[3] + sage: I.lift(v*w) + x0*x1 + 6*x0*x1*x2 - x0*x2 + x1*x2 + sage: w*v + B[2] + 6*B[3] + sage: (1/2)*v + 1/2*B[0] + B[1] + sage: w*(1/2) + 1/2*B[2] + sage: g = G((1,3,2)) + sage: v*g + B[0] + 2*B[1] + sage: w*g + B[2] + sage: g*v + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for *: 'G' and 'I' + + sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) + sage: I = FiniteDimensionalInvariantModule(R); I.rename('I') + sage: B = I.basis() + sage: v = B[0] + 2*B[1]; I.lift(v) + 1 + 2*x0 + 2*x1 + 2*x2 + sage: w = B[2]; I.lift(w) + x0*x1 - x0*x2 + x1*x2 + sage: v*w + B[2] + 6*B[3] + sage: I.lift(v*w) + x0*x1 + 6*x0*x1*x2 - x0*x2 + x1*x2 + sage: w*v + B[2] + 6*B[3] + sage: (1/2)*v + 1/2*B[0] + B[1] + sage: w*(1/2) + 1/2*B[2] + sage: g = G((1,3,2)) + sage: v*v + B[0] + 4*B[1] + sage: g*w + B[2] + sage: v*g + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for *: 'I' and 'G' + + """ P = self.parent() - return P.retract(P.lift(self) * P.lift(other)) + try: + return P.retract(P.lift(self) * P.lift(other)) + except: + return P.retract(P.lift(self)*other) - # lmul -- self*right def _lmul_(self, right): """ + Give the product of ``self*right`` + EXAMPLES:: - sage: #### create invariant module - sage: #### multiply group element + sage: M = CombinatorialFreeModule(QQ,[1,2,3]) + sage: G = CyclicPermutationGroup(3) + sage: g = G.an_element(); g + (1,2,3) + sage: from sage.modules.with_basis.representation import Representation + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: R = Representation(G,M,lambda g,x: M.monomial(g(x)), side = 'right') + sage: I = FiniteDimensionalInvariantModule(R) + sage: v = I.an_element(); v + 2*B[0] + sage: v*g + 2*B[0] + sage: [v*g for g in G.list()] + [2*B[0], 2*B[0], 2*B[0]] + + sage: G = CyclicPermutationGroup(3) + sage: M = algebras.Exterior(QQ, 'x', 3) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators + sage: from sage.categories.algebras import Algebras + sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional(), side = 'right') + sage: I = FiniteDimensionalInvariantModule(R) + sage: B = I.basis() + sage: [I.lift(b) for b in B] + [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] + sage: [[b*g for g in G] for b in B] + [[B[0], B[0], B[0]], + [B[1], B[1], B[1]], + [B[2], B[2], B[2]], + [B[3], B[3], B[3]]] + sage: 3*I.basis()[0] + 3*B[0] + sage: 3*B[0] + B[1]*2 + 3*B[0] + 2*B[1] + """ - if right in self.parent()._semigroup and self.parent()._semigroup_representation._side == 'right': + if right in self.parent()._semigroup and self.parent()._semigroup_representation.side() == 'right': return self elif right in self.parent()._semigroup_representation._module.base_ring(): - P = self.parent() - return P.retract(P.lift(self)*right) + # This preserves the structure of the invariant as a + # ``.base_ring()``-module + return self._mul_(right) return super()._lmul_(right) - # rmul -- left * self def _rmul_(self, left): """ + Give the product of ``left * self`` + EXAMPLES:: - sage: ### create invariant module - sage: ### multiply group element + sage: M = CombinatorialFreeModule(QQ,[1,2,3]) + sage: G = CyclicPermutationGroup(3) + sage: g = G.an_element(); g + (1,2,3) + sage: from sage.modules.with_basis.representation import Representation + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: R = Representation(G,M,lambda g,x: M.monomial(g(x))) + sage: I = FiniteDimensionalInvariantModule(R) + sage: v = I.an_element(); v + 2*B[0] + sage: g*v + 2*B[0] + sage: [g*v for g in G.list()] + [2*B[0], 2*B[0], 2*B[0]] + + sage: G = CyclicPermutationGroup(3) + sage: M = algebras.Exterior(QQ, 'x', 3) + sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators + sage: from sage.categories.algebras import Algebras + sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) + sage: I = FiniteDimensionalInvariantModule(R) + sage: B = I.basis() + sage: [I.lift(b) for b in B] + [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] + sage: [[g*b for g in G] for b in B] + [[B[0], B[0], B[0]], + [B[1], B[1], B[1]], + [B[2], B[2], B[2]], + [B[3], B[3], B[3]]] + sage: 3*I.basis()[0] + 3*B[0] + sage: 3*B[0] + B[1]*2 + 3*B[0] + 2*B[1] """ - if left in self.parent()._semigroup and self.parent()._semigroup_representation._side == 'left': + if left in self.parent()._semigroup and self.parent()._semigroup_representation.side() == 'left': return self elif left in self.parent()._semigroup_representation._module.base_ring(): - P = self.parent() - return P.retract(left*P.lift(self)) + return self._mul_(left) return super()._rmul_(left) def _acted_upon_(self, scalar, self_on_left = False): """ EXAMPLES:: + + sage: G = CyclicPermutationGroup(3) + sage: M = CombinatorialFreeModule(QQ,[1,2,3]) + sage: from sage.modules.with_basis.representation import Representation + sage: R = Representation(G, M, lambda g,x: M.monomial(g(x))) + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: B = I.basis() + sage: [b._acted_upon_(G((1,3,2))) for b in B] + [B[0]] + + sage: R = Representation(G, M, lambda g,x: M.monomial(g(x)), side = 'right') + sage: I = FiniteDimensionalInvariantModule(R) + sage: B = I.basis() + sage: [b._acted_upon_(G((1,3,2)), self_on_left = True) for b in B] + [B[0]] + + sage: R = G.regular_representation(QQ) + sage: I = FiniteDimensionalInvariantModule(R) + sage: B = I.basis() + sage: [I.lift(b) for b in B] + [() + (1,2,3) + (1,3,2)] + sage: B[0]._acted_upon_(G((1,3,2))) + B[0] + sage: B[0]._acted_upon_(G((1,3,2)), self_on_left=True) == None + True + + sage: R = G.regular_representation(QQ, side = 'right') + sage: I = FiniteDimensionalInvariantModule(R) + sage: B = I.basis() + sage: [I.lift(b) for b in B] + [() + (1,2,3) + (1,3,2)] + sage: g = G((1,3,2)) + sage: B[0]._acted_upon_(g, self_on_left = True) + B[0] + sage: B[0]._acted_upon_(g, self_on_left = False) == None + True """ - if scalar in self.parent()._semigroup and self_on_left == (self.parent()._semigroup_representation._side == 'right'): + if scalar in self.parent()._semigroup and self_on_left == (self.parent()._semigroup_representation.side() == 'right'): return self @@ -293,31 +487,29 @@ class FiniteDimensionalTwistedInvariantModule(SubmoduleWithBasis): `M`, the `\chi`-twisted invariant submodule of `M` is the isotypic component of the representation `M` corresponding to the irreducible character `\chi`. - .. MATH:: - - M^S = \{m \in M : s\cdot m = m,\, \forall s \in S \} + .. NOTE: - NOTE: The current implementation works when `S` is a finitely-generated semigroup, - and when `M` is a finite-dimensional free module with a distinguished basis. - """ + The current implementation works when `S` is a finitely-generated semigroup, + and when `M` is a finite-dimensional free module with a distinguished basis. +# """ - def __init__(self, R, character = 'trivial'): +# def __init__(self, R, character = 'trivial'): - super.__init__(R) +# super.__init__(R) - if character != 'trivial': +# if character != 'trivial': - pass +# pass - def projection(self, element): - """ - Give the projection of element (in `self.module()`) onto self - """ - pass +# def projection(self, element): +# """ +# Give the projection of element (in `self.module()`) onto self +# """ +# pass - # class Element +# # class Element - # _lmul_ - # _rmul_ - # _acted_upon_ \ No newline at end of file +# # _lmul_ +# # _rmul_ +# # _acted_upon_ \ No newline at end of file From d5bb24c3f2ed66a96c448e4bb48e8d7c21588e72 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 6 Jul 2021 13:04:46 -0500 Subject: [PATCH 213/336] Remove draft of twisted invariant. Moved to trac #32145 --- src/sage/modules/with_basis/invariant.py | 40 +----------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index d1a10b3f777..6b63c83b896 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -474,42 +474,4 @@ def _acted_upon_(self, scalar, self_on_left = False): return self - return None - - -# Group action should be lifted on the module because???? - -# Confused about the representatoin vs lift to module business. - -class FiniteDimensionalTwistedInvariantModule(SubmoduleWithBasis): - r""" - Construct the `\chi`-twisted invariant submodule of `M`. When a semigroup `S` acts on a module - `M`, the `\chi`-twisted invariant submodule of `M` is the isotypic component of the representation - `M` corresponding to the irreducible character `\chi`. - - .. NOTE: - - The current implementation works when `S` is a finitely-generated semigroup, - and when `M` is a finite-dimensional free module with a distinguished basis. -# """ - -# def __init__(self, R, character = 'trivial'): - -# super.__init__(R) - -# if character != 'trivial': - -# pass - - -# def projection(self, element): -# """ -# Give the projection of element (in `self.module()`) onto self -# """ -# pass - -# # class Element - -# # _lmul_ -# # _rmul_ -# # _acted_upon_ \ No newline at end of file + return None \ No newline at end of file From f121263d7dfd39e700aa7188445d8a02c7031f20 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 6 Jul 2021 13:30:56 -0500 Subject: [PATCH 214/336] Fix whitespace --- src/sage/modules/with_basis/invariant.py | 26 +++++++++---------- src/sage/modules/with_basis/representation.py | 21 +++++++-------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 6b63c83b896..5f8749161c2 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -12,8 +12,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -## TODO: COMB THORUGH TO MAKE SURE ALL STUFF IS HAPPENING IN REPN NOT IN MODULE - from sage.modules.with_basis.subquotient import SubmoduleWithBasis from sage.categories.finitely_generated_semigroups import FinitelyGeneratedSemigroups from sage.categories.finite_dimensional_modules_with_basis import FiniteDimensionalModulesWithBasis @@ -104,7 +102,7 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: I.lift(7*m) 21 + 14*x0 + 49*x0*x1*x2 + 14*x1 + 14*x2 - .. NOTE:: + .. NOTE:: The current implementation works when `S` is a finitely-generated semigroup, and when `M` is a finite-dimensional free module with a distinguished basis. @@ -120,7 +118,7 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): def __init__(self, R, *args, **kwargs): """ TESTS:: - + sage: G = GroupExp()(QQ) # a group that is not finitely generated sage: M = CombinatorialFreeModule(QQ, [1,2,3]) sage: on_basis = lambda g,m: M.term(m) # trivial rep'n @@ -153,9 +151,9 @@ def _invariant_map(g, x): elif self._semigroup_representation.side() == 'right': def _invariant_map(g, x): return x*g - x - + self._invariant_map = _invariant_map - + category = kwargs.pop('category', R.category().Subobjects()) # Give the intersection of kernels of the map `s*x-x` to determine when @@ -275,12 +273,12 @@ def _mul_(self, other): sage: v*w B[2] + 6*B[3] sage: I.lift(v*w) - x0*x1 + 6*x0*x1*x2 - x0*x2 + x1*x2 + x0*x1 + 6*x0*x1*x2 - x0*x2 + x1*x2 sage: w*v B[2] + 6*B[3] sage: (1/2)*v 1/2*B[0] + B[1] - sage: w*(1/2) + sage:remote w*(1/2) 1/2*B[2] sage: g = G((1,3,2)) sage: v*g @@ -302,7 +300,7 @@ def _mul_(self, other): sage: v*w B[2] + 6*B[3] sage: I.lift(v*w) - x0*x1 + 6*x0*x1*x2 - x0*x2 + x1*x2 + x0*x1 + 6*x0*x1*x2 - x0*x2 + x1*x2 sage: w*v B[2] + 6*B[3] sage: (1/2)*v @@ -373,7 +371,7 @@ def _lmul_(self, right): return self elif right in self.parent()._semigroup_representation._module.base_ring(): - # This preserves the structure of the invariant as a + # This preserves the structure of the invariant as a # ``.base_ring()``-module return self._mul_(right) @@ -410,9 +408,9 @@ def _rmul_(self, left): sage: [I.lift(b) for b in B] [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] sage: [[g*b for g in G] for b in B] - [[B[0], B[0], B[0]], - [B[1], B[1], B[1]], - [B[2], B[2], B[2]], + [[B[0], B[0], B[0]], + [B[1], B[1], B[1]], + [B[2], B[2], B[2]], [B[3], B[3], B[3]]] sage: 3*I.basis()[0] 3*B[0] @@ -430,7 +428,7 @@ def _rmul_(self, left): def _acted_upon_(self, scalar, self_on_left = False): """ EXAMPLES:: - + sage: G = CyclicPermutationGroup(3) sage: M = CombinatorialFreeModule(QQ,[1,2,3]) sage: from sage.modules.with_basis.representation import Representation diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 19ef8bcbcad..27fffed163d 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -5,7 +5,6 @@ - Travis Scrimshaw (2015-11-21): Initial version - Siddharth Singh (2020-03-21): Signed Representation -- Trevor Karn (2021-06-18): Add functionality to Representation """ #################################################################################### @@ -210,19 +209,19 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): pass category = kwargs.pop('category', Modules(module.base_ring()).WithBasis()) - + if side not in ["left", "right"]: raise ValueError('side must be "left" or "right"') - + self._left_repr = (side == "left") self._on_basis = on_basis self._module = module - + indices = module.basis().keys() - + if 'FiniteDimensional' in module.category().axioms(): category = category.FiniteDimensional() - + Representation_abstract.__init__(self, semigroup, module.base_ring(), indices, category=category, **module.print_options()) @@ -344,10 +343,10 @@ def product_by_coercion(self, left, right): ... TypeError: unsupported operand parent(s) for *: 'Representation of The Klein 4 group of order 4, as a permutation - group indexed by Subsets of {0, 1, 2, 3} over Rational Field' and - 'Representation of The Klein 4 group of order 4, as a permutation + group indexed by Subsets of {0, 1, 2, 3} over Rational Field' and + 'Representation of The Klein 4 group of order 4, as a permutation group indexed by Subsets of {0, 1, 2, 3} over Rational Field' - + sage: from sage.categories.algebras import Algebras sage: category = Algebras(QQ).FiniteDimensional().WithBasis() sage: T = Representation(G, E, on_basis, category=category) @@ -536,7 +535,7 @@ def _left_on_basis(self, g, m): sage: R = G.regular_representation() sage: R._test_representation() # indirect doctest """ - return self._module.monomial(g*m) + return self.monomial(g*m) def _right_on_basis(self, g, m): """ @@ -548,7 +547,7 @@ def _right_on_basis(self, g, m): sage: R = G.regular_representation(side="right") sage: R._test_representation() # indirect doctest """ - return self._modules.monomial(m*g) + return self.monomial(m*g) class TrivialRepresentation(Representation_abstract): """ From 93659dbd83273fd1926231c6f09a6988abbef40c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Jul 2021 10:58:06 +1000 Subject: [PATCH 215/336] Doing some additional cleanup and normalization of documentation and errors. --- src/sage/rings/padics/CA_template.pxi | 99 ++++++++------- src/sage/rings/padics/CR_template.pxi | 42 +++---- .../rings/padics/padic_ZZ_pX_CA_element.pyx | 38 +++--- .../rings/padics/padic_ZZ_pX_CR_element.pyx | 33 ++--- .../rings/padics/padic_ZZ_pX_FM_element.pyx | 10 +- .../padics/padic_capped_absolute_element.pyx | 14 +-- .../padics/padic_capped_relative_element.pyx | 105 ++++++++-------- src/sage/rings/padics/padic_ext_element.pyx | 14 ++- .../rings/padics/padic_fixed_mod_element.pyx | 10 +- .../padics/padic_floating_point_element.pyx | 66 +++++----- src/sage/rings/padics/padic_generic.py | 2 +- .../rings/padics/padic_generic_element.pyx | 115 +++++++++--------- .../rings/padics/padic_lattice_element.py | 24 ++-- .../rings/padics/padic_template_element.pxi | 70 ++++++----- src/sage/rings/padics/pow_computer.pyx | 4 +- 15 files changed, 332 insertions(+), 314 deletions(-) diff --git a/src/sage/rings/padics/CA_template.pxi b/src/sage/rings/padics/CA_template.pxi index 809b24116f5..5a3065ff10a 100644 --- a/src/sage/rings/padics/CA_template.pxi +++ b/src/sage/rings/padics/CA_template.pxi @@ -1,18 +1,20 @@ -""" +r""" Capped absolute template for complete discrete valuation rings In order to use this template you need to write a linkage file and gluing file. -For an example see mpz_linkage.pxi (linkage file) and padic_capped_absolute_element.pyx (gluing file). +For an example see ``mpz_linkage.pxi`` (linkage file) and +``padic_capped_absolute_element.pyx`` (gluing file). -The linkage file implements a common API that is then used in the class CAElement defined here. -See the documentation of mpz_linkage.pxi for the functions needed. +The linkage file implements a common API that is then used in the class +:class:`CAElement` defined here. +See the documentation of ``mpz_linkage.pxi`` for the functions needed. The gluing file does the following: -- ctypedef's celement to be the appropriate type (e.g. mpz_t) +- ctypedef's celement to be the appropriate type (e.g. ``mpz_t``) - includes the linkage file - includes this template -- defines a concrete class inheriting from CAElement, and implements +- defines a concrete class inheriting from :class:`CAElement`, and implements any desired extra methods AUTHORS: @@ -93,7 +95,7 @@ cdef class CAElement(pAdicTemplateElement): cdef CAElement _new_c(self): """ - Creates a new element with the same basic info. + Create a new element with the same basic info. TESTS:: @@ -118,7 +120,7 @@ cdef class CAElement(pAdicTemplateElement): cdef pAdicTemplateElement _new_with_value(self, celement value, long absprec): """ - Creates a new element with a given value and absolute precision. + Create a new element with a given value and absolute precision. Used by code that doesn't know the precision type. """ @@ -130,13 +132,13 @@ cdef class CAElement(pAdicTemplateElement): cdef int _get_unit(self, celement value) except -1: """ - Sets ``value`` to the unit of this p-adic element. + Set ``value`` to the unit of this p-adic element. """ cremove(value, self.value, self.absprec, self.prime_pow, True) cdef int check_preccap(self) except -1: """ - Checks that this element doesn't have precision higher than + Check that this element doesn't have precision higher than allowed by the precision cap. TESTS:: @@ -144,10 +146,10 @@ cdef class CAElement(pAdicTemplateElement): sage: ZpCA(5)(1).lift_to_precision(30) # indirect doctest Traceback (most recent call last): ... - PrecisionError: Precision higher than allowed by the precision cap. + PrecisionError: precision higher than allowed by the precision cap """ if self.absprec > self.prime_pow.ram_prec_cap: - raise PrecisionError("Precision higher than allowed by the precision cap.") + raise PrecisionError("precision higher than allowed by the precision cap") def __copy__(self): """ @@ -270,7 +272,9 @@ cdef class CAElement(pAdicTemplateElement): sage: ~R(5) * 5 1 + O(17^20) sage: ~R(5) - 7 + 3*17 + 10*17^2 + 13*17^3 + 6*17^4 + 3*17^5 + 10*17^6 + 13*17^7 + 6*17^8 + 3*17^9 + 10*17^10 + 13*17^11 + 6*17^12 + 3*17^13 + 10*17^14 + 13*17^15 + 6*17^16 + 3*17^17 + 10*17^18 + 13*17^19 + O(17^20) + 7 + 3*17 + 10*17^2 + 13*17^3 + 6*17^4 + 3*17^5 + 10*17^6 + 13*17^7 + + 6*17^8 + 3*17^9 + 10*17^10 + 13*17^11 + 6*17^12 + 3*17^13 + + 10*17^14 + 13*17^15 + 6*17^16 + 3*17^17 + 10*17^18 + 13*17^19 + O(17^20) sage: ~R(-1) == R(-1) #indirect doctest True """ @@ -503,7 +507,7 @@ cdef class CAElement(pAdicTemplateElement): else: if not exact_exp and self.absprec > 0: raise ValueError("in order to raise to a p-adic exponent, base must be a unit") - raise PrecisionError("Need more precision") + raise PrecisionError("need more precision") else: val = self.valuation_c() if exact_exp: @@ -529,7 +533,7 @@ cdef class CAElement(pAdicTemplateElement): return ans cdef pAdicTemplateElement _lshift_c(self, long shift): - """ + r""" Multiplies by `\pi^{\mbox{shift}}`. Negative shifts may truncate the result. @@ -561,7 +565,7 @@ cdef class CAElement(pAdicTemplateElement): return ans cdef pAdicTemplateElement _rshift_c(self, long shift): - """ + r""" Divides by ``π^{\mbox{shift}}``. Positive shifts may truncate the result. @@ -594,7 +598,7 @@ cdef class CAElement(pAdicTemplateElement): def add_bigoh(self, absprec): """ - Returns a new element with absolute precision decreased to + Return a new element with absolute precision decreased to ``absprec``. The precision never increases. INPUT: @@ -652,8 +656,8 @@ cdef class CAElement(pAdicTemplateElement): cpdef bint _is_exact_zero(self) except -1: """ - Tests whether this element is an exact zero, which is always - False for capped absolute elements. + Test whether this element is an exact zero, which is always + ``False`` for capped absolute elements. This function exists for compatibility with capped relative elements. @@ -667,7 +671,7 @@ cdef class CAElement(pAdicTemplateElement): cpdef bint _is_inexact_zero(self) except -1: """ - Determines whether this element is indistinguishable from + Determine whether this element is indistinguishable from zero. EXAMPLES:: @@ -684,7 +688,7 @@ cdef class CAElement(pAdicTemplateElement): def is_zero(self, absprec = None): r""" - Determines whether this element is zero modulo + Determine whether this element is zero modulo `\pi^{\mbox{absprec}}`. If ``absprec is None``, returns ``True`` if this element is @@ -706,23 +710,23 @@ cdef class CAElement(pAdicTemplateElement): sage: R(17^6).is_zero(absprec=10) Traceback (most recent call last): ... - PrecisionError: Not enough precision to determine if element is zero + PrecisionError: not enough precision to determine if element is zero """ if absprec is infinity: - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") cdef bint iszero = ciszero(self.value, self.prime_pow) if absprec is None: return iszero cdef long val = self.valuation_c() if isinstance(absprec, int): if iszero and absprec > self.absprec: - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") return val >= absprec if not isinstance(absprec, Integer): absprec = Integer(absprec) if iszero: if mpz_cmp_si((absprec).value, val) > 0: - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") else: return True return mpz_cmp_si((absprec).value, val) <= 0 @@ -745,7 +749,7 @@ cdef class CAElement(pAdicTemplateElement): def is_equal_to(self, _right, absprec=None): r""" - Determines whether the inputs are equal modulo + Determine whether the inputs are equal modulo `\pi^{\mbox{absprec}}`. INPUT: @@ -768,10 +772,10 @@ cdef class CAElement(pAdicTemplateElement): sage: R(13).is_equal_to(R(13+2^10),absprec=10) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision """ if absprec is infinity: - raise PrecisionError("Elements not known to enough precision") + raise PrecisionError("elements not known to enough precision") cdef CAElement right cdef long aprec, rprec, sval, rval if self.parent() is _right.parent(): @@ -787,10 +791,10 @@ cdef class CAElement(pAdicTemplateElement): if mpz_sgn((absprec).value) < 0: return True else: - raise PrecisionError("Elements not known to enough precision") + raise PrecisionError("elements not known to enough precision") aprec = mpz_get_si((absprec).value) if aprec > self.absprec or aprec > right.absprec: - raise PrecisionError("Elements not known to enough precision") + raise PrecisionError("elements not known to enough precision") return ccmp(self.value, right.value, aprec, aprec < self.absprec, aprec < right.absprec, self.prime_pow) == 0 cdef int _cmp_units(self, pAdicGenericElement _right) except -2: @@ -811,7 +815,7 @@ cdef class CAElement(pAdicTemplateElement): cdef pAdicTemplateElement lift_to_precision_c(self, long absprec): """ - Returns an arbitrary lift of this element to higher precision. + Return an arbitrary lift of this element to higher precision. If ``absprec`` is less than the absolute precision of this element this function will return the input element. @@ -819,7 +823,7 @@ cdef class CAElement(pAdicTemplateElement): INPUT: - ``absprec`` -- an integer, at most the precision cap of the - parent. + parent EXAMPLES:: @@ -860,7 +864,7 @@ cdef class CAElement(pAdicTemplateElement): def _teichmuller_set_unsafe(self): """ - Sets this element to the Teichmuller representative with the + Set this element to the Teichmuller representative with the same residue. .. WARNING:: @@ -942,7 +946,7 @@ cdef class CAElement(pAdicTemplateElement): INPUT: - - ``var`` -- string, the variable name for the polynomial + - ``var`` -- string; the variable name for the polynomial EXAMPLES:: @@ -992,7 +996,7 @@ cdef class CAElement(pAdicTemplateElement): cpdef pAdicTemplateElement unit_part(CAElement self): r""" - Returns the unit part of this element. + Return the unit part of this element. EXAMPLES:: @@ -1012,7 +1016,7 @@ cdef class CAElement(pAdicTemplateElement): cdef long valuation_c(self): """ - Returns the valuation of this element. + Return the valuation of this element. TESTS:: @@ -1039,8 +1043,8 @@ cdef class CAElement(pAdicTemplateElement): return cvaluation(self.value, self.absprec, self.prime_pow) cpdef val_unit(self): - """ - Returns a 2-tuple, the first element set to the valuation of this + r""" + Return a 2-tuple, the first element set to the valuation of this element, and the second to the unit part of this element. For a zero element, the unit part is ``O(p^0)``. @@ -1213,7 +1217,7 @@ cdef class pAdicCoercion_ZZ_CA(RingHomomorphism): def section(self): """ - Returns a map back to the ring of integers that approximates an element + Return a map back to the ring of integers that approximates an element by an integer. EXAMPLES:: @@ -1517,7 +1521,7 @@ cdef class pAdicCoercion_CA_frac_field(RingHomomorphism): def section(self): """ - Returns a map back to the ring that converts elements of + Return a map back to the ring that converts elements of non-negative valuation. EXAMPLES:: @@ -1627,8 +1631,8 @@ cdef class pAdicCoercion_CA_frac_field(RingHomomorphism): cdef class pAdicConvert_CA_frac_field(Morphism): - """ - The section of the inclusion from `\ZZ_q`` to its fraction field. + r""" + The section of the inclusion from `\ZZ_q` to its fraction field. EXAMPLES:: @@ -1794,19 +1798,19 @@ cdef class pAdicConvert_CA_frac_field(Morphism): Morphism._update_slots(self, _slots) def unpickle_cae_v2(cls, parent, value, absprec): - """ + r""" Unpickle capped absolute elements. INPUT: - - ``cls`` -- the class of the capped absolute element. + - ``cls`` -- the class of the capped absolute element - - ``parent`` -- the parent, a `p`-adic ring + - ``parent`` -- a `p`-adic ring - ``value`` -- a Python object wrapping a celement, of the kind - accepted by the cunpickle function. + accepted by the cunpickle function - - ``absprec`` -- a Python int or Sage integer. + - ``absprec`` -- a Python int or Sage integer EXAMPLES:: @@ -1827,3 +1831,4 @@ def unpickle_cae_v2(cls, parent, value, absprec): cunpickle(ans.value, value, ans.prime_pow) ans.absprec = absprec return ans + diff --git a/src/sage/rings/padics/CR_template.pxi b/src/sage/rings/padics/CR_template.pxi index 5424be0f500..f69c7529781 100644 --- a/src/sage/rings/padics/CR_template.pxi +++ b/src/sage/rings/padics/CR_template.pxi @@ -68,7 +68,7 @@ cdef inline int assert_nonzero(CRElement x) except -1: if exactzero(x.ordp): raise ZeroDivisionError("cannot divide by zero") if x.relprec == 0: - raise PrecisionError("cannot divide by something indistinguishable from zero.") + raise PrecisionError("cannot divide by something indistinguishable from zero") cdef class CRElement(pAdicTemplateElement): cdef int _set(self, x, long val, long xprec, absprec, relprec) except -1: @@ -232,10 +232,10 @@ cdef class CRElement(pAdicTemplateElement): sage: Zp(5)(1).lift_to_precision(30) Traceback (most recent call last): ... - PrecisionError: Precision higher than allowed by the precision cap. + PrecisionError: precision higher than allowed by the precision cap """ if self.relprec > self.prime_pow.ram_prec_cap: - raise PrecisionError("Precision higher than allowed by the precision cap.") + raise PrecisionError("precision higher than allowed by the precision cap") def __copy__(self): """ @@ -1012,7 +1012,7 @@ cdef class CRElement(pAdicTemplateElement): sage: b.is_zero(6) Traceback (most recent call last): ... - PrecisionError: Not enough precision to determine if element is zero + PrecisionError: not enough precision to determine if element is zero """ if absprec is None: return self.relprec == 0 @@ -1022,13 +1022,13 @@ cdef class CRElement(pAdicTemplateElement): return False if isinstance(absprec, int): if self.relprec == 0 and absprec > self.ordp: - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") return self.ordp >= absprec if not isinstance(absprec, Integer): absprec = Integer(absprec) if self.relprec == 0: if mpz_cmp_si((absprec).value, self.ordp) > 0: - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") else: return True return mpz_cmp_si((absprec).value, self.ordp) <= 0 @@ -1071,7 +1071,7 @@ cdef class CRElement(pAdicTemplateElement): sage: a.is_equal_to(aa, 15) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: a.is_equal_to(a, 50000) True @@ -1081,26 +1081,26 @@ cdef class CRElement(pAdicTemplateElement): sage: a.is_equal_to(b, 5) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: b.is_equal_to(b, 5) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: b.is_equal_to(bb, 3) True sage: b.is_equal_to(bb, 4) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: c.is_equal_to(b, 2), c.is_equal_to(b, 3) (True, False) sage: c.is_equal_to(b, 4) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: c.is_equal_to(cc, 2), c.is_equal_to(cc, 4), c.is_equal_to(cc, 5) (True, True, False) @@ -1112,28 +1112,28 @@ cdef class CRElement(pAdicTemplateElement): sage: aa.is_equal_to(a, 15) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: b.is_equal_to(a), b.is_equal_to(a, 2) (True, True) sage: b.is_equal_to(a, 5) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: bb.is_equal_to(b, 3) True sage: bb.is_equal_to(b, 4) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: b.is_equal_to(c, 2), b.is_equal_to(c, 3) (True, False) sage: b.is_equal_to(c, 4) Traceback (most recent call last): ... - PrecisionError: Elements not known to enough precision + PrecisionError: elements not known to enough precision sage: cc.is_equal_to(c, 2), cc.is_equal_to(c, 4), cc.is_equal_to(c, 5) (True, True, False) @@ -1147,7 +1147,7 @@ cdef class CRElement(pAdicTemplateElement): if exactzero(self.ordp) and exactzero(right.ordp): return True elif absprec is infinity: - raise PrecisionError("Elements not known to enough precision") + raise PrecisionError("elements not known to enough precision") if absprec is None: aprec = min(self.ordp + self.relprec, right.ordp + right.relprec) else: @@ -1158,10 +1158,10 @@ cdef class CRElement(pAdicTemplateElement): exactzero(self.ordp) and exactzero(right.ordp): return True else: - raise PrecisionError("Elements not known to enough precision") + raise PrecisionError("elements not known to enough precision") aprec = mpz_get_si((absprec).value) if aprec > self.ordp + self.relprec or aprec > right.ordp + right.relprec: - raise PrecisionError("Elements not known to enough precision") + raise PrecisionError("elements not known to enough precision") if self.ordp >= aprec and right.ordp >= aprec: return True elif self.ordp != right.ordp: @@ -1213,7 +1213,7 @@ cdef class CRElement(pAdicTemplateElement): sage: c.lift_to_precision(40) Traceback (most recent call last): ... - PrecisionError: Precision higher than allowed by the precision cap. + PrecisionError: precision higher than allowed by the precision cap """ cpdef CRElement ans if absprec == maxordp: @@ -1285,7 +1285,7 @@ cdef class CRElement(pAdicTemplateElement): if self.ordp > 0: self._set_exact_zero() elif self.ordp < 0: - raise ValueError("cannot set negative valuation element to Teichmuller representative.") + raise ValueError("cannot set negative valuation element to Teichmuller representative") elif self.relprec == 0: raise ValueError("not enough precision") else: @@ -1503,7 +1503,7 @@ cdef class CRElement(pAdicTemplateElement): """ # Since we keep this element normalized there's not much to do here. if p is not None and p != self.parent().prime(): - raise ValueError('Ring (%s) residue field of the wrong characteristic.'%self.parent()) + raise ValueError('ring (%s) residue field of the wrong characteristic'%self.parent()) if exactzero((self).ordp): raise ValueError("unit part of 0 not defined") cdef Integer val = Integer.__new__(Integer) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx index 16a9e0d4ff7..225c52efa83 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -311,13 +311,13 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): self._set_from_mpz_both(xlift.value, aprec, rprec) return if parent.prime() != x.parent().prime(): - raise TypeError("Cannot coerce between p-adic parents with different primes.") + raise TypeError("cannot coerce between p-adic parents with different primes") if isinstance(x, pari_gen) or isinstance(x, GpElement): if isinstance(x, GpElement): x = x.__pari__() if x.type() == "t_PADIC": if x.variable() != self.prime_pow.prime: - raise TypeError("Cannot coerce a pari p-adic with the wrong prime.") + raise TypeError("cannot coerce a pari p-adic with the wrong prime") ltmp = x.padicprec(self.prime_pow.prime) * self.prime_pow.e if ltmp < aprec: aprec = ltmp @@ -422,7 +422,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): aprec = rprec + __x.ordp self._set(&(poly).x, aprec) else: - raise NotImplementedError("Conversion from different p-adic extensions not yet supported") + raise NotImplementedError("conversion from different p-adic extensions not yet supported") else: try: x = list(x) @@ -1267,7 +1267,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): mpz_clear(tmp) return ans elif isinstance(_right, Rational) or (isinstance(_right, pAdicGenericElement) and _right._is_base_elt(self.prime_pow.prime)): - raise ValueError("Need more precision") + raise ValueError("need more precision") else: raise TypeError("exponent must be an integer, rational or base p-adic with the same prime") if isinstance(_right, (int, long)): @@ -1564,14 +1564,14 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): sage: ZZ(w) Traceback (most recent call last): ... - ValueError: This element not well approximated by an integer. + ValueError: this element not well approximated by an integer sage: ZZ(W(5)) 5 """ cdef Integer ans cdef ZZ_c tmp_z if ZZ_pX_deg(self.value) > 0: - raise ValueError("This element not well approximated by an integer.") + raise ValueError("this element not well approximated by an integer") ans = PY_NEW(Integer) tmp_z = ZZ_p_rep(ZZ_pX_ConstTerm(self.value)) ZZ_to_mpz(ans.value, &tmp_z) @@ -1598,14 +1598,15 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): ans.value = self.value return ans - def is_zero(self, absprec = None): - """ - Returns whether the valuation of ``self`` is at least ``absprec``. If - ``absprec`` is ``None``, returns if ``self`` is indistinguishable from - zero. + def is_zero(self, absprec=None): + r""" + Return whether the valuation of ``self`` is at least ``absprec``. + + If ``absprec`` is ``None``, returns if ``self`` is indistinguishable + from zero. If ``self`` is an inexact zero of valuation less than ``absprec``, - raises a PrecisionError. + raises a ``PrecisionError``. EXAMPLES:: @@ -1636,20 +1637,20 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): if mpz_sgn((absprec).value) < 0: ans = True elif ZZ_pX_IsZero(self.value): - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") else: ans = False else: aprec = mpz_get_si((absprec).value) if ZZ_pX_IsZero(self.value) and aprec > self.absprec: - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") else: ans = (self.valuation_c() >= aprec) return ans cpdef ntl_ZZ_pX _ntl_rep(self): """ - Returns an ``ntl_ZZ_pX`` that holds the value of ``self``. + Return an ``ntl_ZZ_pX`` that holds the value of ``self``. EXAMPLES:: @@ -1671,7 +1672,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): cpdef _ntl_rep_abs(self): """ - Returns a pair ``(f, 0)`` where ``f = self._ntl_rep()``. + Return a pair ``(f, 0)`` where ``f = self._ntl_rep()``. EXAMPLES:: @@ -1837,11 +1838,11 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): if mpz_sgn((absprec).value) < 0: return self else: - raise PrecisionError("Precision higher than allowed by the precision cap.") + raise PrecisionError("precision higher than allowed by the precision cap") else: aprec = mpz_get_si((absprec).value) if aprec > self.prime_pow.ram_prec_cap: - raise PrecisionError("Precision higher than allowed by the precision cap.") + raise PrecisionError("precision higher than allowed by the precision cap") if aprec <= self.absprec: return self ans = self._new_c(aprec) # restores context @@ -2350,3 +2351,4 @@ def make_ZZpXCAElement(parent, value, absprec, version): return ans else: raise ValueError("unknown unpickling version") + diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 87d70edaf51..db39fab2b60 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -341,7 +341,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): if isinstance(x, pari_gen): if x.type() == "t_PADIC": if x.variable() != self.prime_pow.prime: - raise TypeError("Cannot coerce a pari p-adic with the wrong prime.") + raise TypeError("cannot coerce a pari p-adic with the wrong prime") ltmp = x.padicprec(self.prime_pow.prime) * self.prime_pow.e if absprec is infinity or ltmp < aprec: aprec = ltmp @@ -485,7 +485,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): rprec = _x.relprec self._set(&_x.unit, _x.ordp, rprec) else: - raise NotImplementedError("Conversion from different p-adic extensions not yet supported") + raise NotImplementedError("conversion from different p-adic extensions not yet supported") else: try: x = list(x) @@ -1663,7 +1663,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): if self._is_exact_zero(): return self if self.prime_pow.in_field or mpz_sgn((shift).value) > 0: - raise ValueError("Shift does not fit in long") + raise ValueError("shift does not fit in long") else: ans = self._new_c(0) ans.ordp = 0 @@ -2304,9 +2304,9 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): return ans def _integer_(self, Z=None): - """ + r""" Return an integer congruent to this element modulo - `\pi`^``self.absolute_precision()``, if possible + `\pi^{\mathrm{self.absolute_precision()}`, if possible. EXAMPLES:: @@ -2322,7 +2322,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): sage: ZZ(w) Traceback (most recent call last): ... - ValueError: This element not well approximated by an integer. + ValueError: this element not well approximated by an integer sage: ZZ(W(5)) # todo: this should be different... 381469726562505 """ @@ -2334,23 +2334,23 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): if self.ordp < 0: self._normalize() if self.ordp < 0: - raise ValueError("This element has negative valuation") + raise ValueError("this element has negative valuation") cdef ntl_ZZ_pX f = self._ntl_rep_abs()[0] if f.degree() > 0: - raise ValueError("This element not well approximated by an integer.") + raise ValueError("this element not well approximated by an integer") ans = PY_NEW(Integer) tmp_z = ZZ_p_rep(ZZ_pX_ConstTerm(f.x)) ZZ_to_mpz(ans.value, &tmp_z) return ans def is_zero(self, absprec = None): - """ + r""" Return whether the valuation of this element is at least ``absprec``. If ``absprec`` is ``None``, checks if this element is indistinguishable from zero. If this element is an inexact zero of valuation less than ``absprec``, - raises a PrecisionError. + raises a ``PrecisionError``. EXAMPLES:: @@ -2384,13 +2384,13 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): if mpz_sgn((absprec).value) < 0: ans = True elif self.relprec == 0: - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") else: ans = False else: aprec = mpz_get_si((absprec).value) if self.relprec == 0 and aprec > self.ordp: - raise PrecisionError("Not enough precision to determine if element is zero") + raise PrecisionError("not enough precision to determine if element is zero") else: ans = (self.ordp >= aprec) return ans @@ -2693,7 +2693,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): if self.relprec == 0: raise ValueError("absprec larger than maximum allowable valuation") else: - raise PrecisionError("Precision higher than allowed by the precision cap.") + raise PrecisionError("precision higher than allowed by the precision cap") else: aprec = mpz_get_si((absprec).value) if aprec <= self.ordp + self.relprec: @@ -2710,7 +2710,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): # Now we're done handling all the special cases. rprec = aprec - self.ordp if rprec > self.prime_pow.ram_prec_cap: - raise PrecisionError("Precision higher than allowed by the precision cap.") + raise PrecisionError("precision higher than allowed by the precision cap") ans = self._new_c(rprec) ans.ordp = self.ordp ZZ_pX_conv_modulus(ans.unit, self.unit, self.prime_pow.get_context_capdiv(rprec).x) @@ -3044,13 +3044,13 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): sage: K.teichmuller(K(2/5)) Traceback (most recent call last): ... - ValueError: cannot set negative valuation element to Teichmuller representative. + ValueError: cannot set negative valuation element to Teichmuller representative """ self._normalize() if self.ordp > 0: self._set_exact_zero() elif self.ordp < 0: - raise ValueError("cannot set negative valuation element to Teichmuller representative.") + raise ValueError("cannot set negative valuation element to Teichmuller representative") elif self.relprec == 0: raise ValueError("not enough precision known") else: @@ -3269,3 +3269,4 @@ def make_ZZpXCRElement(parent, unit, ordp, relprec, version): return ans else: raise ValueError("unknown unpickling version") + diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index 7a4434aee90..9c0bcff4f69 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -1068,7 +1068,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): # """ # raise NotImplementedError - def norm(self, base = None): + def norm(self, base=None): r""" Return the absolute or relative norm of this element. @@ -1108,7 +1108,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): norm_of_uniformizer = (-1)**self.parent().degree() * self.parent().defining_polynomial()[0] return self.parent().ground_ring()(self.unit_part().matrix_mod_pn().det()) * norm_of_uniformizer**self.valuation() - def trace(self, base = None): + def trace(self, base=None): r""" Return the absolute or relative trace of this element. @@ -1218,7 +1218,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): cdef ZZ_p_c _const_term(self): """ - Returns the constant term of ``self.unit``. + Return the constant term of ``self.unit``. .. NOTE:: @@ -1282,8 +1282,8 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): """ return self - def expansion(self, n = None, lift_mode = 'simple'): - """ + def expansion(self, n=None, lift_mode='simple'): + r""" Return a list giving a series representation of this element. - If ``lift_mode == 'simple' or 'smallest'``, the returned list will diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pyx b/src/sage/rings/padics/padic_capped_absolute_element.pyx index 6cdf3f0f8eb..a34e1b27d77 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pyx +++ b/src/sage/rings/padics/padic_capped_absolute_element.pyx @@ -217,11 +217,11 @@ cdef class pAdicCappedAbsoluteElement(CAElement): sage: a.residue(-1) Traceback (most recent call last): ... - ValueError: cannot reduce modulo a negative power of p. + ValueError: cannot reduce modulo a negative power of p sage: a.residue(11) Traceback (most recent call last): ... - PrecisionError: not enough precision known in order to compute residue. + PrecisionError: not enough precision known in order to compute residue sage: a.residue(5, check_prec=False) 8 @@ -236,9 +236,9 @@ cdef class pAdicCappedAbsoluteElement(CAElement): if not isinstance(absprec, Integer): absprec = Integer(absprec) if check_prec and mpz_cmp_si((absprec).value, self.absprec) > 0: - raise PrecisionError("not enough precision known in order to compute residue.") + raise PrecisionError("not enough precision known in order to compute residue") elif mpz_sgn((absprec).value) < 0: - raise ValueError("cannot reduce modulo a negative power of p.") + raise ValueError("cannot reduce modulo a negative power of p") if field is None: field = (absprec == 1) elif field and absprec != 1: @@ -370,7 +370,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): cdef pAdicCappedAbsoluteElement ans, unit if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -431,7 +431,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): cdef pAdicCappedAbsoluteElement ans if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -487,7 +487,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): cdef pAdicCappedAbsoluteElement ans if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() diff --git a/src/sage/rings/padics/padic_capped_relative_element.pyx b/src/sage/rings/padics/padic_capped_relative_element.pyx index 26fec12d26b..de5fa7248cd 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pyx +++ b/src/sage/rings/padics/padic_capped_relative_element.pyx @@ -140,11 +140,11 @@ cdef class pAdicCappedRelativeElement(CRElement): sage: R(pari(R(0,5))) O(5^5) - # todo: doctests for converting from other types of p-adic rings + .. TODO:: doctests for converting from other types of p-adic rings """ def lift(self): - """ + r""" Return an integer or rational congruent to ``self`` modulo ``self``'s precision. If a rational is returned, its denominator will equal ``p^ordp(self)``. @@ -166,7 +166,7 @@ cdef class pAdicCappedRelativeElement(CRElement): TESTS:: - sage: O(5^5).lift() #indirect doctest + sage: O(5^5).lift() # indirect doctest 0 sage: R = Qp(5); R(0).lift() 0 @@ -197,7 +197,7 @@ cdef class pAdicCappedRelativeElement(CRElement): def __pari__(self): """ - Converts this element to an equivalent pari element. + Convert this element to an equivalent pari element. EXAMPLES:: @@ -212,7 +212,7 @@ cdef class pAdicCappedRelativeElement(CRElement): cdef pari_gen _to_gen(self): """ - Converts this element to an equivalent pari element. + Convert this element to an equivalent pari element. EXAMPLES:: @@ -236,8 +236,8 @@ cdef class pAdicCappedRelativeElement(CRElement): self.prime_pow.pow_mpz_t_tmp(self.relprec), self.unit) def _integer_(self, Z=None): - """ - Returns an integer congruent to this element modulo + r""" + Return an integer congruent to this element modulo ``p^self.absolute_precision()``. EXAMPLES:: @@ -246,26 +246,28 @@ cdef class pAdicCappedRelativeElement(CRElement): 95367431640624 """ if self.ordp < 0: - raise ValueError("Cannot form an integer out of a p-adic field element with negative valuation") + raise ValueError("cannot form an integer out of a p-adic field element with negative valuation") return self.lift_c() def residue(self, absprec=1, field=None, check_prec=True): - """ - Reduces this element modulo `p^{\mathrm{absprec}}`. + r""" + Reduce this element modulo `p^{\mathrm{absprec}}`. INPUT: - ``absprec`` -- a non-negative integer (default: ``1``) - - ``field`` -- boolean (default ``None``). Whether to return an element of GF(p) or Zmod(p). + - ``field`` -- boolean (default ``None``); hether to return an element + of `\GF{p}` or `\ZZ / p\ZZ` - - ``check_prec`` -- boolean (default ``True``). Whether to raise an error if this - element has insufficient precision to determine the reduction. + - ``check_prec`` -- boolean (default ``True``); whether to raise + an error if this element has insufficient precision to determine + the reduction OUTPUT: This element reduced modulo `p^\mathrm{absprec}` as an element of - `\ZZ/p^\mathrm{absprec}\ZZ` + `\ZZ/p^\mathrm{absprec}\ZZ`. EXAMPLES:: @@ -300,7 +302,7 @@ cdef class pAdicCappedRelativeElement(CRElement): sage: b.residue() Traceback (most recent call last): ... - ValueError: element must have non-negative valuation in order to compute residue. + ValueError: element must have non-negative valuation in order to compute residue TESTS:: @@ -311,11 +313,11 @@ cdef class pAdicCappedRelativeElement(CRElement): sage: a.residue(-1) Traceback (most recent call last): ... - ValueError: cannot reduce modulo a negative power of p. + ValueError: cannot reduce modulo a negative power of p sage: a.residue(5) Traceback (most recent call last): ... - PrecisionError: not enough precision known in order to compute residue. + PrecisionError: not enough precision known in order to compute residue sage: a.residue(5, check_prec=False) 8 @@ -332,11 +334,11 @@ cdef class pAdicCappedRelativeElement(CRElement): if not isinstance(absprec, Integer): absprec = Integer(absprec) if check_prec and absprec > self.precision_absolute(): - raise PrecisionError("not enough precision known in order to compute residue.") + raise PrecisionError("not enough precision known in order to compute residue") elif absprec < 0: - raise ValueError("cannot reduce modulo a negative power of p.") + raise ValueError("cannot reduce modulo a negative power of p") if self.ordp < 0: - raise ValueError("element must have non-negative valuation in order to compute residue.") + raise ValueError("element must have non-negative valuation in order to compute residue") if field is None: field = (absprec == 1) elif field and absprec != 1: @@ -358,7 +360,7 @@ cdef class pAdicCappedRelativeElement(CRElement): def _log_binary_splitting(self, aprec, mina=0): r""" - Return ``\log(self)`` for ``self`` equal to 1 in the residue field + Return ``\log(self)`` for ``self`` equal to 1 in the residue field. This is a helper method for :meth:`log`. It uses a fast binary splitting algorithm. @@ -366,13 +368,13 @@ cdef class pAdicCappedRelativeElement(CRElement): INPUT: - ``aprec`` -- an integer, the precision to which the result is - correct. ``aprec`` must not exceed the precision cap of the ring over - which this element is defined. + correct; ``aprec`` must not exceed the precision cap of the ring over + which this element is defined - ``mina`` -- an integer (default: 0), the series will check `n` up to this valuation (and beyond) to see if they can contribute to the - series. + series - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is 1 in the residue field. If this assumption is not fulfilled @@ -386,18 +388,18 @@ cdef class pAdicCappedRelativeElement(CRElement): 2. Write - .. MATH:: + .. MATH:: - u^{p-1} = \prod_{i=1}^\infty (1 - a_i p^{(v+1)*2^i}) + u^{p-1} = \prod_{i=1}^\infty (1 - a_i p^{(v+1)*2^i}) - with `0 \leq a_i < p^{(v+1)*2^i}` and compute - `\log(1 - a_i p^{(v+1)*2^i})` using the standard Taylor expansion + with `0 \leq a_i < p^{(v+1)*2^i}` and compute + `\log(1 - a_i p^{(v+1)*2^i})` using the standard Taylor expansion - .. MATH:: + .. MATH:: - \log(1 - x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots + \log(1 - x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots - together with a binary splitting method. + together with a binary splitting method. 3. Divide the result by `p^v` @@ -422,7 +424,7 @@ cdef class pAdicCappedRelativeElement(CRElement): cdef pAdicCappedRelativeElement ans if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -436,7 +438,7 @@ cdef class pAdicCappedRelativeElement(CRElement): return ans def _exp_binary_splitting(self, aprec): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -446,7 +448,7 @@ cdef class pAdicCappedRelativeElement(CRElement): - ``aprec`` -- an integer, the precision to which to compute the exponential - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -485,7 +487,7 @@ cdef class pAdicCappedRelativeElement(CRElement): cdef Integer selfint = self.lift_c() if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -498,22 +500,22 @@ cdef class pAdicCappedRelativeElement(CRElement): return ans def _exp_newton(self, aprec, log_algorithm=None): - """ - Compute the exponential power series of this element + r""" + Compute the exponential power series of this element. This is a helper method for :meth:`exp`. INPUT: - - ``aprec`` -- an integer, the precision to which to compute the + - ``aprec`` -- an integer; the precision to which to compute the exponential - - ``log_algorithm`` (default: None) -- the algorithm used for - computing the logarithm. This attribute is passed to the log - method. See :meth:`log` for more details about the possible - algorithms. + - ``log_algorithm`` -- (default: ``None``) the algorithm used for + computing the logarithm; this attribute is passed to the :meth:`log` + method; see :meth:`log` for more details about the possible + algorithms - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -543,7 +545,7 @@ cdef class pAdicCappedRelativeElement(CRElement): cdef Integer selfint = self.lift_c() if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -574,17 +576,17 @@ def unpickle_pcre_v1(R, unit, ordp, relprec): return unpickle_cre_v2(pAdicCappedRelativeElement, R, unit, ordp, relprec) def base_p_list(Integer n, bint pos, PowComputer_class prime_pow): - """ - Returns a base-`p` list of digits of ``n``. + r""" + Return a base-`p` list of digits of ``n``. INPUT: - - ``n`` -- a positive Integer. + - ``n`` -- a positive :class:`Integer` - - ``pos`` -- a boolean. If True, then returns the standard base `p` expansion. - Otherwise, the digits lie in the range `-p/2` to `p/2`. + - ``pos`` -- a boolean; if ``True``, then returns the standard base `p` + expansion, otherwise the digits lie in the range `-p/2` to `p/2`. - - ``prime_pow`` -- A PowComputer giving the prime. + - ``prime_pow`` -- a :class:`PowComputer` giving the prime EXAMPLES:: @@ -608,3 +610,4 @@ def base_p_list(Integer n, bint pos, PowComputer_class prime_pow): cdef ExpansionIter expansion = ExpansionIter(dummy, n.exact_log(p) + 2, mode) mpz_set(expansion.curvalue, n.value) return trim_zeros(list(expansion)) + diff --git a/src/sage/rings/padics/padic_ext_element.pyx b/src/sage/rings/padics/padic_ext_element.pyx index 6bd46612385..7ea8b0ddcc6 100644 --- a/src/sage/rings/padics/padic_ext_element.pyx +++ b/src/sage/rings/padics/padic_ext_element.pyx @@ -37,11 +37,13 @@ cdef class pAdicExtElement(pAdicGenericElement): """ Sets self from a list. - The list should either be uniform in type, or all of the entries should be coercible to integers. - If any of the entries in L is a list, L will be cast to a ZZ_pEX + The list should either be uniform in type, or all of the entries + should be coercible to integers. If any of the entries in ``L`` + is a list, ``L`` will be cast to a ZZ_pEX. INPUT: - L -- a list. + + - ``L`` -- a list """ raise NotImplementedError @@ -270,7 +272,7 @@ cdef class pAdicExtElement(pAdicGenericElement): def _const_term_test(self): """ - Returns the constant term of a polynomial representing ``self``. + Return the constant term of a polynomial representing ``self``. This function is mainly for troubleshooting, and the meaning of the return value will depend on whether self is capped @@ -294,7 +296,7 @@ cdef class pAdicExtElement(pAdicGenericElement): raise NotImplementedError def _ext_p_list(self, pos): - """ + r""" Return a list of integers (in the Eisenstein case) or a list of lists of integers (in the unramified case). @@ -389,7 +391,7 @@ cdef class pAdicExtElement(pAdicGenericElement): return ans cpdef bint _is_base_elt(self, p) except -1: - """ + r""" Return ``True`` if this element is an element of Zp or Qp (rather than an extension). diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pyx b/src/sage/rings/padics/padic_fixed_mod_element.pyx index 4211651be85..4b2723b7a8a 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pyx +++ b/src/sage/rings/padics/padic_fixed_mod_element.pyx @@ -289,7 +289,7 @@ cdef class pAdicFixedModElement(FMElement): sage: a.residue(-1) Traceback (most recent call last): ... - ValueError: Cannot reduce modulo a negative power of p. + ValueError: cannot reduce modulo a negative power of p sage: a.residue(5) 8 @@ -306,7 +306,7 @@ cdef class pAdicFixedModElement(FMElement): if not isinstance(absprec, Integer): absprec = Integer(absprec) if absprec < 0: - raise ValueError("Cannot reduce modulo a negative power of p.") + raise ValueError("cannot reduce modulo a negative power of p") if field is None: field = (absprec == 1) elif field and absprec != 1: @@ -439,7 +439,7 @@ cdef class pAdicFixedModElement(FMElement): cdef pAdicFixedModElement ans if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -497,7 +497,7 @@ cdef class pAdicFixedModElement(FMElement): cdef pAdicFixedModElement ans if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -552,7 +552,7 @@ cdef class pAdicFixedModElement(FMElement): cdef pAdicFixedModElement ans if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() diff --git a/src/sage/rings/padics/padic_floating_point_element.pyx b/src/sage/rings/padics/padic_floating_point_element.pyx index 7b6cf59dabe..89132c93025 100644 --- a/src/sage/rings/padics/padic_floating_point_element.pyx +++ b/src/sage/rings/padics/padic_floating_point_element.pyx @@ -1,4 +1,4 @@ -""" +r""" `p`-Adic Floating Point Elements Elements of `p`-Adic Rings with Floating Point Precision @@ -139,7 +139,7 @@ cdef class pAdicFloatingPointElement(FPElement): """ def lift(self): - """ + r""" Return an integer or rational congruent to ``self`` modulo ``self``'s precision. If a rational is returned, its denominator will equal ``p^ordp(self)``. @@ -158,7 +158,7 @@ cdef class pAdicFloatingPointElement(FPElement): return self.lift_c() cdef lift_c(self): - """ + r""" Implementation of lift. TESTS:: @@ -192,7 +192,7 @@ cdef class pAdicFloatingPointElement(FPElement): def __pari__(self): """ - Converts this element to an equivalent pari element. + Convert this element to an equivalent pari element. EXAMPLES:: @@ -205,7 +205,7 @@ cdef class pAdicFloatingPointElement(FPElement): cdef pari_gen _to_gen(self): """ - Converts this element to an equivalent pari element. + Convert this element to an equivalent pari element. EXAMPLES:: @@ -224,8 +224,8 @@ cdef class pAdicFloatingPointElement(FPElement): self.prime_pow.pow_mpz_t_top(), self.unit) def _integer_(self, Z=None): - """ - Returns an integer congruent to this element modulo + r""" + Return an integer congruent to this element modulo ``p^self.absolute_precision()``. EXAMPLES:: @@ -234,25 +234,26 @@ cdef class pAdicFloatingPointElement(FPElement): 95367431640624 """ if self.ordp < 0: - raise ValueError("Cannot form an integer out of a p-adic field element with negative valuation") + raise ValueError("cannot form an integer out of a p-adic field element with negative valuation") return self.lift_c() def residue(self, absprec=1, field=None, check_prec=False): - """ - Reduces this element modulo `p^{\mathrm{absprec}}`. + r""" + Reduce this element modulo `p^{\mathrm{absprec}}`. INPUT: - ``absprec`` -- a non-negative integer (default: ``1``) - - ``field`` -- boolean (default ``None``). Whether to return an element of GF(p) or Zmod(p). + - ``field`` -- boolean (default ``None``); whether to return an + element of `\GF{p}` or `\ZZ / p\ZZ` - - ``check_prec`` -- boolean (default ``False``). No effect (for compatibility with other types). + - ``check_prec`` -- ignored (for compatibility with other types) OUTPUT: - This element reduced modulo `p^\mathrm{absprec}` as an element of - `\ZZ/p^\mathrm{absprec}\ZZ` + This element reduced modulo `p^{\mathrm{absprec}}` as an element of + `\ZZ/p^{\mathrm{absprec}}\ZZ`. EXAMPLES:: @@ -273,7 +274,7 @@ cdef class pAdicFloatingPointElement(FPElement): sage: b.residue() Traceback (most recent call last): ... - ValueError: element must have non-negative valuation in order to compute residue. + ValueError: element must have non-negative valuation in order to compute residue TESTS:: @@ -284,7 +285,7 @@ cdef class pAdicFloatingPointElement(FPElement): sage: a.residue(-1) Traceback (most recent call last): ... - ValueError: cannot reduce modulo a negative power of p. + ValueError: cannot reduce modulo a negative power of p sage: a.residue(5) 8 @@ -296,9 +297,9 @@ cdef class pAdicFloatingPointElement(FPElement): if not isinstance(absprec, Integer): absprec = Integer(absprec) if mpz_sgn((absprec).value) < 0: - raise ValueError("cannot reduce modulo a negative power of p.") + raise ValueError("cannot reduce modulo a negative power of p") if self.ordp < 0: - raise ValueError("element must have non-negative valuation in order to compute residue.") + raise ValueError("element must have non-negative valuation in order to compute residue") if field is None: field = (absprec == 1) elif field and absprec != 1: @@ -321,7 +322,7 @@ cdef class pAdicFloatingPointElement(FPElement): return Mod(selfvalue, modulus) def _exp_binary_splitting(self, aprec): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -331,7 +332,7 @@ cdef class pAdicFloatingPointElement(FPElement): - ``aprec`` -- an integer, the precision to which to compute the exponential - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -343,7 +344,7 @@ cdef class pAdicFloatingPointElement(FPElement): .. MATH:: - self = \sum_{i=1}^\infty a_i p^{2^i} + \mathrm{self} = \sum_{i=1}^\infty a_i p^{2^i} with `0 \leq a_i < p^{2^i}` and compute `\exp(a_i p^{2^i})` using the standard Taylor expansion @@ -370,7 +371,7 @@ cdef class pAdicFloatingPointElement(FPElement): cdef Integer selfint = self.lift_c() if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -382,22 +383,22 @@ cdef class pAdicFloatingPointElement(FPElement): return ans def _exp_newton(self, aprec, log_algorithm=None): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. INPUT: - - ``aprec`` -- an integer, the precision to which to compute the + - ``aprec`` -- an integer; the precision to which to compute the exponential - - ``log_algorithm`` (default: None) -- the algorithm used for - computing the logarithm. This attribute is passed to the log - method. See :meth:`log` for more details about the possible - algorithms. + - ``log_algorithm`` -- (default: ``None``) the algorithm used for + computing the logarithm. This attribute is passed to the :meth:`log` + method; see :meth:`log` for more details about the possible + algorithms - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -405,11 +406,11 @@ cdef class pAdicFloatingPointElement(FPElement): ALGORITHM: - Solve the equation `\log(x) = self` using the Newton scheme:: + Solve the equation `\log(x) = \mathrm{self}` using the Newton scheme:: .. MATH:: - x_{i+1} = x_i \cdot (1 + self - \log(x_i)) + x_{i+1} = x_i \cdot (1 + \mathrm{self} - \log(x_i)). The binary complexity of this algorithm is roughly the same than that of the computation of the logarithm. @@ -427,7 +428,7 @@ cdef class pAdicFloatingPointElement(FPElement): cdef Integer selfint = self.lift_c() if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: - raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + raise NotImplementedError("the prime %s does not fit in a long" % self.prime_pow.prime) p = self.prime_pow.prime ans = self._new_c() @@ -441,3 +442,4 @@ cdef class pAdicFloatingPointElement(FPElement): sig_off() return ans + diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 4c74bdb8a65..a76fba9a87e 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -1693,7 +1693,7 @@ def _call_(self, x): sage: Zmod(121).convert_map_from(Qp(11))(3/11) Traceback (most recent call last): ... - ValueError: element must have non-negative valuation in order to compute residue. + ValueError: element must have non-negative valuation in order to compute residue """ return x.residue(self._n, field=self._field, check_prec=self._field) diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 37b2393c9ce..5f8fe60e0e6 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -753,7 +753,7 @@ cdef class pAdicGenericElement(LocalGenericElement): .. MATH:: - x + \frac{x^p}{p} + \frac{x^{p^2}}{p^2} + \dots + x + \frac{x^p}{p} + \frac{x^{p^2}}{p^2} + \cdots. INPUT: @@ -777,7 +777,7 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: pi.artin_hasse_exp(algorithm='direct') # indirect doctest Traceback (most recent call last): ... - NotImplementedError: One factor of the Artin-Hasse exponential does not converge + NotImplementedError: one factor of the Artin-Hasse exponential does not converge There is however an important exception. When we are working over `\ZZ_2` or `\QQ_2` and `x` is congruent to `2` @@ -789,8 +789,8 @@ cdef class pAdicGenericElement(LocalGenericElement): AH(x) = - \exp(x + \frac{x^2}{2} + \frac{x^4}{4} + \dots) - with a negative sign. - This method knows about this fact and handles the computation correctly:: + with a negative sign. This method knows about this fact and handles + the computation correctly:: sage: W = Zp(2,8) sage: x = W(1234); x @@ -832,7 +832,7 @@ cdef class pAdicGenericElement(LocalGenericElement): denom *= p s = pow/denom if s.valuation() <= ep: - raise NotImplementedError("One factor of the Artin-Hasse exponential does not converge") + raise NotImplementedError("one factor of the Artin-Hasse exponential does not converge") arg += s AH = arg.exp(algorithm=exp_algorithm) return AH @@ -2520,17 +2520,17 @@ cdef class pAdicGenericElement(LocalGenericElement): - ``p_branch`` -- an element in the base ring or its fraction field; the implementation will choose the branch of the - logarithm which sends `p` to ``branch``. + logarithm which sends `p` to ``branch`` - ``pi_branch`` -- an element in the base ring or its fraction field; the implementation will choose the branch of the - logarithm which sends the uniformizer to ``branch``. You + logarithm which sends the uniformizer to ``branch``; you may specify at most one of ``p_branch`` and ``pi_branch``, - and must specify one of them if this element is not a unit. + and must specify one of them if this element is not a unit - - ``aprec`` -- an integer or ``None`` (default: ``None``) if not + - ``aprec`` -- an integer or ``None`` (default: ``None``); if not ``None``, then the result will only be correct to precision - ``aprec``. + ``aprec`` - ``change_frac`` -- In general the codomain of the logarithm should be in the `p`-adic field, however, for most neighborhoods of 1, it lies @@ -2544,7 +2544,7 @@ cdef class pAdicGenericElement(LocalGenericElement): .. MATH:: - \log(1-x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots + \log(1-x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots. Its binary complexity is quadratic with respect to the precision. @@ -2583,10 +2583,10 @@ cdef class pAdicGenericElement(LocalGenericElement): 13 + 6*13^2 + 2*13^3 + 5*13^4 + 10*13^6 + 13^7 + 11*13^8 + 8*13^9 + O(13^10) Note that the relative precision decreases when we take log. - Precisely the absolute precision on ``\log(a)`` agrees with the relative - precision on ``a`` thanks to the relation ``d\log(a) = da/a``. + Precisely the absolute precision on ``log(a)`` agrees with the relative + precision on ``a`` thanks to the relation `d\log(a) = da/a`. - The call `log(a)` works as well:: + The call ``log(a)`` works as well:: sage: log(a) 13 + 6*13^2 + 2*13^3 + 5*13^4 + 10*13^6 + 13^7 + 11*13^8 + 8*13^9 + O(13^10) @@ -2607,7 +2607,7 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: b.log() Traceback (most recent call last): ... - ValueError: You must specify a branch of the logarithm for non-units + ValueError: you must specify a branch of the logarithm for non-units sage: b.log(p_branch=4) 4 + O(5^10) sage: c = R(10) @@ -2632,7 +2632,8 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: z = 1 + w^2 + 4*w^7; z 1 + w^2 + 4*w^7 + O(w^20) sage: z.log() - w^2 + 2*w^4 + 3*w^6 + 4*w^7 + w^9 + 4*w^10 + 4*w^11 + 4*w^12 + 3*w^14 + w^15 + w^17 + 3*w^18 + 3*w^19 + O(w^20) + w^2 + 2*w^4 + 3*w^6 + 4*w^7 + w^9 + 4*w^10 + 4*w^11 + 4*w^12 + + 3*w^14 + w^15 + w^17 + 3*w^18 + 3*w^19 + O(w^20) In an extension, there will usually be a difference between specifying ``p_branch`` and ``pi_branch``:: @@ -2641,7 +2642,7 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: b.log() Traceback (most recent call last): ... - ValueError: You must specify a branch of the logarithm for non-units + ValueError: you must specify a branch of the logarithm for non-units sage: b.log(p_branch=0) O(w^20) sage: b.log(p_branch=w) @@ -2862,7 +2863,7 @@ cdef class pAdicGenericElement(LocalGenericElement): if self.is_zero(): raise ValueError('logarithm is not defined at zero') if p_branch is not None and pi_branch is not None: - raise ValueError("You may only specify a branch of the logarithm in one way") + raise ValueError("you may only specify a branch of the logarithm in one way") R = self.parent() p = R.prime() q = p**R.absolute_f() @@ -2873,7 +2874,7 @@ cdef class pAdicGenericElement(LocalGenericElement): else: if pi_branch is None: if p_branch is None: - raise ValueError("You must specify a branch of the logarithm for non-units") + raise ValueError("you must specify a branch of the logarithm for non-units") pi_branch = (p_branch - R._log_unit_part_p()) / e # Be careful: in ramified extensions, R._log_unit_part_p() is theoretically known at higher precision than the cap # In some cases, this may result in a loss of precision on pi_branch, and then on the final result @@ -2930,7 +2931,7 @@ cdef class pAdicGenericElement(LocalGenericElement): elif algorithm == "binary_splitting": log_unit = y._log_binary_splitting(aprec, minn) else: - raise ValueError("Algorithm must be either 'generic', 'binary_splitting' or None") + raise ValueError("algorithm must be either 'generic', 'binary_splitting' or None") retval = total + log_unit*R(denom).inverse_of_unit() if not change_frac: @@ -3066,7 +3067,7 @@ cdef class pAdicGenericElement(LocalGenericElement): 1 + 7 + 4*7^2 + 2*7^3 + O(7^5) """ - raise NotImplementedError("The binary splitting algorithm is not implemented for the parent: %s" % self.parent()) + raise NotImplementedError("the binary splitting algorithm is not implemented for the parent: %s" % self.parent()) def _exp_newton(self, aprec, log_algorithm=None): """ @@ -3129,7 +3130,7 @@ cdef class pAdicGenericElement(LocalGenericElement): return a - def exp(self, aprec = None, algorithm=None): + def exp(self, aprec=None, algorithm=None): r""" Compute the `p`-adic exponential of this element if the exponential series converges. @@ -3137,25 +3138,26 @@ cdef class pAdicGenericElement(LocalGenericElement): INPUT: - ``aprec`` -- an integer or ``None`` (default: ``None``); if - specified, computes only up to the indicated precision. + specified, computes only up to the indicated precision - ``algorithm`` -- ``generic``, ``binary_splitting``, ``newton`` or ``None`` (default) + The generic algorithm evaluates naively the series defining the exponential, namely .. MATH:: - \exp(x) = 1 + x + x^2/2 + x^3/6 + x^4/24 + \cdots + \exp(x) = 1 + x + x^2/2 + x^3/6 + x^4/24 + \cdots. Its binary complexity is quadratic with respect to the precision. The binary splitting algorithm is faster, it has a quasi-linear complexity. - The ``Newton`` algorithms solve the equation `\log(x) = self` using - a Newton scheme. It runs roughly as fast as the computation of the - logarithm. + The ``Newton`` algorithms solve the equation `\log(x) =` ``self`` + using a Newton scheme. It runs roughly as fast as the computation + of the logarithm. By default, we use the binary splitting if it is available. If it is not, we use the Newton algorithm if a fast algorithm for @@ -3349,7 +3351,7 @@ cdef class pAdicGenericElement(LocalGenericElement): elif algorithm == 'newton': ans = self._exp_newton(aprec) else: - raise ValueError("Algorithm must be 'generic', 'binary_splitting', 'newton' or None") + raise ValueError("algorithm must be 'generic', 'binary_splitting', 'newton' or None") return ans.add_bigoh(aprec) @@ -3606,7 +3608,7 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: R(5).nth_root(11) Traceback (most recent call last): ... - ValueError: This element is not a nth power + ValueError: this element is not a nth power Similarly, when precision on the input is too small, an error is raised:: @@ -3616,7 +3618,7 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: x.nth_root(5) Traceback (most recent call last): ... - PrecisionError: Not enough precision to be sure that this element is a nth power + PrecisionError: not enough precision to be sure that this element is a nth power Check that :trac:`30314` is fixed:: @@ -3706,7 +3708,7 @@ cdef class pAdicGenericElement(LocalGenericElement): if self._is_exact_zero(): return self if self.is_zero(): - raise PrecisionError("Not enough precision to be sure that this element is a nth power") + raise PrecisionError("not enough precision to be sure that this element is a nth power") v = n.valuation(p) m = n // (p**v) @@ -3714,14 +3716,14 @@ cdef class pAdicGenericElement(LocalGenericElement): # We check the valuation val = self.valuation() if val % n != 0: - raise ValueError("This element is not a nth power") + raise ValueError("this element is not a nth power") # and the residue a = K(self) >> val abar = a.residue() try: xbar = abar.nth_root(m) except ValueError: - raise ValueError("This element is not a nth power") + raise ValueError("this element is not a nth power") # We take the inverse mth root at small precision prec = a.precision_absolute() @@ -3749,11 +3751,11 @@ cdef class pAdicGenericElement(LocalGenericElement): else: root, accuracy = root._inverse_pth_root() if accuracy is not infinity and accuracy is not None: - raise ValueError("This element is not a nth power") + raise ValueError("this element is not a nth power") # We check the precision if v > 0 and prec < minprec: - raise PrecisionError("Not enough precision to be sure that this element is a nth power") + raise PrecisionError("not enough precision to be sure that this element is a nth power") # We lift the root using Newton iteration if v % 2 == parity: @@ -3840,7 +3842,7 @@ cdef class pAdicGenericElement(LocalGenericElement): else: invroottwist, accuracy = hint if accuracy is None: - raise NotImplementedError("Try to increase the precision cap of the parent...") + raise NotImplementedError("try to increase the precision cap of the parent...") a = self prec = a.precision_absolute() @@ -4124,29 +4126,26 @@ cdef class pAdicGenericElement(LocalGenericElement): verbose(H, level=3) return (H[n](z - 1) - ((z.log(p_branch))**(n-1)*(1 - z).log(p_branch))/Integer(n-1).factorial()).add_bigoh(N) - def polylog(self, n, p_branch = 0): - """ - Return `Li_n(self)`, the `n`th `p`-adic polylogarithm of this element. + def polylog(self, n, p_branch=0): + r""" + Return `Li_n(\mathrm{self})`, the `n`-th `p`-adic polylogarithm + of this element. INPUT: - ``n`` -- a non-negative integer - ``p_branch`` -- an element in the base ring or its fraction field; the implementation will choose the branch of the - logarithm which sends `p` to ``branch``. - - OUTPUT: - - - `Li_n(`self`)` + logarithm which sends `p` to ``branch`` EXAMPLES: - The `n`-th polylogarithm of `-1` is `0` for even `n` :: + The `n`-th polylogarithm of `-1` is `0` for even `n`:: sage: Qp(13)(-1).polylog(6) == 0 True - We can check some identities, for example those mentioned in [DCW2016]_ :: + We can check some identities, for example those mentioned in [DCW2016]_:: sage: x = Qp(7, prec=30)(1/3) sage: (x^2).polylog(4) - 8*x.polylog(4) - 8*(-x).polylog(4) == 0 @@ -4164,36 +4163,36 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: x.polylog(2) + (1-x).polylog(2) + x.log(0)**2*(1-x).log(0) == 0 True - `Li_1(z) = -\log(1-z)` for `|z| < 1` :: + `Li_1(z) = -\log(1-z)` for `|z| < 1`:: sage: Qp(5)(10).polylog(1) == -Qp(5)(1-10).log(0) True - The dilogarithm of 1 is zero :: + The dilogarithm of 1 is zero:: sage: Qp(5)(1).polylog(2) O(5^20) - The cubing relation holds for the trilogarithm at 1 :: + The cubing relation holds for the trilogarithm at 1:: sage: K = Qp(7) sage: z = K.zeta(3) sage: -8*K(1).polylog(3) == 9*(K(z).polylog(3) + K(z^2).polylog(3)) True - The polylogarithm of 0 is 0 :: + The polylogarithm of 0 is 0:: sage: Qp(11)(0).polylog(7) 0 - Only polylogarithms for positive `n` are defined :: + Only polylogarithms for positive `n` are defined:: sage: Qp(11)(2).polylog(-1) Traceback (most recent call last): ... - ValueError: Polylogarithm only implemented for n at least 0. + ValueError: polylogarithm only implemented for n at least 0 - Check that :trac:`29222` is fixed :: + Check that :trac:`29222` is fixed:: sage: K = Qp(7) sage: print(K(1 + 7^11).polylog(4)) @@ -4210,10 +4209,8 @@ cdef class pAdicGenericElement(LocalGenericElement): .. TODO:: - - Implement for extensions - - Use the change method to create K from self.parent() - - + - Implement for extensions. + - Use the change method to create K from ``self.parent()``. """ from sage.rings.power_series_ring import PowerSeriesRing from sage.rings.padics.factory import Qp @@ -4222,14 +4219,14 @@ cdef class pAdicGenericElement(LocalGenericElement): from sage.rings.infinity import PlusInfinity if self.parent().absolute_degree() != 1: - raise NotImplementedError("Polylogarithms are not currently implemented for elements of extensions") + raise NotImplementedError("polylogarithms are not currently implemented for elements of extensions") # TODO implement this (possibly after the change method for padic generic elements is added). if n == 0: return self/(1-self) if n == 1: return -(1-self).log(p_branch) if n < 0: - raise ValueError('Polylogarithm only implemented for n at least 0.') + raise ValueError('polylogarithm only implemented for n at least 0') prec = self.precision_absolute() diff --git a/src/sage/rings/padics/padic_lattice_element.py b/src/sage/rings/padics/padic_lattice_element.py index 4714886437b..a4e44d12add 100644 --- a/src/sage/rings/padics/padic_lattice_element.py +++ b/src/sage/rings/padics/padic_lattice_element.py @@ -291,11 +291,11 @@ def residue(self, absprec=1, field=None, check_prec=True): sage: a.residue(-1) Traceback (most recent call last): ... - ValueError: cannot reduce modulo a negative power of p. + ValueError: cannot reduce modulo a negative power of p sage: a.residue(5) Traceback (most recent call last): ... - PrecisionError: not enough precision known in order to compute residue. + PrecisionError: not enough precision known in order to compute residue sage: a.residue(5, check_prec=False) 8 @@ -305,11 +305,11 @@ def residue(self, absprec=1, field=None, check_prec=True): if not isinstance(absprec, Integer): absprec = Integer(absprec) if check_prec and absprec > self.precision_absolute(): - raise PrecisionError("not enough precision known in order to compute residue.") + raise PrecisionError("not enough precision known in order to compute residue") elif absprec < 0: - raise ValueError("cannot reduce modulo a negative power of p.") + raise ValueError("cannot reduce modulo a negative power of p") if self.valuation() < 0: - raise ValueError("element must have non-negative valuation in order to compute residue.") + raise ValueError("element must have non-negative valuation in order to compute residue") if field is None: field = (absprec == 1) elif field and absprec != 1: @@ -429,14 +429,14 @@ def valuation(self, secure=False): sage: y.valuation(secure=True) Traceback (most recent call last): ... - PrecisionError: Not enough precision + PrecisionError: not enough precision """ val = self._value.valuation() prec = self.precision_absolute() if val < prec: return val elif secure: - raise PrecisionError("Not enough precision") + raise PrecisionError("not enough precision") else: return prec @@ -467,7 +467,7 @@ def precision_relative(self, secure=False): sage: y.precision_relative(secure=True) Traceback (most recent call last): ... - PrecisionError: Not enough precision + PrecisionError: not enough precision """ if not secure and self.is_zero(): return ZZ(0) @@ -1017,7 +1017,7 @@ def unit_part(self): sage: c.unit_part() Traceback (most recent call last): ... - PrecisionError: Not enough precision + PrecisionError: not enough precision """ v = self.valuation(secure=True) return self >> v @@ -1046,7 +1046,7 @@ def val_unit(self): sage: c.val_unit() Traceback (most recent call last): ... - PrecisionError: Not enough precision + PrecisionError: not enough precision """ v = self.valuation(secure=True) return v, self >> v @@ -1193,7 +1193,7 @@ def expansion(self, n=None, lift_mode='simple', start_val=None): [4, 1, 4, 4, 1, 0, 0, 0, 0, 0] """ if lift_mode != 'simple': - raise NotImplementedError("Other modes than 'simple' are not implemented yet") + raise NotImplementedError("other modes than 'simple' are not implemented yet") prec = self.precision_absolute() val = self.valuation() expansion = self._value.list(prec) @@ -1203,7 +1203,7 @@ def expansion(self, n=None, lift_mode='simple', start_val=None): try: return expansion[n-val] except KeyError: - raise PrecisionError("The digit in position %s is not determined" % n) + raise PrecisionError("the digit in position %s is not determined" % n) if start_val is None: if self._parent.is_field(): start_val = val diff --git a/src/sage/rings/padics/padic_template_element.pxi b/src/sage/rings/padics/padic_template_element.pxi index 31e89a824b3..f4629a5d598 100644 --- a/src/sage/rings/padics/padic_template_element.pxi +++ b/src/sage/rings/padics/padic_template_element.pxi @@ -138,7 +138,7 @@ cdef class pAdicTemplateElement(pAdicGenericElement): elif sage.rings.finite_rings.element_base.is_FiniteFieldElement(x): k = self.parent().residue_field() if not k.has_coerce_map_from(x.parent()): - raise NotImplementedError("conversion from finite fields which do not embed into the residue field not implemented.") + raise NotImplementedError("conversion from finite fields which do not embed into the residue field not implemented") x = k(x) if not k.is_prime_field(): @@ -313,16 +313,16 @@ cdef class pAdicTemplateElement(pAdicGenericElement): raise NotImplementedError def lift_to_precision(self, absprec=None): - """ - Returns another element of the same parent with absolute precision at + r""" + Return another element of the same parent with absolute precision at least ``absprec``, congruent to this `p`-adic element modulo the precision of this element. INPUT: - - ``absprec`` -- an integer or ``None`` (default: ``None``), the + - ``absprec`` -- an integer or ``None`` (default: ``None``); the absolute precision of the result. If ``None``, lifts to the maximum - precision allowed. + precision allowed .. NOTE:: @@ -340,7 +340,7 @@ cdef class pAdicTemplateElement(pAdicGenericElement): sage: R(1,15).lift_to_precision(30) Traceback (most recent call last): ... - PrecisionError: Precision higher than allowed by the precision cap. + PrecisionError: precision higher than allowed by the precision cap sage: R(-1,2).lift_to_precision().precision_absolute() == R.precision_cap() True @@ -362,14 +362,14 @@ cdef class pAdicTemplateElement(pAdicGenericElement): if not isinstance(absprec, Integer): absprec = Integer(absprec) if mpz_fits_slong_p((absprec).value) == 0: - raise PrecisionError("Precision higher than allowed by the precision cap") + raise PrecisionError("precision higher than allowed by the precision cap") ans = self.lift_to_precision_c(mpz_get_si((absprec).value)) ans.check_preccap() return ans cdef pAdicTemplateElement lift_to_precision_c(self, long absprec): """ - Lifts this element to another with precision at least absprec. + Lift this element to another with precision at least ``absprec``. """ raise NotImplementedError @@ -384,7 +384,7 @@ cdef class pAdicTemplateElement(pAdicGenericElement): .. MATH:: - \pi^v \cdot \sum_{i=0}^\infty a_i \pi^i + \pi^v \cdot \sum_{i=0}^\infty a_i \pi^i, where `v` is the valuation of this element when the parent is a field, and `v = 0` otherwise. @@ -693,15 +693,15 @@ cdef class pAdicTemplateElement(pAdicGenericElement): sage: a.residue(2) Traceback (most recent call last): ... - NotImplementedError: reduction modulo p^n with n>1. + NotImplementedError: reduction modulo p^n with n>1 sage: a.residue(10) Traceback (most recent call last): ... - PrecisionError: insufficient precision to reduce modulo p^10. + PrecisionError: insufficient precision to reduce modulo p^10 sage: a.residue(10, check_prec=False) Traceback (most recent call last): ... - NotImplementedError: reduction modulo p^n with n>1. + NotImplementedError: reduction modulo p^n with n>1 sage: R. = ZqCA(27, 4) sage: (3 + 3*a).residue() @@ -717,17 +717,17 @@ cdef class pAdicTemplateElement(pAdicGenericElement): sage: (a/3).residue() Traceback (most recent call last): ... - ValueError: element must have non-negative valuation in order to compute residue. + ValueError: element must have non-negative valuation in order to compute residue """ if absprec < 0: - raise ValueError("cannot reduce modulo a negative power of the uniformizer.") + raise ValueError("cannot reduce modulo a negative power of the uniformizer") if self.valuation() < 0: - raise ValueError("element must have non-negative valuation in order to compute residue.") + raise ValueError("element must have non-negative valuation in order to compute residue") R = self.parent() if check_prec and (R.is_fixed_mod() or R.is_floating_point()): check_prec = False if check_prec and absprec > self.precision_absolute(): - raise PrecisionError("insufficient precision to reduce modulo p^%s."%absprec) + raise PrecisionError("insufficient precision to reduce modulo p^%s"%absprec) if field and absprec != 1: raise ValueError("field keyword may only be set at precision 1") if absprec == 0: @@ -739,14 +739,14 @@ cdef class pAdicTemplateElement(pAdicGenericElement): return parent.zero() return parent(self.expansion(0)) else: - raise NotImplementedError("reduction modulo p^n with n>1.") + raise NotImplementedError("reduction modulo p^n with n>1") cdef Integer exact_pow_helper(long *ansrelprec, long relprec, _right, PowComputer_ prime_pow): """ - This function is used by exponentiation in both CR_template.pxi - and CA_template.pxi to determine the extra precision gained from - an exponent of positive valuation. See __pow__ there and in - padic_ZZ_pX_CR_element.pyx for more details on this phenomenon. + This function is used by exponentiation in both ``CR_template.pxi`` + and ``CA_template.pxi`` to determine the extra precision gained from + an exponent of positive valuation. See ``__pow__`` there and in + ``padic_ZZ_pX_CR_element.pyx`` for more details on this phenomenon. INPUT: @@ -874,7 +874,8 @@ cdef class ExpansionIter(object): """ An iterator over a `p`-adic expansion. - This class should not be instantiated directly, but instead using :meth:`expansion`. + This class should not be instantiated directly, but instead + using :meth:`expansion`. INPUT: @@ -951,7 +952,7 @@ cdef class ExpansionIter(object): def __len__(self): """ - Returns the number of terms that will be emitted. + Return the number of terms that will be emitted. TESTS:: @@ -966,7 +967,7 @@ cdef class ExpansionIter(object): def __next__(self): """ - Provides the next coefficient in the `p`-adic expansion. + Provide the next coefficient in the `p`-adic expansion. EXAMPLES:: @@ -999,7 +1000,7 @@ cdef class ExpansionIter(object): return cexpansion_next(self.curvalue, self.mode, self.curpower, pp) cdef class ExpansionIterable(object): - """ + r""" An iterable storing a `p`-adic expansion of an element. This class should not be instantiated directly, but instead using :meth:`expansion`. @@ -1010,7 +1011,11 @@ cdef class ExpansionIterable(object): - ``prec`` -- the number of terms to be emitted - ``val_shift`` -- how many zeros to add at the beginning of the expansion, or the number of initial terms to truncate (if negative) - - ``mode`` -- either ``simple_mode``, ``smallest_mode`` or ``teichmuller_mode`` + - ``mode`` -- one of the following: + + * ``'simple_mode'`` + * ``'smallest_mode'`` + * ``'teichmuller_mode'`` EXAMPLES:: @@ -1047,7 +1052,7 @@ cdef class ExpansionIterable(object): def __dealloc__(self): """ - Deallocates memory for the iteratable. + Deallocate memory for the iteratable. TESTS:: @@ -1058,7 +1063,7 @@ cdef class ExpansionIterable(object): def __iter__(self): """ - Returns an iterator, based on a corresponding :class:`ExpansionIter`. + Return an iterator, based on a corresponding :class:`ExpansionIter`. If ``val_shift`` is positive, will first emit that many zeros (of the appropriate type: ``[]`` instead when the inertia degree @@ -1089,7 +1094,7 @@ cdef class ExpansionIterable(object): def __len__(self): """ - Returns the number of terms that will be emitted. + Return the number of terms that will be emitted. TESTS:: @@ -1106,7 +1111,7 @@ cdef class ExpansionIterable(object): def __getitem__(self, n): """ - Return the ``n``th entry in the expansion. + Return the ``n``-th entry in the expansion. Negative indices are not allowed. @@ -1122,7 +1127,7 @@ cdef class ExpansionIterable(object): sage: a = E[-1] Traceback (most recent call last): ... - ValueError: Negative indices not supported + ValueError: negative indices not supported sage: Zp(5,4)(373).expansion(lift_mode='smallest')[3] -2 """ @@ -1134,7 +1139,7 @@ cdef class ExpansionIterable(object): cdef long m = n - self.val_shift cdef celement value if n < 0: - raise ValueError("Negative indices not supported") + raise ValueError("negative indices not supported") elif m < 0: return _zero(self.mode, self.teich_ring) elif m >= self.prec: @@ -1169,3 +1174,4 @@ cdef class ExpansionIterable(object): modestr = " (teichmuller)" p = self.elt.prime_pow.prime return "%s-adic expansion of %s%s"%(p, self.elt, modestr) + diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index ec4ff3009dc..c2fa8031086 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -609,9 +609,9 @@ cdef PowComputer_base PowComputer_c(Integer m, Integer cache_limit, Integer prec 81 """ if cache_limit < 0: - raise ValueError("cache_limit must be non-negative.") + raise ValueError("cache_limit must be non-negative") if prec_cap < 0: - raise ValueError("prec_cap must be non-negative.") + raise ValueError("prec_cap must be non-negative") if mpz_cmp_si((prec_cap).value, maxpreccap) >= 0: raise ValueError("cannot create p-adic parents with precision cap larger than (1 << (sizeof(long)*8 - 2))") From 2b2e7bf5865a369c352559ff0dfcf5985dcb2d83 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Jul 2021 11:02:16 +1000 Subject: [PATCH 216/336] Fixing two other doctest failures in other files from error message changes. --- src/sage/rings/polynomial/polynomial_rational_flint.pyx | 2 +- src/sage/rings/tate_algebra_element.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 55cfd8a4819..aba4482bd3b 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2328,7 +2328,7 @@ cdef class Polynomial_rational_flint(Polynomial): sage: f.factor_padic(3,-1) Traceback (most recent call last): ... - ValueError: prec_cap must be non-negative. + ValueError: prec_cap must be non-negative sage: f.factor_padic(6,10) Traceback (most recent call last): ... diff --git a/src/sage/rings/tate_algebra_element.pyx b/src/sage/rings/tate_algebra_element.pyx index 495a4b080c7..ee3e01b5d9d 100644 --- a/src/sage/rings/tate_algebra_element.pyx +++ b/src/sage/rings/tate_algebra_element.pyx @@ -3140,7 +3140,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): sage: g.residue() Traceback (most recent call last): ... - ValueError: element must have non-negative valuation in order to compute residue. + ValueError: element must have non-negative valuation in order to compute residue The residue is not implemented for series with convergence radius different from 1. From 551f2cc0e04bdb4ed22edfc1541a705c4c6d6715 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Tue, 6 Jul 2021 19:38:16 -0600 Subject: [PATCH 217/336] trac 32148 length of propositional formula --- src/sage/logic/boolformula.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/sage/logic/boolformula.py b/src/sage/logic/boolformula.py index 5bc168d8404..8af231ca111 100644 --- a/src/sage/logic/boolformula.py +++ b/src/sage/logic/boolformula.py @@ -1508,9 +1508,9 @@ def get_next_op(self, str): i += 1 return str[i] - def __len__(self): + def length(self): r""" - Return the length of a Boolean formula. + Return the length of ``self``. OUTPUT: @@ -1521,37 +1521,40 @@ def __len__(self): sage: import sage.logic.propcalc as propcalc sage: s = propcalc.formula("a") - sage: len(s) + sage: s.length() 1 sage: s = propcalc.formula("(a)") - sage: len(s) + sage: s.length() 1 sage: s = propcalc.formula("~a") - sage: len(s) + sage: s.length() 2 sage: s = propcalc.formula("a -> b") - sage: len(s) + sage: s.length() 3 sage: s = propcalc.formula("alpha -> beta") - sage: len(s) + sage: s.length() 3 sage: s = propcalc.formula("a -> a") - sage: len(s) + sage: s.length() 3 sage: s = propcalc.formula("~(a -> b)") - sage: len(s) + sage: s.length() 4 sage: s = propcalc.formula("((a&b)|(a&c))->~d") - sage: len(s) + sage: s.length() 10 TESTS:: sage: s = propcalc.formula("(((alpha) -> ((beta))))") - sage: len(s) + sage: s.length() 3 """ return len(flatten(self.full_tree())) + from sage.misc.superseded import deprecated_function_alias + __len__ = deprecated_function_alias(32148, length) + # allow is_consequence to be called as a function (not only as a method of BooleanFormula) is_consequence = BooleanFormula.is_consequence From ac561ae53e341043a75609f1f0ccec86b8983daa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Jul 2021 20:16:21 -0700 Subject: [PATCH 218/336] Chart._restrict_set: Update doctest output --- src/sage/manifolds/chart.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index b310335f3f9..d2cdfd08b51 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -883,9 +883,7 @@ def _restrict_set(self, universe, coord_restrictions): sage: X._restrict_set(universe, (x>0, [x 0 } and - Set-theoretic intersection of - { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : x < y } and - { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : y < 0 } + { (x, y) ∈ Vector space of dimension 2 over Real Field with 53 bits of precision : x < y, y < 0 } sage: X._restrict_set(universe, [(x0]) Set-theoretic intersection of Set-theoretic union of From 3a30f30e533911469e52fdeee3655dfd078c074c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Jul 2021 13:33:00 +1000 Subject: [PATCH 219/336] Fixing docstrings in ell_rational_field.py. --- .../padics/padic_capped_relative_element.pyx | 2 +- .../elliptic_curves/ell_rational_field.py | 737 +++++++++--------- 2 files changed, 363 insertions(+), 376 deletions(-) diff --git a/src/sage/rings/padics/padic_capped_relative_element.pyx b/src/sage/rings/padics/padic_capped_relative_element.pyx index de5fa7248cd..c59c8e3573b 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pyx +++ b/src/sage/rings/padics/padic_capped_relative_element.pyx @@ -257,7 +257,7 @@ cdef class pAdicCappedRelativeElement(CRElement): - ``absprec`` -- a non-negative integer (default: ``1``) - - ``field`` -- boolean (default ``None``); hether to return an element + - ``field`` -- boolean (default ``None``); whether to return an element of `\GF{p}` or `\ZZ / p\ZZ` - ``check_prec`` -- boolean (default ``True``); whether to raise diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index bda999eeca3..186426e1488 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -121,9 +121,9 @@ class EllipticCurve_rational_field(EllipticCurve_number_field): INPUT: - ``ainvs`` -- a list or tuple `[a_1, a_2, a_3, a_4, a_6]` of - Weierstrass coefficients. + Weierstrass coefficients - .. note:: + .. NOTE:: This class should not be called directly; use :class:`sage.constructor.EllipticCurve` to construct @@ -203,9 +203,9 @@ def __init__(self, ainvs, **kwds): def _set_rank(self, r): """ Internal function to set the cached rank of this elliptic curve to - r. + ``r``. - .. warning:: + .. WARNING:: No checking is done! Not intended for use by users. @@ -224,9 +224,9 @@ def _set_rank(self, r): def _set_torsion_order(self, t): """ Internal function to set the cached torsion order of this elliptic - curve to t. + curve to ``t``. - .. warning:: + .. WARNING:: No checking is done! Not intended for use by users. @@ -245,9 +245,9 @@ def _set_torsion_order(self, t): def _set_cremona_label(self, L): """ Internal function to set the cached label of this elliptic curve to - L. + ``L``. - .. warning:: + .. WARNING:: No checking is done! Not intended for use by users. @@ -270,9 +270,9 @@ def _set_cremona_label(self, L): def _set_conductor(self, N): """ Internal function to set the cached conductor of this elliptic - curve to N. + curve to ``N.`` - .. warning:: + .. WARNING:: No checking is done! Not intended for use by users. Setting to the wrong value will cause strange problems (see @@ -291,9 +291,9 @@ def _set_conductor(self, N): def _set_modular_degree(self, deg): """ Internal function to set the cached modular degree of this elliptic - curve to deg. + curve to ``deg``. - .. warning:: + .. WARNING:: No checking is done! @@ -312,9 +312,9 @@ def _set_modular_degree(self, deg): def _set_gens(self, gens): """ Internal function to set the cached generators of this elliptic - curve to gens. + curve to ``gens``. - .. warning:: + .. WARNING:: No checking is done! @@ -361,7 +361,7 @@ def is_p_integral(self, p): INPUT: - - ``p`` -- a prime integer + - ``p`` -- a prime integer EXAMPLES:: @@ -412,7 +412,6 @@ def mwrank(self, options=''): INPUT: - - ``options`` (string) -- run-time options passed when starting mwrank. The format is as follows (see below for examples of usage): @@ -430,8 +429,7 @@ def mwrank(self, options=''): - ``string`` - output of mwrank on this curve - - .. note:: + .. NOTE:: The output is a raw string and completely illegible using automatic display, so it is recommended to use print for @@ -457,10 +455,8 @@ def mwrank(self, options=''): sage: E = EllipticCurve([0,0,0,877,0]) - Run mwrank with 'verbose' flag set to 0 but list generators if - found - - :: + Run mwrank with ``'verbose'`` flag set to 0 but list generators if + found:: sage: print(E.mwrank('-v0 -l')) Curve [0,0,0,877,0] : 0 <= rank <= 1 @@ -487,7 +483,6 @@ def conductor(self, algorithm="pari"): INPUT: - - ``algorithm`` - str, (default: "pari") - ``"pari"`` - use the PARI C-library :pari:`ellglobalred` @@ -498,14 +493,14 @@ def conductor(self, algorithm="pari"): coefficients (TODO: limited to small conductor until mwrank gets integer factorization) - - ``"gp"`` - use the GP interpreter. + - ``"gp"`` - use the GP interpreter - ``"generic"`` - use the general number field implementation - ``"all"`` - use all four implementations, verify - that the results are the same (or raise an error), and output the - common value. + that the results are the same (or raise an error), and + output the common value EXAMPLES:: @@ -522,7 +517,7 @@ def conductor(self, algorithm="pari"): sage: E.conductor(algorithm="all") 3006 - .. note:: + .. NOTE:: The conductor computed using each algorithm is cached separately. Thus calling ``E.conductor('pari')``, then @@ -709,7 +704,7 @@ def database_curve(self): sage: E.database_curve() Elliptic Curve defined by y^2 = x^3 + x^2 + 3*x + 5 over Rational Field - .. note:: + .. NOTE:: The model of the curve in the database can be different from the Weierstrass model for this curve, e.g., database @@ -733,8 +728,7 @@ def Np(self, p): INPUT: - - ``p`` (int) -- a prime, not necessarily of good reduction. - + - ``p`` (int) -- a prime, not necessarily of good reduction OUTPUT: @@ -795,19 +789,18 @@ def mwrank_curve(self, verbose=False): return self.__mwrank_curve def two_descent(self, verbose=True, - selmer_only = False, - first_limit = 20, - second_limit = 8, - n_aux = -1, - second_descent = 1): + selmer_only=False, + first_limit=20, + second_limit=8, + n_aux=-1, + second_descent=1): """ Compute 2-descent data for this curve. INPUT: - - - ``verbose`` - (default: True) print what mwrank is - doing. If False, **no output** is printed. + - ``verbose`` - (default: ``True``) print what mwrank is + doing; if ``False``, **no output** is printed - ``selmer_only`` - (default: ``False``) selmer_only switch @@ -823,7 +816,6 @@ def two_descent(self, verbose=True, - ``second_descent`` - (default: True) second_descent only relevant for descent via 2-isogeny - OUTPUT: Return ``True`` if the descent succeeded, i.e. if the lower bound and @@ -861,12 +853,10 @@ def aplist(self, n, python_ints=False): INPUT: + - ``n`` -- integer - - ``n`` - integer - - - ``python_ints`` - bool (default: ``False``); if ``True`` - return a list of Python ints instead of Sage integers. - + - ``python_ints`` -- bool (default: ``False``); if ``True`` + return a list of Python ints instead of Sage integers OUTPUT: list of integers @@ -893,21 +883,18 @@ def aplist(self, n, python_ints=False): else: return [Integer(a) for a in v] - - def anlist(self, n, python_ints=False): - """ + r""" The Fourier coefficients up to and including `a_n` of the - modular form attached to this elliptic curve. The i-th element of - the return list is a[i]. + modular form attached to this elliptic curve. The `i`-th element of + the return list is ``a[i]``. INPUT: + - ``n`` -- integer - - ``n`` - integer - - - ``python_ints`` - bool (default: ``False``); if ``True`` - return a list of Python ints instead of Sage integers. + - ``python_ints`` -- bool (default: ``False``); if ``True`` + return a list of Python ints instead of Sage integers OUTPUT: list of integers @@ -983,15 +970,13 @@ def q_expansion(self, prec): INPUT: - - - ``prec`` - an integer - + - ``prec`` -- an integer OUTPUT: a power series (in the variable 'q') - .. note:: + .. NOTE:: If you want the output to be a modular form and not just a `q`-expansion, use :meth:`.modular_form`. @@ -1000,7 +985,8 @@ def q_expansion(self, prec): sage: E = EllipticCurve('37a1') sage: E.q_expansion(20) - q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + 6*q^6 - q^7 + 6*q^9 + 4*q^10 - 5*q^11 - 6*q^12 - 2*q^13 + 2*q^14 + 6*q^15 - 4*q^16 - 12*q^18 + O(q^20) + q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + 6*q^6 - q^7 + 6*q^9 + 4*q^10 + - 5*q^11 - 6*q^12 - 2*q^13 + 2*q^14 + 6*q^15 - 4*q^16 - 12*q^18 + O(q^20) """ return PowerSeriesRing(Q, 'q')(self.anlist(prec), prec, check=True) @@ -1019,9 +1005,10 @@ def modular_form(self): If you need to see more terms in the `q`-expansion:: sage: f.q_expansion(20) - q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + 6*q^6 - q^7 + 6*q^9 + 4*q^10 - 5*q^11 - 6*q^12 - 2*q^13 + 2*q^14 + 6*q^15 - 4*q^16 - 12*q^18 + O(q^20) + q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + 6*q^6 - q^7 + 6*q^9 + 4*q^10 + - 5*q^11 - 6*q^12 - 2*q^13 + 2*q^14 + 6*q^15 - 4*q^16 - 12*q^18 + O(q^20) - .. note:: + .. NOTE:: If you just want the `q`-expansion, use :meth:`.q_expansion`. @@ -1041,11 +1028,8 @@ def modular_symbol_space(self, sign=1, base_ring=Q, bound=None): INPUT: - - - ``sign`` - 0, -1, or 1 - - - ``base_ring`` - a ring - + - ``sign`` -- 0, -1, or 1 + - ``base_ring`` -- a ring EXAMPLES:: @@ -1118,14 +1102,15 @@ def _modular_symbol_normalize(self, sign, normalize, implementation, nap): @cached_method(key = _modular_symbol_normalize) def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=0): - r"""Return the modular symbol map associated to this elliptic curve + r""" + Return the modular symbol map associated to this elliptic curve with given sign. INPUT: - - ``sign`` - +1 (default) or -1. + - ``sign`` -- +1 (default) or -1. - - ``normalize`` - (default: None); either 'L_ratio', 'period', + - ``normalize`` -- (default: ``None``); either 'L_ratio', 'period', or 'none'; ignored unless ``implementation`` is 'sage'. For 'L_ratio', the modular symbol tries to normalize correctly as explained below by comparing it to ``L_ratio`` @@ -1137,14 +1122,14 @@ def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=0) certainly not correctly normalized, i.e. all values will be a fixed scalar multiple of what they should be. - - ``implementation`` - either 'eclib' (default), 'sage' or + - ``implementation`` -- either 'eclib' (default), 'sage' or 'num'. Here, 'eclib' uses Cremona's ``C++`` implementation in the ``eclib`` library, 'sage' uses an implementation within Sage which is often quite a bit slower, and 'num' uses Wuthrich's implementation of numerical modular symbols. - - ``nap`` - (int, default 0); ignored unless implementation is + - ``nap`` -- (int, default 0); ignored unless implementation is 'eclib'. The number of ap of E to use in determining the normalisation of the modular symbols. If 0 (the default), then the value of 100*E.conductor().isqrt() is used. Using @@ -1152,43 +1137,43 @@ def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=0) DEFINITION: - The modular symbol map sends any rational number `r` to the - rational number whichis the ratio of the real or imaginary - part (depending on the sign) of the integral of `2 \pi i - f(z) dz` from `\infty` to `r`, where `f` is the newform - attached to `E`, to the real or imaginary period of `E`. - - More precisely: If the sign is +1, then the value returned - is the quotient of the real part of this integral by the - least positive period `\Omega_E^{+}` of `E`. In particular - for `r=0`, the value is equal to `L(E,1)/\Omega_E^{+}` - (unlike in ``L_ratio`` of ``lseries()``, where the value is - also divided by the number of connected components of - `E(\RR)`). In particular the modular symbol depends on `E` - and not only the isogeny class of `E`. For sign `-1`, it - is the quotient of the imaginary part of the integral - divided by the purely imaginary period of `E` with smallest - positive imaginary part. Note however there is an issue - about these normalizations, hence the optional argument - ``normalize`` explained below + The modular symbol map sends any rational number `r` to the + rational number whichis the ratio of the real or imaginary + part (depending on the sign) of the integral of `2 \pi i + f(z) dz` from `\infty` to `r`, where `f` is the newform + attached to `E`, to the real or imaginary period of `E`. + + More precisely: If the sign is +1, then the value returned + is the quotient of the real part of this integral by the + least positive period `\Omega_E^{+}` of `E`. In particular + for `r=0`, the value is equal to `L(E,1)/\Omega_E^{+}` + (unlike in ``L_ratio`` of ``lseries()``, where the value is + also divided by the number of connected components of + `E(\RR)`). In particular the modular symbol depends on `E` + and not only the isogeny class of `E`. For sign `-1`, it + is the quotient of the imaginary part of the integral + divided by the purely imaginary period of `E` with smallest + positive imaginary part. Note however there is an issue + about these normalizations, hence the optional argument + ``normalize`` explained below ALGORITHM: - For the implementations 'sage' and 'eclib', the used - algorithm starts by finding the space of modular symbols - within the full space of all modular symbols of that - level. This initial step will take a very long time if the - conductor is large (e.g. minutes for five digit - conductors). Once the space is determined, each evaluation - is very fast (logarithmic in the denominator of `r`). - - The implementation 'num' uses a different algorithm. It - uses numerical integration along paths in the upper half - plane. The bounds are rigorously proved so that the outcome - is known to be correct. The initial step costs no time, - instead each evaluation will take more time than in the - above. More information in the documentation of the class - ``ModularSymbolNumerical``. + For the implementations 'sage' and 'eclib', the used + algorithm starts by finding the space of modular symbols + within the full space of all modular symbols of that + level. This initial step will take a very long time if the + conductor is large (e.g. minutes for five digit + conductors). Once the space is determined, each evaluation + is very fast (logarithmic in the denominator of `r`). + + The implementation 'num' uses a different algorithm. It + uses numerical integration along paths in the upper half + plane. The bounds are rigorously proved so that the outcome + is known to be correct. The initial step costs no time, + instead each evaluation will take more time than in the + above. More information in the documentation of the class + ``ModularSymbolNumerical``. .. SEEALSO:: @@ -1314,7 +1299,7 @@ def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=0) return M def modular_symbol_numerical(self, sign=1, prec=20): - """ + r""" Return the modular symbol as a numerical function. Just as in :meth:`modular_symbol` this returns a function @@ -1336,16 +1321,16 @@ def modular_symbol_numerical(self, sign=1, prec=20): ALGORITHM: - This method does not compute spaces of modular symbols, - so it is suitable for curves of larger conductor than - can be handled by :meth:`modular_symbol`. - It is essentially the same implementation as - ``modular_symbol`` with implementation set to 'num'. - However the precision is not automatically chosen to - be certain that the output is equal to the rational - number it approximates. + This method does not compute spaces of modular symbols, + so it is suitable for curves of larger conductor than + can be handled by :meth:`modular_symbol`. + It is essentially the same implementation as + ``modular_symbol`` with implementation set to 'num'. + However the precision is not automatically chosen to + be certain that the output is equal to the rational + number it approximates. - For large conductors one should set the ``prec`` very small. + For large conductors one should set the ``prec`` very small. EXAMPLES:: @@ -1467,9 +1452,9 @@ def analytic_rank(self, algorithm="pari", leading_coefficient=False): - ``leading_coefficient`` -- (default: ``False``) Boolean; if set to True, return a tuple `(rank, lead)` where `lead` is the value of the first non-zero derivative of the L-function of the elliptic - curve. Only implemented for algorithm='pari'. + curve. Only implemented for ``algorithm='pari'``. - .. note:: + .. NOTE:: If the curve is loaded from the large Cremona database, then the modular degree is taken from the database. @@ -1481,12 +1466,12 @@ def analytic_rank(self, algorithm="pari", leading_coefficient=False): using default parameters, testing indicates that for 99.75% of curves the returned rank bound is the true rank. - .. note:: + .. NOTE:: - If you use set_verbose(1), extra information about the computation - will be printed when algorithm='zero_sum'. + If you use ``set_verbose(1)``, extra information about the + computation will be printed when ``algorithm='zero_sum'``. - .. note:: + .. NOTE:: It is an open problem to *prove* that *any* particular elliptic curve has analytic rank `\geq 4`. @@ -1507,9 +1492,10 @@ def analytic_rank(self, algorithm="pari", leading_coefficient=False): sage: E.analytic_rank(algorithm='all') 2 - With the optional parameter leading_coefficient set to ``True``, a - tuple of both the analytic rank and the leading term of the - L-series at `s = 1` is returned. This only works for algorithm=='pari':: + With the optional parameter leading_coefficient set to ``True``, + a tuple of both the analytic rank and the leading term of the + L-series at `s = 1` is returned. This only works for + ``algorithm=='pari'``:: sage: EllipticCurve([0,-1,1,-10,-20]).analytic_rank(leading_coefficient=True) (0, 0.25384186085591068...) @@ -1524,7 +1510,8 @@ def analytic_rank(self, algorithm="pari", leading_coefficient=False): TESTS: - When the input is horrendous, some of the algorithms just bomb out with a RuntimeError:: + When the input is horrendous, some of the algorithms just bomb + out with a ``RuntimeError``:: sage: EllipticCurve([1234567,89101112]).analytic_rank(algorithm='rubinstein') Traceback (most recent call last): @@ -1597,10 +1584,10 @@ def analytic_rank_upper_bound(self, INPUT: - - ``max_Delta`` -- (default: None) If not None, a positive real value + - ``max_Delta`` -- (default: ``None``) If not ``None``, a positive real value specifying the maximum Delta value used in the zero sum; larger values of Delta yield better bounds - but runtime is exponential in - Delta. If left as None, Delta is set + Delta. If left as ``None``, Delta is set to `\min\{\frac{1}{\pi}(\log(N+1000)/2-\log(2\pi)-\eta), 2.5\}`, where `N` is the conductor of the curve attached to self, and `\eta` is the Euler-Mascheroni constant `= 0.5772...`; the crossover @@ -1608,7 +1595,7 @@ def analytic_rank_upper_bound(self, empirical results show that for about 99.7% of all curves the returned value is the actual analytic rank. - - ``adaptive`` -- (default: True) Boolean + - ``adaptive`` -- (default: ``True``) boolean - ``True`` -- the computation is first run with small and then successively larger `\Delta` values up to max_Delta. If at any @@ -1618,11 +1605,11 @@ def analytic_rank_upper_bound(self, - ``False`` -- the computation is run a single time with `\Delta` equal to ``max_Delta``, and the resulting bound returned. - - ``N`` -- (default: None) If not None, a positive integer equal to - the conductor of self. This is passable so that rank estimation + - ``N`` -- (default: ``None``) If not ``None``, a positive integer equal + to the conductor of ``self``. This is passable so that rank estimation can be done for curves whose (large) conductor has been precomputed. - - ``root_number`` -- (default: "compute") String or integer + - ``root_number`` -- (default: "compute") string or integer - ``"compute"`` -- the root number of self is computed and used to (possibly) lower the analytic rank estimate by 1. @@ -1634,12 +1621,12 @@ def analytic_rank_upper_bound(self, self. This is passable so that rank estimation can be done for curves whose root number has been precomputed. - - ``bad_primes`` -- (default: None) If not None, a list of the primes + - ``bad_primes`` -- (default: ``None``) If not ``None``, a list of the primes of bad reduction for the curve attached to self. This is passable so that rank estimation can be done for curves of large conductor whose bad primes have been precomputed. - - ``ncpus`` - (default: None) If not None, a positive integer + - ``ncpus`` - (default: ``None``) If not ``None``, a positive integer defining the maximum number of CPUs to be used for the computation. If left as None, the maximum available number of CPUs will be used. Note: Due to parallelization overhead, multiple processors will @@ -1790,8 +1777,6 @@ def simon_two_descent(self, verbose=0, lim1=5, lim3=50, limtriv=3, INPUT: - - ``self`` -- an elliptic curve `E` over `\QQ` - - ``verbose`` -- 0, 1, 2, or 3 (default: 0), the verbosity level - ``lim1`` -- (default: 5) limit on trivial points on quartics @@ -1827,7 +1812,9 @@ def simon_two_descent(self, verbose=0, lim1=5, lim3=50, limtriv=3, To obtain a list of generators, use E.gens(). - IMPLEMENTATION: Uses Denis Simon's PARI/GP scripts from + IMPLEMENTATION: + + Uses Denis Simon's PARI/GP scripts from http://www.math.unicaen.fr/~simon/ EXAMPLES: @@ -1947,12 +1934,10 @@ def three_selmer_rank(self, algorithm='UseSUnits'): INPUT: - - - ``algorithm`` - 'Heuristic' (which is usually much + - ``algorithm`` -- 'Heuristic' (which is usually much faster in large examples), 'FindCubeRoots', or 'UseSUnits' (default) - OUTPUT: nonnegative integer EXAMPLES: A rank 0 curve:: @@ -2001,24 +1986,24 @@ def rank(self, use_database=True, verbose=False, INPUT: - - ``use_database (bool)`` -- (default: ``True``), if - ``True``, try to look up the rank in the Cremona database. + - ``use_database`` -- boolean (default: ``True``); if + ``True``, try to look up the rank in the Cremona database - - ``verbose`` - (default: ``False``), if specified changes - the verbosity of mwrank computations. + - ``verbose`` -- (default: ``False``) if specified changes + the verbosity of mwrank computations - - ``algorithm`` - (default: 'mwrank_lib'), one of: + - ``algorithm`` -- (default: ``'mwrank_lib'``) one of: - ``'mwrank_shell'`` - call mwrank shell command - ``'mwrank_lib'`` - call mwrank c library - - ``only_use_mwrank`` - (default: True) if False try - using analytic rank methods first. + - ``only_use_mwrank`` -- (default: ``True``) if ``False`` try + using analytic rank methods first - - ``proof`` - bool or None (default: None, see - proof.elliptic_curve or sage.structure.proof). Note that results - obtained from databases are considered proof = True + - ``proof`` -- bool (default: ``None``, see + ``proof.elliptic_curve`` or ``sage.structure.proof``); note that + results obtained from databases are considered ``proof=True`` OUTPUT: the rank of the elliptic curve as :class:`Integer` @@ -2185,8 +2170,8 @@ def rank(self, use_database=True, verbose=False, raise ValueError("unknown algorithm {!r}".format(algorithm)) def gens(self, proof=None, **kwds): - """ - Return generators for the Mordell-Weil group E(Q) *modulo* + r""" + Return generators for the Mordell-Weil group `E(Q)` *modulo* torsion. INPUT: @@ -2280,8 +2265,8 @@ def _compute_gens(self, proof, use_database=True, descent_second_limit=12, sat_bound=1000): - """ - Return generators for the Mordell-Weil group E(Q) *modulo* + r""" + Return generators for the Mordell-Weil group `E(Q)` *modulo* torsion. INPUT: @@ -2436,7 +2421,7 @@ def gens_certain(self): return self.__gens[1] def ngens(self, proof=None): - """ + r""" Return the number of generators of this elliptic curve. .. NOTE:: @@ -2525,27 +2510,27 @@ def regulator(self, proof=None, precision=53, **kwds): return reg def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): - """Given a list of rational points on E, compute the saturation in - E(Q) of the subgroup they generate. + r""" + Given a list of rational points on `E`, compute the saturation in + `E(Q)` of the subgroup they generate. INPUT: + - ``points (list)`` -- list of points on `E` - - ``points (list)`` - list of points on E - - - ``verbose (bool)`` - (default: ``False``), if ``True``, give + - ``verbose (bool)`` -- (default: ``False``) if ``True``, give verbose output - - ``max_prime`` (int, default -1) -- If `-1` (the default), an + - ``max_prime`` -- int (default: `-1`); if `-1` (the default), an upper bound is computed for the primes at which the subgroup may not be saturated, and saturation is performed for all - primes up to this bound. Otherwise, the bound used is the - minimum of ``max_prime`` and the computed bound. + primes up to this bound; otherwise, the bound used is the + minimum of ``max_prime`` and the computed bound - - ``min_prime (int)`` - (default: 2), only do `p`-saturation - at primes `p` greater than or equal to this. + - ``min_prime (int)`` - (default: `2`) only do `p`-saturation + at primes `p` greater than or equal to this - .. note:: + .. NOTE:: To saturate at a single prime `p`, set ``max_prime`` and ``min_prime`` both to `p`. One situation where this is @@ -2564,13 +2549,15 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): - ``regulator (real with default precision)`` - regulator of saturated points. - ALGORITHM: Uses Cremona's ``eclib`` package, which computes a + ALGORITHM: + + Uses Cremona's ``eclib`` package, which computes a bound on the saturation index. To `p`-saturate, or prove `p`-saturation, we consider the reductions of the points modulo primes `q` of good reduction such that `E(\GF{q})` has order divisible by `p`. - .. note:: + .. NOTE:: In versons of ``eclib`` up to ``v20190909``, division of points in ``eclib`` was done using floating point methods, @@ -2582,7 +2569,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): division polynomials, and `p`-saturation cannot fail in this way. - .. note:: + .. NOTE:: The computed index of saturation may be large, in which case saturation may take a long time. For example, the @@ -2590,7 +2577,6 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): saturation index bound of 11816 and takes around 40 seconds to prove saturation. - EXAMPLES:: sage: E = EllipticCurve('37a1') @@ -2718,21 +2704,22 @@ def CPS_height_bound(self): def silverman_height_bound(self, algorithm='default'): r""" - Return the Silverman height bound. This is a positive real - (floating point) number B such that for all points `P` on the - curve over any number field, `|h(P) - \hat{h}(P)| \leq B`, - where `h(P)` is the naive logarithmic height of `P` and - `\hat{h}(P)` is the canonical height. + Return the Silverman height bound. + + This is a positive real (floating point) number B such that + for all points `P` on the curve over any number field, + `|h(P) - \hat{h}(P)| \leq B`, where `h(P)` is the naive + logarithmic height of `P` and `\hat{h}(P)` is the canonical height. INPUT: - - ``algorithm`` -- + - ``algorithm`` -- one of the following: - - 'default' (default) -- compute using a Python - implementation in Sage + * ``'default'`` (default) - compute using a Python + implementation in Sage - - 'mwrank' -- use a C++ implementation in the mwrank - library + * ``'mwrank'`` -- use a C++ implementation in the mwrank + library .. NOTE:: @@ -2780,31 +2767,27 @@ def h_oo(x): raise ValueError("unknown algorithm '%s'"%algorithm) def point_search(self, height_limit, verbose=False, rank_bound=None): - """ + r""" Search for points on a curve up to an input bound on the naive logarithmic height. INPUT: + - ``height_limit`` -- float; bound on naive height - - ``height_limit (float)`` - bound on naive height - - - ``verbose (bool)`` - (default: ``False``) - - If ``True``, report on the saturation process. - - If ``False``, just return the result. - - - ``rank_bound (bool)`` - (default: ``None``) + - ``verbose`` -- boolean (default: ``False``); + if ``True``, report on the saturation process + otherwise just return the result - If provided, stop saturating once we find this many - independent nontorsion points. + - ``rank_bound`` -- boolean (optional); + if provided, stop saturating once we find this many + independent nontorsion points OUTPUT: points (list) - list of independent points which generate the subgroup of the Mordell-Weil group generated by the points found and then saturated. - .. warning:: + .. WARNING:: height_limit is logarithmic, so increasing by 1 will cause the running time to increase by a factor of approximately @@ -2862,7 +2845,7 @@ def point_search(self, height_limit, verbose=False, rank_bound=None): return points def selmer_rank(self): - """ + r""" The rank of the 2-Selmer group of the curve. EXAMPLES: The following is the curve 960D1, which has rank 0, but @@ -2909,9 +2892,11 @@ def selmer_rank(self): return self.__selmer_rank def rank_bound(self): - """ + r""" Upper bound on the rank of the curve, computed using - 2-descent. In many cases, this is the actual rank of the + 2-descent. + + In many cases, this is the actual rank of the curve. If the curve has no 2-torsion it is the same as the 2-selmer rank. @@ -2943,9 +2928,9 @@ def rank_bound(self): return self.__rank_bound def an(self, n): - """ - The n-th Fourier coefficient of the modular form corresponding to - this elliptic curve, where n is a positive integer. + r""" + The ``n``-th Fourier coefficient of the modular form corresponding to + this elliptic curve, where ``n`` is a positive integer. EXAMPLES:: @@ -2957,8 +2942,8 @@ def an(self, n): def ap(self, p): """ - The p-th Fourier coefficient of the modular form corresponding to - this elliptic curve, where p is prime. + The ``p``-th Fourier coefficient of the modular form corresponding to + this elliptic curve, where ``p`` is prime. EXAMPLES:: @@ -3018,14 +3003,15 @@ def is_minimal(self): def is_p_minimal(self, p): """ - Tests if curve is p-minimal at a given prime p. + Tests if curve is ``p``-minimal at a given prime ``p``. + + INPUT: - INPUT: p -- a prime + - ``p`` -- a prime OUTPUT: - ``True`` -- if curve is p-minimal - - ``False`` -- if curve is not p-minimal EXAMPLES:: @@ -3051,17 +3037,17 @@ def is_p_minimal(self, p): return self.discriminant().valuation(p) == Emin.discriminant().valuation(p) def kodaira_type(self, p): - """ - Local Kodaira type of the elliptic curve at `p`. + r""" + Local Kodaira type of the elliptic curve at ``p``. INPUT: - - p -- an integral prime + - ``p`` -- an integral prime OUTPUT: - - the Kodaira type of this elliptic curve at p, - as a KodairaSymbol. + - the Kodaira type of this elliptic curve at ``p``, + as a :class:`KodairaSymbol` EXAMPLES:: @@ -3074,19 +3060,17 @@ def kodaira_type(self, p): kodaira_symbol = kodaira_type def kodaira_type_old(self, p): - """ - Local Kodaira type of the elliptic curve at `p`. + r""" + Local Kodaira type of the elliptic curve at ``p``. INPUT: - - - p, an integral prime - + - ``p`` -- an integral prime OUTPUT: - - the Kodaira type of this elliptic curve at p, - as a KodairaSymbol. + - the Kodaira type of this elliptic curve at ``p``, + as a :class:`KodairaSymbol` EXAMPLES:: @@ -3110,7 +3094,7 @@ def kodaira_type_old(self, p): def tamagawa_number(self, p): r""" - The Tamagawa number of the elliptic curve at `p`. + The Tamagawa number of the elliptic curve at ``p``. This is the order of the component group `E(\QQ_p)/E^0(\QQ_p)`. @@ -3128,7 +3112,7 @@ def tamagawa_number(self, p): def tamagawa_number_old(self, p): r""" - The Tamagawa number of the elliptic curve at `p`. + The Tamagawa number of the elliptic curve at ``p``. This is the order of the component group `E(\QQ_p)/E^0(\QQ_p)`. @@ -3152,7 +3136,7 @@ def tamagawa_number_old(self, p): def tamagawa_exponent(self, p): r""" - The Tamagawa index of the elliptic curve at `p`. + The Tamagawa index of the elliptic curve at ``p``. This is the index of the component group `E(\QQ_p)/E^0(\QQ_p)`. It equals the @@ -3230,18 +3214,18 @@ def real_components(self): """ return 2 if self.discriminant() > 0 else 1 - def has_good_reduction_outside_S(self, S=[]): + def has_good_reduction_outside_S(self, S=None): r""" - Test if this elliptic curve has good reduction outside `S`. + Test if this elliptic curve has good reduction outside ``S``. INPUT: - - `S` -- list of primes (default: empty list). + - ``S`` -- list of primes (default: ``[]``). - .. note:: + .. NOTE:: - Primality of elements of S is not checked, and the output - is undefined if S is not a list or contains non-primes. + Primality of elements of ``S`` is not checked, and the output + is undefined if ``S`` is not a list or contains non-primes. This only tests the given model, so should only be applied to minimal models. @@ -3257,6 +3241,8 @@ def has_good_reduction_outside_S(self, S=[]): sage: EllipticCurve('2310a1').has_good_reduction_outside_S([2,3,5,7,11]) True """ + if S is None: + S = [] return self.discriminant().is_S_unit(S) def period_lattice(self, embedding=None): @@ -3290,21 +3276,22 @@ def period_lattice(self, embedding=None): def elliptic_exponential(self, z, embedding=None): r""" - Compute the elliptic exponential of a complex number with respect to the elliptic curve. + Compute the elliptic exponential of a complex number with + respect to the elliptic curve. INPUT: - - ``z`` (complex) -- a complex number + - ``z`` -- a complex number - - ``embedding`` - ignored (for compatibility with the - period_lattice function for elliptic_curve_number_field) + - ``embedding`` - ignored (for compatibility with the + period_lattice function for elliptic_curve_number_field) OUTPUT: The image of `z` modulo `L` under the Weierstrass parametrization `\CC/L \to E(\CC)`. - .. note:: + .. NOTE:: The precision is that of the input ``z``, or the default precision of 53 bits if ``z`` is exact. @@ -3432,13 +3419,14 @@ def lseries_gross_zagier(self, A): def Lambda(self, s, prec): r""" - Return the value of the Lambda-series of the elliptic curve E at - s, where s can be any complex number. + Return the value of the Lambda-series of the elliptic curve `E` at + ``s``, where ``s`` can be any complex number. + + IMPLEMENTATION: - IMPLEMENTATION: Fairly *slow* computation using the definitions - and implemented in Python. + Fairly *slow* computation using the definitions implemented in Python. - Uses prec terms of the power series. + Uses ``prec`` terms of the power series. EXAMPLES:: @@ -3459,10 +3447,10 @@ def _F(n, t): return gamma_inc(t+1, 2*pi*n/sqrtN) * C(sqrtN/(2*pi*n))**(t+1) return sum(a[n]*(_F(n,s-1) + eps*_F(n,1-s)) for n in range(1, prec+1)) - def is_local_integral_model(self,*p): + def is_local_integral_model(self, *p): r""" - Tests if self is integral at the prime `p`, or at all the - primes if `p` is a list or tuple of primes + Tests if ``self`` is integral at the prime ``p``, or at all the + primes if ``p`` is a list or tuple of primes. EXAMPLES:: @@ -3482,9 +3470,9 @@ def is_local_integral_model(self,*p): assert p.is_prime(), "p must be prime in is_local_integral_model()" return all(x.valuation(p) >= 0 for x in self.ainvs()) - def local_integral_model(self,p): + def local_integral_model(self, p): r""" - Return a model of self which is integral at the prime `p`. + Return a model of self which is integral at the prime ``p``. EXAMPLES:: @@ -3561,32 +3549,34 @@ def integral_short_weierstrass_model(self): return constructor.EllipticCurve([A, B]) def _generalized_congmod_numbers(self, M, invariant="both"): - """ + r""" Internal method to compute the generalized modular degree and congruence number at level `MN`, where `N` is the conductor of `E`. + Values obtained are cached. - This function is called by self.modular_degree() and self.congruence_number() when - `M>1`. Since so much of the computation of the two values is shared, this method + This function is called by :meth:`modular_degree()` and + :meth:`congruence_number()` when `M > 1`. Since so much + of the computation of the two values is shared, this method by default computes and caches both. INPUT: - - ``M`` - Non-negative integer; this function is only ever called on M>1, although - the algorithm works fine for the case `M==1` + - ``M`` -- non-negative integer; this function is only ever called on + `M > 1`, although the algorithm works fine for the case `M = 1` - - ``invariant`` - String; default "both". Options are: + - ``invariant`` -- string (default: "both"``); options are: - - "both" - Both modular degree and congruence number at level `MN` are computed + - "both" - both modular degree and congruence number at level `MN` are computed - - "moddeg" - Only modular degree is computed + - "moddeg" - only modular degree is computed - - "congnum" - Only congruence number is computed + - "congnum" - only congruence number is computed OUTPUT: - - A dictionary containing either the modular degree (a positive integer) at index "moddeg", - or the congruence number (a positive integer) at index "congnum", or both. + A dictionary containing either the modular degree (a positive integer) at index "moddeg", + or the congruence number (a positive integer) at index "congnum", or both. As far as we know there is no other implementation for this algorithm, so as yet there is nothing to check the below examples against. @@ -3653,18 +3643,18 @@ def modular_degree(self, algorithm='sympow', M=1): INPUT: - - ``algorithm`` - string: + - ``algorithm`` -- string: - - ``'sympow'`` - (default) use Mark Watkin's (newer) C - program sympow + * ``'sympow'`` - (default) use Mark Watkin's (newer) C + program sympow - - ``'magma'`` - requires that MAGMA be installed (also - implemented by Mark Watkins) + * ``'magma'`` - requires that MAGMA be installed (also + implemented by Mark Watkins) - - ``M`` - Non-negative integer; the modular degree at level `MN` is returned - (see above) + - ``M`` -- non-negative integer; the modular degree at level `MN` + is returned (see above) - .. note:: + .. NOTE:: On 64-bit computers ec does not work, so Sage uses sympow even if ec is selected on a 64-bit computer. @@ -3672,7 +3662,6 @@ def modular_degree(self, algorithm='sympow', M=1): The correctness of this function when called with algorithm "sympow" is subject to the following three hypothesis: - - Manin's conjecture: the Manin constant is 1 - Steven's conjecture: the `X_1(N)`-optimal quotient is @@ -3698,7 +3687,7 @@ def modular_degree(self, algorithm='sympow', M=1): have to guess ahead of time at what point to curtail this expansion.' (Quote from an email of Mark Watkins.) - .. note:: + .. NOTE:: If the curve is loaded from the large Cremona database, then the modular degree is taken from the database. @@ -3750,7 +3739,7 @@ def modular_degree(self, algorithm='sympow', M=1): 245 """ # Case 1: standard modular degree - if M==1: + if M == 1: try: return self.__modular_degree @@ -3778,7 +3767,7 @@ def modular_degree(self, algorithm='sympow', M=1): def modular_parametrization(self): r""" Return the modular parametrization of this elliptic curve, which is - a map from `X_0(N)` to self, where `N` is the conductor of self. + a map from `X_0(N)` to self, where `N` is the conductor of ``self``. EXAMPLES:: @@ -3834,8 +3823,8 @@ def congruence_number(self, M=1): INPUT: - - `M` -- Non-negative integer; congruence number is computed - at level `MN`, where `N` is the conductor of ``self``. + - ``M`` -- non-negative integer; congruence number is computed + at level `MN`, where `N` is the conductor of ``self`` EXAMPLES:: @@ -3951,11 +3940,11 @@ def cremona_label(self, space=False): label = cremona_label def reduction(self,p): - """ + r""" Return the reduction of the elliptic curve at a prime of good reduction. - .. note:: + .. NOTE:: The actual reduction is done in ``self.change_ring(GF(p))``; the reduction is performed after changing to a model which @@ -3965,7 +3954,7 @@ def reduction(self,p): - ``p`` -- a (positive) prime number - OUTPUT: an elliptic curve over the finite field GF(p) + OUTPUT: an elliptic curve over the finite field `\GF{p}` EXAMPLES:: @@ -4022,7 +4011,7 @@ def torsion_order(self): self.__torsion_order = self.torsion_subgroup().order() return self.__torsion_order - def _torsion_bound(self,number_of_places = 20): + def _torsion_bound(self, number_of_places\=20): r""" Compute an upper bound on the order of the torsion group of the elliptic curve by counting points modulo several primes of good @@ -4033,20 +4022,18 @@ def _torsion_bound(self,number_of_places = 20): INPUT: - - ``number_of_places (default = 20)`` - the number + - ``number_of_places`` -- (default: 20) the number of places that will be used to find the bound OUTPUT: - - ``integer`` - the upper bound - - EXAMPLES: + - integer for the upper bound """ E = self bound = Integer(0) k = 0 p = Integer(2) # will run through odd primes - while k < number_of_places : + while k < number_of_places: p = p.next_prime() # check if the formal group at the place is torsion-free # if so the torsion injects into the reduction @@ -4059,13 +4046,13 @@ def _torsion_bound(self,number_of_places = 20): return bound def torsion_subgroup(self): - """ + r""" Return the torsion subgroup of this elliptic curve. OUTPUT: The EllipticCurveTorsionSubgroup instance associated to this elliptic curve. - .. note:: + .. NOTE:: To see the torsion points as a list, use :meth:`.torsion_points`. @@ -4173,16 +4160,15 @@ def torsion_points(self): @cached_method def root_number(self, p=None): - """ + r""" Return the root number of this elliptic curve. - This is 1 if the order of vanishing of the L-function L(E,s) at 1 + This is 1 if the order of vanishing of the L-function `L(E,s)` at 1 is even, and -1 if it is odd. INPUT: - - `p` -- optional, default (None); if given, return the local - root number at `p` + - `p` -- (optional) if given, return the local root number at ``p`` EXAMPLES:: @@ -4230,7 +4216,7 @@ def has_cm(self): :meth:`cm_discriminant()` and :meth:`has_rational_cm` - .. note:: + .. NOTE:: Even if `E` has CM in this sense (that its `j`-invariant is a CM `j`-invariant), since the associated negative @@ -4254,7 +4240,7 @@ def has_cm(self): return self.j_invariant() in CMJ def cm_discriminant(self): - """ + r""" Return the associated quadratic discriminant if this elliptic curve has Complex Multiplication over the algebraic closure. @@ -4288,9 +4274,8 @@ def has_rational_cm(self, field=None): INPUT: - - ``field`` -- a field, which should be an extension of `\QQ`. - If ``field`` is ``None`` (the default), it is taken to be - `\QQ`. + - ``field`` -- (default: `\QQ`) a field, which should be an + extension of `\QQ`; OUTPUT: @@ -4299,7 +4284,7 @@ def has_rational_cm(self, field=None): If ``field`` is ``None`` the output will always be ``False``. See also :meth:`cm_discriminant()` and :meth:`has_cm`. - .. note:: + .. NOTE:: If `E` has CM but the discriminant `D` is not a square in the given field `K`, which will certainly be the case for @@ -4371,7 +4356,7 @@ def has_rational_cm(self, field=None): def quadratic_twist(self, D): """ Return the global minimal model of the quadratic twist of this - curve by D. + curve by ``D``. EXAMPLES:: @@ -4392,14 +4377,14 @@ def minimal_quadratic_twist(self): discriminant of the quadratic field over which they are isomorphic. - .. note:: + .. NOTE:: If there is more than one curve with minimal conductor, the one returned is the one with smallest label (if in the database), or the one with minimal `a`-invariant list (otherwise). - .. note:: + .. NOTE:: For curves with `j`-invariant 0 or 1728 the curve returned is the minimal quadratic twist, not necessarily the minimal @@ -4428,7 +4413,8 @@ def minimal_quadratic_twist(self): sage: E.minimal_quadratic_twist() (Elliptic Curve defined by y^2 = x^3 + 4*x over Rational Field, 5) - If the curve has square-free conductor then it is already minimal (see :trac:`14060`):: + If the curve has square-free conductor then it is already + minimal (see :trac:`14060`):: sage: E = next(cremona_optimal_curves([2*3*5*7*11])) sage: (E, 1) == E.minimal_quadratic_twist() @@ -4485,26 +4471,26 @@ def isogeny_class(self, algorithm="sage", order=None): INPUT: - - ``algorithm`` - string: one of the following: + - ``algorithm`` -- string: one of the following: - "database" - use the Cremona database (only works if curve is isomorphic to a curve in the database) - "sage" (default) - use the native Sage implementation. - - ``order`` -- None, string, or list of curves (default: - None): If not None then the curves in the class are + - ``order`` -- ``None``, string, or list of curves (default: + ``None``); If not ``None`` then the curves in the class are reordered after being computed. Note that if the order is - None then the resulting order will depend on the algorithm. + ``None`` then the resulting order will depend on the algorithm. - - if ``order`` is "database" or "sage", then the reordering + - If ``order`` is "database" or "sage", then the reordering is so that the order of curves matches the order produced by that algorithm. - - if ``order`` is "lmfdb" then the curves are sorted + - If ``order`` is "lmfdb" then the curves are sorted lexicographically by a-invariants, in the LMFDB database. - - if ``order`` is a list of curves, then the curves in the + - If ``order`` is a list of curves, then the curves in the class are reordered to be isomorphic with the specified list of curves. @@ -4517,7 +4503,7 @@ class are reordered to be isomorphic with the specified for computing the isogeny matrix and the list of isogenies between curves in this class. - .. note:: + .. NOTE:: The curves in the isogeny class will all be standard minimal models. @@ -4658,14 +4644,14 @@ def isogenies_prime_degree(self, l=None): INPUT: - - ``l`` -- either None or a prime or a list of primes. + - ``l`` -- either ``None`` or a prime or a list of primes OUTPUT: (list) `\ell`-isogenies for the given `\ell` or if `\ell` is None, all `\ell`-isogenies. - .. note:: + .. NOTE:: The codomains of the isogenies returned are standard minimal models. This is because the functions @@ -4735,23 +4721,23 @@ def is_isogenous(self, other, proof=True, maxp=200): INPUT: - - ``other`` -- another elliptic curve. + - ``other`` -- another elliptic curve - - ``proof`` (default True) -- If ``False``, the function will + - ``proof`` -- (default: ``True``) if ``False``, the function will return ``True`` whenever the two curves have the same - conductor and are isogenous modulo `p` for `p` up to ``maxp``. - If ``True``, this test is followed by a rigorous test (which - may be more time-consuming). + conductor and are isogenous modulo `p` for `p` up to ``maxp``; + otherwise this test is followed by a rigorous test (which + may be more time-consuming) - - ``maxp`` (int, default 200) -- The maximum prime `p` for - which isogeny modulo `p` will be checked. + - ``maxp`` -- (default: 200) the maximum prime `p` for + which isogeny modulo `p` will be checked OUTPUT: (bool) True if there is an isogeny from curve ``self`` to curve ``other``. - METHOD: + ALGORITHM: First the conductors are compared as well as the Traces of Frobenius for good primes up to ``maxp``. If any of these @@ -4809,17 +4795,17 @@ def is_isogenous(self, other, proof=True, maxp=200): def isogeny_degree(self, other): """ - Return the minimal degree of an isogeny between self and - other. + Return the minimal degree of an isogeny between ``self`` and + ``other``. INPUT: - - ``other`` -- another elliptic curve. + - ``other`` -- another elliptic curve OUTPUT: - (int) The minimal degree of an isogeny from ``self`` to - ``other``, or 0 if the curves are not isogenous. + The minimal degree of an isogeny from ``self`` to + ``other``, or `0` if the curves are not isogenous. EXAMPLES:: @@ -4995,10 +4981,10 @@ def isogeny_graph(self, order=None): curve, where the vertices are isogenous curves over `\QQ` and the edges are prime degree isogenies. - .. note:: + .. NOTE:: - The vertices are labeled 1 to n rather than 0 to n-1 to - correspond to LMFDB and Cremona labels. + The vertices are labeled `1` to `n` rather than `0` to `n-1` + to correspond to LMFDB and Cremona labels. EXAMPLES:: @@ -5037,11 +5023,15 @@ def manin_constant(self): Return the Manin constant of this elliptic curve. If `\phi: X_0(N) \to E` is the modular - parametrization of minimal degree, then the Manin constant `c` is defined to be the rational - number `c` such that `\phi^*(\omega_E) = c\cdot \omega_f` where `\omega_E` is a Néron differential and `\omega_f = f(q) dq/q` is the differential on `X_0(N)` corresponding to the - newform `f` attached to the isogeny class of `E`. + parametrization of minimal degree, then the Manin constant `c` + is defined to be the rational number `c` such that + `\phi^*(\omega_E) = c\cdot \omega_f` where `\omega_E` is a Néron + differential and `\omega_f = f(q) dq/q` is the differential on `X_0(N)` + corresponding to the newform `f` attached to the isogeny class of `E`. - It is known that the Manin constant is an integer. It is conjectured that in each class there is at least one, more precisely the so-called strong Weil curve or `X_0(N)`-optimal curve, that has Manin constant `1`. + It is known that the Manin constant is an integer. It is conjectured + that in each class there is at least one, more precisely the so-called + strong Weil curve or `X_0(N)`-optimal curve, that has Manin constant `1`. OUTPUT: @@ -5155,7 +5145,7 @@ def _multiple_of_degree_of_isogeny_to_optimal_curve(self): the isogeny between this curve and the optimal curve in its isogeny class is a divisor of m. - .. warning:: + .. WARNING:: The result is *not* provably correct, in the sense that when the numbers are huge isogenies could be @@ -5246,13 +5236,14 @@ def is_semistable(self): return self.conductor().is_squarefree() def is_ordinary(self, p, ell=None): - """ - Return ``True`` precisely when the mod-p representation attached to - this elliptic curve is ordinary at ell. + r""" + Return ``True`` precisely when the mod-``p`` representation attached + to this elliptic curve is ordinary at ``ell``. INPUT: - - ``p`` - a prime ell - a prime (default: p) + - ``p`` -- a prime + - ``ell`` -- a prime (default: ``p``) OUTPUT: bool @@ -5273,11 +5264,11 @@ def is_ordinary(self, p, ell=None): def is_good(self, p, check=True): """ - Return ``True`` if `p` is a prime of good reduction for `E`. + Return ``True`` if ``p`` is a prime of good reduction for `E`. INPUT: - - ``p`` - a prime + - ``p`` -- a prime OUTPUT: bool @@ -5297,16 +5288,16 @@ def is_good(self, p, check=True): raise ValueError("p must be prime") return self.conductor() % p != 0 - def is_supersingular(self, p, ell=None): """ Return ``True`` precisely when p is a prime of good reduction and the - mod-p representation attached to this elliptic curve is + mod-``p`` representation attached to this elliptic curve is supersingular at ell. INPUT: - - ``p`` - a prime ell - a prime (default: p) + - ``p`` -- a prime + - ``ell`` -- a prime (default: ``p``) OUTPUT: bool @@ -5514,7 +5505,7 @@ def sha(self): def mod5family(self): """ Return the family of all elliptic curves with the same mod-5 - representation as self. + representation as ``self``. EXAMPLES:: @@ -5586,16 +5577,15 @@ def tate_curve(self, p): return Eq def height(self, precision=None): - """ - Return the real height of this elliptic curve. This is used in - integral_points() - - INPUT: + r""" + Return the real height of this elliptic curve. + This is used in :meth:`integral_points()`. - - ``precision`` - desired real precision of the result - (default real precision if None) + INPUT: + - ``precision`` -- desired real precision of the result + (default real precision if ``None``) EXAMPLES:: @@ -5635,23 +5625,24 @@ def height(self, precision=None): return max(R(1),h_j, h_gs) def faltings_height(self, stable=False, prec=None): - r"""Return the Faltings height (stable or unstable) of this elliptic curve. + r""" + Return the Faltings height (stable or unstable) of this elliptic curve. INPUT: - - ``stable`` (boolean, default ``False``) -- if ``True``, + - ``stable`` -- boolean (default: ``False``); if ``True``, return the *stable* Faltings height, otherwise the unstable - height. + height - - ``prec`` (integer or ``None``, default ``None``) -- bit - precision of output. If ``None`` (default), use standard - precision (53 bits). + - ``prec`` -- integer (default: ``None``); bit + precision of output; if ``None``, use standard + precision (53 bits) OUTPUT: (real) the Faltings height of this elliptic curve. - .. note:: + .. NOTE:: Different authors normalise the Faltings height differently. We use the formula `-\frac{1}{2}\log(A)`, @@ -5725,9 +5716,9 @@ def antilogarithm(self, z, max_denominator=None): - ``z`` -- a complex number representing an element of `\CC/L` where `L` is the period lattice of the elliptic curve - - ``max_denominator`` (int or None) -- parameter controlling + - ``max_denominator`` -- integer (optional); parameter controlling the attempted conversion of real numbers to rationals. If - None, ``simplest_rational()`` will be used; otherwise, + not given, ``simplest_rational()`` will be used; otherwise, ``nearby_rational()`` will be used with this value of ``max_denominator``. @@ -5736,7 +5727,7 @@ def antilogarithm(self, z, max_denominator=None): - point on the curve: the rational point which is the image of `z` under the Weierstrass parametrization, if it exists and can be determined from `z` and the given value - of max_denominator (if any); otherwise a ValueError exception + of max_denominator (if any); otherwise a ``ValueError`` exception is raised. EXAMPLES:: @@ -5780,7 +5771,7 @@ def integral_x_coords_in_interval(self,xmin,xmax): INPUT: - - ``xmin``, ``xmax`` (integers) -- two integers. + - ``xmin``, ``xmax`` (integers) -- two integers OUTPUT: @@ -5823,22 +5814,20 @@ def integral_points(self, mw_base='auto', both_signs=False, verbose=False): INPUT: + - ``mw_base`` -- (default: ``'auto'`` - calls ``self.gens()``) list + of EllipticCurvePoint generating the Mordell-Weil group of `E` - - ``mw_base`` - list of EllipticCurvePoint generating - the Mordell-Weil group of E (default: 'auto' - calls self.gens()) - - - ``both_signs`` - True/False (default False): if - True the output contains both P and -P, otherwise only one of each - pair. + - ``both_signs`` -- boolean (default: ``False``); if + ``True`` the output contains both `P` and `-P`, otherwise + only one of each pair - - ``verbose`` - True/False (default False): if True, + - ``verbose`` -- boolean (default: ``False``); if ``True``, some details of the computation are output + OUTPUT: A sorted list of all the integral points on `E` (up to sign + unless ``both_signs`` is ``True``) - OUTPUT: A sorted list of all the integral points on E (up to sign - unless both_signs is True) - - .. note:: + .. NOTE:: The complexity increases exponentially in the rank of curve E. The computation time (but not the output!) depends on @@ -5847,9 +5836,7 @@ def integral_points(self, mw_base='auto', both_signs=False, verbose=False): points which are not in the subgroup generated by the given points will almost certainly not be listed. - EXAMPLES: A curve of rank 3 with no torsion points - - :: + EXAMPLES: A curve of rank 3 with no torsion points:: sage: E = EllipticCurve([0,0,1,-7,6]) sage: P1=E.point((2,0)); P2=E.point((-1,3)); P3=E.point((4,6)) @@ -6215,20 +6202,19 @@ def S_integral_points(self, S, mw_base='auto', both_signs=False, verbose=False, INPUT: - - ``S`` - list of primes + - ``S`` -- list of primes - - ``mw_base`` - list of EllipticCurvePoint generating the - Mordell-Weil group of E (default: 'auto' - calls - :meth:`.gens`) + - ``mw_base`` -- (default: ``'auto'`` - calls :meth:`.gens`) list of + EllipticCurvePoint generating the Mordell-Weil group of `E` - - ``both_signs`` - True/False (default False): if True the - output contains both P and -P, otherwise only one of each - pair. + - ``both_signs`` -- boolean (default: ``False``); if ``True`` the + output contains both `P` and `-P`, otherwise only one of each + pair - - ``verbose`` - True/False (default False): if True, some - details of the computation are output. + - ``verbose`` -- boolean (default: ``False``); if ``True``, some + details of the computation are output - - ``proof`` - True/False (default True): if True ALL + - ``proof`` -- boolean (default: ``True``); if ``True`` ALL S-integral points will be returned. If False, the MW basis will be computed with the proof=False flag, and also the time-consuming final call to @@ -6916,10 +6902,10 @@ def integral_points_with_bounded_mw_coeffs(E, mw_base, N, x_bound): INPUT: - - ``E`` - an elliptic curve - - ``mw_base`` - a list of points on `E` (generators) - - ``N`` - a positive integer (bound on coefficients) - - ``x_bound`` - a positive real number (upper bound on size of x-coordinates) + - ``E`` -- an elliptic curve + - ``mw_base`` -- a list of points on `E` (generators) + - ``N`` -- a positive integer (bound on coefficients) + - ``x_bound`` -- a positive real number (upper bound on size of x-coordinates) OUTPUT: @@ -7097,3 +7083,4 @@ def elliptic_curve_congruence_graph(curves): G.add_edge(E.cremona_label(), F.cremona_label(), p_edges) return G + From 460e70c36555c6c0873d091386c34e8c90d1982f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Jul 2021 14:58:39 +1000 Subject: [PATCH 220/336] Some spelling errors and removed misplaced / character. --- .../rings/padics/padic_generic_element.pyx | 2 +- .../rings/padics/padic_relaxed_errors.pyx | 5 ++-- src/sage/rings/padics/relaxed_template.pxi | 30 +++++++++---------- .../elliptic_curves/ell_rational_field.py | 2 +- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 5f8fe60e0e6..0f6c67a227c 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -2328,7 +2328,7 @@ cdef class pAdicGenericElement(LocalGenericElement): -\log(1-x)=\sum_{n=1}^\infty \frac{x^n}{n}. For the result to be correct to precision ``aprec``, we sum all terms - for which the valuation of `x^n/n` is stricly smaller than ``aprec``. + for which the valuation of `x^n/n` is strictly smaller than ``aprec``. EXAMPLES:: diff --git a/src/sage/rings/padics/padic_relaxed_errors.pyx b/src/sage/rings/padics/padic_relaxed_errors.pyx index dff08758100..9451c7df14d 100644 --- a/src/sage/rings/padics/padic_relaxed_errors.pyx +++ b/src/sage/rings/padics/padic_relaxed_errors.pyx @@ -47,7 +47,7 @@ def raise_error(error, permissive=False): sage: raise_error(1) Traceback (most recent call last): ... - PrecisionError: computation has been abandonned; try to increase precision + PrecisionError: computation has been abandoned; try to increase precision sage: raise_error(1, permissive=True) @@ -68,4 +68,5 @@ def raise_error(error, permissive=False): if error & (ERROR_PRECISION | ERROR_NOTDEFINED): raise PrecisionError("not enough precision") if error & ERROR_ABANDON: - raise PrecisionError("computation has been abandonned; try to increase precision") + raise PrecisionError("computation has been abandoned; try to increase precision") + diff --git a/src/sage/rings/padics/relaxed_template.pxi b/src/sage/rings/padics/relaxed_template.pxi index 9ef4ea4e704..48a0468bc91 100644 --- a/src/sage/rings/padics/relaxed_template.pxi +++ b/src/sage/rings/padics/relaxed_template.pxi @@ -186,7 +186,7 @@ cdef class RelaxedElement(pAdicGenericElement): An error code which is a superposition of the following: - ``0`` -- no error - - ``ERROR_ABANDON = 1`` -- computation has been abandonned + - ``ERROR_ABANDON = 1`` -- computation has been abandoned - ``ERROR_NOTDEFINED = 2`` -- a number is not defined - ``ERROR_PRECISION = 4`` -- not enough precision - ``ERROR_OVERFLOW = 8`` -- overflow @@ -229,7 +229,7 @@ cdef class RelaxedElement(pAdicGenericElement): - ``prec`` -- an integer - ``halt`` -- an integer; the absolute precision after which the - computation is abandonned if the first significant digit has not + computation is abandoned if the first significant digit has not been found yet OUTPUT: @@ -1158,10 +1158,10 @@ cdef class RelaxedElement(pAdicGenericElement): if ``None``, use the default precision of the parent - ``halt`` -- an integer or a boolean (default: ``True``); - the absolute precision after which the computation is abandonned + the absolute precision after which the computation is abandoned if the first significant digit has not been found yet; if ``True``, the default halting precision of the parent is used; - if ``False``, the computation is never abandonned + if ``False``, the computation is never abandoned - ``permissive`` -- a boolean (default: ``False`` if ``prec`` is given, ``True`` otherwise); if ``False``, raise an error @@ -1189,7 +1189,7 @@ cdef class RelaxedElement(pAdicGenericElement): sage: b.at_precision_relative(5) Traceback (most recent call last): ... - PrecisionError: computation has been abandonned; try to increase precision + PrecisionError: computation has been abandoned; try to increase precision By setting the argument ``halt``, one can force the computation to continue until a prescribed limit:: @@ -1197,7 +1197,7 @@ cdef class RelaxedElement(pAdicGenericElement): sage: b.at_precision_relative(5, halt=20) # not enough to find the valuation Traceback (most recent call last): ... - PrecisionError: computation has been abandonned; try to increase precision + PrecisionError: computation has been abandoned; try to increase precision sage: b.at_precision_relative(5, halt=21) # now, we're okay 5^20 + O(5^25) @@ -1339,10 +1339,10 @@ cdef class RelaxedElement(pAdicGenericElement): INPUT: - ``halt`` -- an integer or a boolean (default: ``True``); - the absolute precision after which the computation is abandonned + the absolute precision after which the computation is abandoned if the first significant digit has not been found yet; if ``True``, the default halting precision of the parent is used; - if ``False``, the computation is never abandonned + if ``False``, the computation is never abandoned - ``secure`` -- a boolean (default: the value given at the creation of the parent); when the valuation cannot be determined for sure, @@ -1443,10 +1443,10 @@ cdef class RelaxedElement(pAdicGenericElement): INPUT: - ``halt`` -- an integer or a boolean (default: ``True``); - the absolute precision after which the computation is abandonned + the absolute precision after which the computation is abandoned if the first significant digit has not been found yet; if ``True``, the default halting precision of the parent is used; - if ``False``, the computation is never abandonned + if ``False``, the computation is never abandoned EXAMPLES:: @@ -1473,7 +1473,7 @@ cdef class RelaxedElement(pAdicGenericElement): ... ValueError: unit part of 0 not defined - See :meth:`valuation` for more details on the paramter ``halt``. + See :meth:`valuation` for more details on the parameter ``halt``. """ val = self.valuation(halt) @@ -1488,10 +1488,10 @@ cdef class RelaxedElement(pAdicGenericElement): INPUT: - ``halt`` -- an integer or a boolean (default: ``True``); - the absolute precision after which the computation is abandonned + the absolute precision after which the computation is abandoned if the first significant digit has not been found yet; if ``True``, the default halting precision of the parent is used; - if ``False``, the computation is never abandonned + if ``False``, the computation is never abandoned EXAMPLES:: @@ -1519,7 +1519,7 @@ cdef class RelaxedElement(pAdicGenericElement): ... ValueError: unit part of 0 not defined - See :meth:`valuation` for more details on the paramter ``halt``. + See :meth:`valuation` for more details on the parameter ``halt``. """ val = self.valuation(halt) @@ -2049,7 +2049,7 @@ cdef class RelaxedElement_abandon(RelaxedElement): sage: x[0] Traceback (most recent call last): ... - PrecisionError: computation has been abandonned; try to increase precision + PrecisionError: computation has been abandoned; try to increase precision """ self._valuation = -maxordp diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 186426e1488..2424cd1118b 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -4011,7 +4011,7 @@ def torsion_order(self): self.__torsion_order = self.torsion_subgroup().order() return self.__torsion_order - def _torsion_bound(self, number_of_places\=20): + def _torsion_bound(self, number_of_places=20): r""" Compute an upper bound on the order of the torsion group of the elliptic curve by counting points modulo several primes of good From 49a30e603e591c9a636f5fcb3450646c565b25ce Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Jul 2021 17:00:29 +1000 Subject: [PATCH 221/336] Adding that n,k are positive integers. --- src/sage/algebras/quantum_clifford.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/quantum_clifford.py b/src/sage/algebras/quantum_clifford.py index 933277e3564..2835334405b 100644 --- a/src/sage/algebras/quantum_clifford.py +++ b/src/sage/algebras/quantum_clifford.py @@ -59,8 +59,8 @@ class QuantumCliffordAlgebra(CombinatorialFreeModule): INPUT: - - ``n`` -- the rank - - ``k`` -- (default: 1) the twist + - ``n`` -- positive integer; the rank + - ``k`` -- positive integer (default: 1); the twist - ``q`` -- (optional) the parameter `q` - ``F`` -- (default: `\QQ(q)`) the base field that contains ``q`` From 41a6951ffb36b55ba20dc365a0e8789fc6bf9736 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 7 Jul 2021 11:07:28 +0200 Subject: [PATCH 222/336] set up prism with precomputed double description --- src/sage/geometry/polyhedron/base.py | 96 ++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index cf2e2f24ada..667d5061d76 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -3961,22 +3961,22 @@ def is_prism(self, certificate=False): sage: R = Q.prism() sage: R.is_prism(certificate=True) (True, - [[A vertex at (1, 0, 0, 0), + [[A vertex at (1, 6, 36, 216), + A vertex at (1, 0, 0, 0), + A vertex at (1, 7, 49, 343), + A vertex at (1, 5, 25, 125), A vertex at (1, 1, 1, 1), A vertex at (1, 2, 4, 8), - A vertex at (1, 3, 9, 27), A vertex at (1, 4, 16, 64), - A vertex at (1, 5, 25, 125), - A vertex at (1, 6, 36, 216), - A vertex at (1, 7, 49, 343)], - [A vertex at (0, 0, 0, 0), + A vertex at (1, 3, 9, 27)], + [A vertex at (0, 3, 9, 27), + A vertex at (0, 6, 36, 216), + A vertex at (0, 0, 0, 0), + A vertex at (0, 7, 49, 343), + A vertex at (0, 5, 25, 125), A vertex at (0, 1, 1, 1), A vertex at (0, 2, 4, 8), - A vertex at (0, 3, 9, 27), - A vertex at (0, 4, 16, 64), - A vertex at (0, 5, 25, 125), - A vertex at (0, 6, 36, 216), - A vertex at (0, 7, 49, 343)]]) + A vertex at (0, 4, 16, 64)]]) TESTS:: @@ -4100,7 +4100,7 @@ def gale_transform(self): sage: p = Polyhedron(vertices = [[0,0],[0,1],[1,0]]) sage: p2 = p.prism() sage: p2.gale_transform() - ((-1, -1), (1, 0), (0, 1), (1, 1), (-1, 0), (0, -1)) + ((-1, 0), (0, -1), (1, 1), (-1, -1), (1, 0), (0, 1)) REFERENCES: @@ -7905,14 +7905,72 @@ def prism(self): sage: polytopes.simplex(backend='cdd').prism().backend() 'cdd' """ - new_verts = [] - new_verts.extend( [ [0] + v for v in self.vertices()] ) - new_verts.extend( [ [1] + v for v in self.vertices()] ) - new_rays = [ [0] + r for r in self.rays()] - new_lines = [ [0] + l for l in self.lines()] - + from itertools import chain + new_verts = chain(([0] + v for v in self.vertices()), + ([1] + v for v in self.vertices())) + new_rays = ([0] + r for r in self.rays()) + new_lines = ([0] + l for l in self.lines()) + new_eqns = ([e.b()] + [0] + list(e[1:]) for e in self.equations()) + new_ieqs = chain([[0, 1] + [0]*self.ambient_dim(), [1, -1] + [0]*self.ambient_dim()], + ([i.b()] + [0] + list(i[1:]) for i in self.inequalities())) + + pref_rep = 'Hrep' if 2*(self.n_vertices() + self.n_rays()) >= self.n_inequalities() else 'Vrep' parent = self.parent().change_ring(self.base_ring(), ambient_dim=self.ambient_dim()+1) - return parent.element_class(parent, [new_verts, new_rays, new_lines], None) + + if not self.vertices(): + # Fix the empty polyhedron. + return parent.element_class(parent, [[], [], []], None) + + return parent.element_class(parent, [new_verts, new_rays, new_lines], + [new_ieqs, new_eqns], + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) + + def _test_prism(self, tester=None, **options): + """ + Run tests on the method :meth:`.prism`. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_prism() + """ + if tester is None: + tester = self._tester(**options) + + if self.n_vertices() + self.n_rays() < 40 and self.n_facets() < 40: + prism = self.prism() + + # Check that the double description is set up correctly. + if self.base_ring().is_exact() and self.n_vertices() + self.n_rays() < 15 and self.n_facets() < 15: + prism._test_basic_properties(tester) + + # Check that the prism preserves the backend. + tester.assertEqual(self.prism().backend(), self.backend()) + + tester.assertEqual(2*self.n_vertices(), prism.n_vertices()) + tester.assertEqual(self.n_rays(), prism.n_rays()) + tester.assertEqual(self.n_lines(), prism.n_lines()) + tester.assertEqual(self.n_equations(), prism.n_equations()) + if self.is_empty(): + return + + tester.assertEqual(2 + self.n_inequalities(), prism.n_inequalities()) + + if not self.is_compact(): + # ``is_prism`` only implemented for compact polyhedra. + return + + b, cert = prism.is_prism(certificate=True) + tester.assertTrue(b) + + if not self.is_prism() and self.base_ring().is_exact(): + # In this case the certificate is unique. + + R = self.base_ring() + cert_set = set(frozenset(tuple(v) for v in f) for f in cert) + expected_cert = set(frozenset((i,) + tuple(v) + for v in self.vertices()) + for i in (R(0), R(1))) + tester.assertEqual(cert_set, expected_cert) def one_point_suspension(self, vertex): """ From 0cfc14634cac3d85ba2aaa263019164b3889b938 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 7 Jul 2021 13:54:01 +0200 Subject: [PATCH 223/336] tiny improvements --- src/sage/geometry/polyhedron/base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 667d5061d76..9914971ea2c 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -5908,7 +5908,7 @@ def stack(self, face, position=None): sage: hexaprism = polytopes.regular_polygon(6).prism() sage: hexaprism.f_vector() (1, 12, 18, 8, 1) - sage: square_face = hexaprism.faces(2)[0] + sage: square_face = hexaprism.faces(2)[2] sage: stacked_hexaprism = hexaprism.stack(square_face) sage: stacked_hexaprism.f_vector() (1, 13, 22, 11, 1) @@ -7911,10 +7911,10 @@ def prism(self): new_rays = ([0] + r for r in self.rays()) new_lines = ([0] + l for l in self.lines()) new_eqns = ([e.b()] + [0] + list(e[1:]) for e in self.equations()) - new_ieqs = chain([[0, 1] + [0]*self.ambient_dim(), [1, -1] + [0]*self.ambient_dim()], - ([i.b()] + [0] + list(i[1:]) for i in self.inequalities())) + new_ieqs = chain(([i.b()] + [0] + list(i[1:]) for i in self.inequalities()), + [[0, 1] + [0]*self.ambient_dim(), [1, -1] + [0]*self.ambient_dim()]) - pref_rep = 'Hrep' if 2*(self.n_vertices() + self.n_rays()) >= self.n_inequalities() else 'Vrep' + pref_rep = 'Hrep' if 2*(self.n_vertices() + self.n_rays()) >= self.n_inequalities() + 2 else 'Vrep' parent = self.parent().change_ring(self.base_ring(), ambient_dim=self.ambient_dim()+1) if not self.vertices(): From f75726d37348b2eb745124934a054e1f2addba80 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 7 Jul 2021 14:25:56 +0200 Subject: [PATCH 224/336] only compute prism once --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 9914971ea2c..9d1486fd8f5 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7944,7 +7944,7 @@ def _test_prism(self, tester=None, **options): prism._test_basic_properties(tester) # Check that the prism preserves the backend. - tester.assertEqual(self.prism().backend(), self.backend()) + tester.assertEqual(prism.backend(), self.backend()) tester.assertEqual(2*self.n_vertices(), prism.n_vertices()) tester.assertEqual(self.n_rays(), prism.n_rays()) From b616de008f71abb7896306f26e0c9dae716f6018 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 7 Jul 2021 13:47:17 +0200 Subject: [PATCH 225/336] implement double description for bipyramid --- src/sage/geometry/polyhedron/base.py | 78 +++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index cf2e2f24ada..f16440afa8b 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7876,15 +7876,77 @@ def bipyramid(self): sage: polytopes.simplex(backend='cdd').bipyramid().backend() 'cdd' """ - new_verts = \ - [[ 0] + list(x) for x in self.vertex_generator()] + \ - [[ 1] + list(self.center())] + \ - [[-1] + list(self.center())] - new_rays = [[0] + r for r in self.rays()] - new_lines = [[0] + list(l) for l in self.lines()] - + c = self.center() + from itertools import chain + new_verts = chain(([0] + list(x) for x in self.vertex_generator()), + [[1] + list(c), [-1] + list(c)]) + new_rays = ([0] + r for r in self.rays()) + new_lines = ([0] + l for l in self.lines()) + new_ieqs = chain(([i.b()] + [ c*i.A() + i.b()] + list(i.A()) for i in self.inequalities()), + ([i.b()] + [-c*i.A() - i.b()] + list(i.A()) for i in self.inequalities())) + new_eqns = ([e.b()] + [0] + list(e.A()) for e in self.equations()) + + pref_rep = 'Hrep' if 2 + (self.n_vertices() + self.n_rays()) >= 2*self.n_inequalities() else 'Vrep' parent = self.parent().base_extend(self.center().parent(), ambient_dim=self.ambient_dim()+1) - return parent.element_class(parent, [new_verts, new_rays, new_lines], None) + + if c not in self.relative_interior(): + # Fix polyhedra with non-proper center. + return parent.element_class(parent, [new_verts, new_rays, new_lines], None) + + return parent.element_class(parent, [new_verts, new_rays, new_lines], + [new_ieqs, new_eqns], + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) + + def _test_bipyramid(self, tester=None, **options): + """ + Run tests on the method :meth:`.bipyramid`. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_bipyramid() + """ + if tester is None: + tester = self._tester(**options) + + if (self.n_vertices() + self.n_rays() >= 40 + or self.n_facets() >= 40 + or self.n_vertices() <= 1): + return + + bipyramid = self.bipyramid() + + # Check that the double description is set up correctly. + if self.base_ring().is_exact() and self.n_vertices() + self.n_rays() < 15 and self.n_facets() < 15: + bipyramid._test_basic_properties(tester) + + # Check that the bipyramid preserves the backend. + tester.assertEqual(self.bipyramid().backend(), self.backend()) + + if self.center() not in self.relative_interior(): + # In this case (unbounded) the bipyramid behaves a bit different. + return + + tester.assertEqual(2 + self.n_vertices(), bipyramid.n_vertices()) + tester.assertEqual(self.n_rays(), bipyramid.n_rays()) + tester.assertEqual(self.n_lines(), bipyramid.n_lines()) + tester.assertEqual(self.n_equations(), bipyramid.n_equations()) + tester.assertEqual(2*self.n_inequalities(), bipyramid.n_inequalities()) + + if not self.is_compact(): + # ``is_bipyramid`` is only implemented for compact polyhedra. + return + + b, cert = bipyramid.is_bipyramid(certificate=True) + tester.assertTrue(b) + + if not self.is_bipyramid() and self.base_ring().is_exact(): + # In this case the certificate is unique. + + R = self.base_ring() + a = (R(1),) + tuple(self.center()) + b = (R(-1),) + tuple(self.center()) + c, d = [tuple(v) for v in cert] + tester.assertEqual(sorted([a, b]), sorted([c, d])) def prism(self): """ From 30c429dcf242d6e0dcd5cc42fb2568386001b3dc Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 7 Jul 2021 14:25:33 +0200 Subject: [PATCH 226/336] only compute bipryamd once --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index f16440afa8b..4e1342d4ea1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7920,7 +7920,7 @@ def _test_bipyramid(self, tester=None, **options): bipyramid._test_basic_properties(tester) # Check that the bipyramid preserves the backend. - tester.assertEqual(self.bipyramid().backend(), self.backend()) + tester.assertEqual(bipyramid.backend(), self.backend()) if self.center() not in self.relative_interior(): # In this case (unbounded) the bipyramid behaves a bit different. From 2b1c64b047628dfd5179cb98f637da57bb74452f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 7 Jul 2021 14:24:36 +0200 Subject: [PATCH 227/336] double description for pyramid --- src/sage/geometry/polyhedron/base.py | 32 ++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index cf2e2f24ada..85b6e5fd899 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7788,13 +7788,25 @@ def pyramid(self): 'cdd' """ assert self.is_compact(), "Not a polytope." + c = self.center() - new_verts = \ - [[0] + x for x in self.Vrep_generator()] + \ - [[1] + list(self.center())] + from itertools import chain + new_verts = chain(([0] + x for x in self.Vrep_generator()), + [[1] + list(c)]) + new_ieqs = chain(([i.b()] + [-c*i.A() - i.b()] + list(i.A()) for i in self.inequalities()), + [[0, 1] + [0]*self.ambient_dim()]) + new_eqns = ([e.b()] + [0] + list(e.A()) for e in self.equations()) + pref_rep = 'Hrep' if self.n_vertices() > self.n_inequalities() else 'Vrep' parent = self.parent().base_extend(self.center().parent(), ambient_dim=self.ambient_dim()+1) - return parent.element_class(parent, [new_verts, [], []], None) + + if self.n_vertices() == 1: + # Fix the polyhedron with one vertex. + return parent.element_class(parent, [new_verts, [], []], None) + + return parent.element_class(parent, [new_verts, [], []], + [new_ieqs, new_eqns], + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) def _test_pyramid(self, tester=None, **options): """ @@ -7844,6 +7856,18 @@ def check_pyramid_certificate(P, cert): tester.assertTrue(pyr_polar.is_combinatorially_isomorphic(pyr_polar)) + # Basic properties of the pyramid. + + # Check that the prism preserves the backend. + tester.assertEqual(pyr.backend(), self.backend()) + + tester.assertEqual(1 + self.n_vertices(), pyr.n_vertices()) + tester.assertEqual(self.n_equations(), pyr.n_equations()) + tester.assertEqual(1 + self.n_inequalities(), pyr.n_inequalities()) + + if self.n_vertices() < 15 and self.n_facets() < 15: + pyr._test_basic_properties() + def bipyramid(self): """ Return a polyhedron that is a bipyramid over the original. From af7ca87f87075d79b9c7ccd2e59f8fcb8205e1e8 Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Wed, 7 Jul 2021 18:39:03 +0200 Subject: [PATCH 228/336] Refactor ExactTerm._repr_ to TermWithCoefficient._product_repr_ --- src/sage/rings/asymptotic/term_monoid.py | 95 ++++++++++++------------ 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 81e40c4e61e..047da7db3c2 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -1771,7 +1771,7 @@ def _element_constructor_(self, data, coefficient=None): sage: G = GrowthGroup('x^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: t1 = T(x^2, 5); t1 # indirect doctest - Term with coefficient 5 and growth x^2 + Term with coefficient 5*x^2 TESTS:: @@ -1782,23 +1782,23 @@ def _element_constructor_(self, data, coefficient=None): :: sage: T(G.gen()^10) - Term with coefficient 1 and growth x^10 + Term with coefficient x^10 sage: T(G.gen()^10, coefficient=10) - Term with coefficient 10 and growth x^10 + Term with coefficient 10*x^10 sage: T(x^123) - Term with coefficient 1 and growth x^123 + Term with coefficient x^123 :: sage: T(x) - Term with coefficient 1 and growth x + Term with coefficient x :: sage: G_log = GrowthGroup('log(x)^ZZ') sage: T_log = TermWithCoefficientMonoid(TermMonoid, G_log, ZZ) sage: T_log(log(x)) - Term with coefficient 1 and growth log(x) + Term with coefficient log(x) """ if isinstance(data, self.element_class) and data.parent() == self: @@ -2818,9 +2818,9 @@ class TermWithCoefficient(GenericTerm): sage: CT_ZZ = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: CT_QQ = TermWithCoefficientMonoid(TermMonoid, G, QQ) sage: CT_ZZ(x^2, 5) - Term with coefficient 5 and growth x^2 + Term with coefficient 5*x^2 sage: CT_QQ(x^3, 3/8) - Term with coefficient 3/8 and growth x^3 + Term with coefficient 3/8*x^3 """ def __init__(self, parent, growth, coefficient): @@ -2848,7 +2848,7 @@ def __init__(self, parent, growth, coefficient): ValueError: 1/2 is not a coefficient in Generic Term Monoid x^ZZ with (implicit) coefficients in Integer Ring. sage: t = CT_QQ(x, 1/2); t - Term with coefficient 1/2 and growth x + Term with coefficient 1/2*x For technical reasons, the coefficient 0 is not allowed:: @@ -2864,7 +2864,7 @@ def __init__(self, parent, growth, coefficient): sage: x = SR('x'); x.parent() Symbolic Ring sage: CT_ZZ(x^42, 42) - Term with coefficient 42 and growth x^42 + Term with coefficient 42*x^42 """ try: coefficient = parent.coefficient_ring(coefficient) @@ -2901,10 +2901,38 @@ def _repr_(self): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: T(x^2, 5)._repr_() - 'Term with coefficient 5 and growth x^2' + 'Term with coefficient 5*x^2' """ - return 'Term with coefficient %s and growth %s' % \ - (self.coefficient, self.growth) + return f'Term with coefficient {self._product_repr_()}' + + def _product_repr_(self, latex=False): + if latex: + from sage.misc.latex import latex as latex_repr + f = latex_repr + else: + f = repr + + g = f(self.growth) + c = f(self.coefficient) + + if g == '1': + return c + elif c == '1': + return '{g}'.format(g=g) + elif c == '-1': + return '-{g}'.format(g=g) + elif self.coefficient._is_atomic() or (-self.coefficient)._is_atomic(): + # note that -pi/2 is not atomic, but -5 is. As subtractions are handled + # in the asymptotic ring, we ignore such non-atomicity. + s = '{c} {g}' if latex else '{c}*{g}' + else: + s = r'\left({c}\right) {g}' if latex else '({c})*{g}' + s = s.format(c=c, g=g) + + if latex: + import re + s = re.sub(r'([0-9])\s+([0-9])', r'\1 \\cdot \2', s) + return s def _mul_(self, other): r""" @@ -2941,7 +2969,7 @@ def _mul_(self, other): sage: t1 = CT(x^2, 2); t2 = CT(x^3, 3) sage: t1 * t2 - Term with coefficient 6 and growth x^5 + Term with coefficient 6*x^5 And now, an example for exact terms:: @@ -2975,11 +3003,11 @@ def _calculate_pow_(self, exponent): sage: G = GrowthGroup('z^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: t = T('2*z'); t - Term with coefficient 2 and growth z + Term with coefficient 2*z sage: t._calculate_pow_(3) - Term with coefficient 8 and growth z^3 + Term with coefficient 8*z^3 sage: t._calculate_pow_(-2) - Term with coefficient 1/4 and growth z^(-2) + Term with coefficient 1/4*z^(-2) :: @@ -3088,7 +3116,7 @@ def _eq_(self, other): sage: T = TermWithCoefficientMonoid(TermMonoid, GrowthGroup('x^ZZ'), ZZ) sage: t = T.an_element(); t - Term with coefficient 1 and growth x + Term with coefficient x sage: t == T(x, 1) True sage: t == T(x, 2) @@ -3188,7 +3216,7 @@ def _an_element_(self): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G = GrowthGroup('x^ZZ') sage: TermWithCoefficientMonoid(TermMonoid, G, ZZ).an_element() # indirect doctest - Term with coefficient 1 and growth x + Term with coefficient x sage: TermMonoid('exact', G, ZZ).an_element() # indirect doctest x sage: TermMonoid('exact', G, QQ).an_element() # indirect doctest @@ -3337,34 +3365,7 @@ def _repr_(self, latex=False): sage: (1+a)/n (a + 1)*n^(-1) """ - if latex: - from sage.misc.latex import latex as latex_repr - f = latex_repr - else: - f = repr - - g = f(self.growth) - c = f(self.coefficient) - - if g == '1': - return c - elif c == '1': - return '{g}'.format(g=g) - elif c == '-1': - return '-{g}'.format(g=g) - elif self.coefficient._is_atomic() or (-self.coefficient)._is_atomic(): - # note that -pi/2 is not atomic, but -5 is. As subtractions are handled - # in the asymptotic ring, we ignore such non-atomicity. - s = '{c} {g}' if latex else '{c}*{g}' - else: - s = r'\left({c}\right) {g}' if latex else '({c})*{g}' - s = s.format(c=c, g=g) - - if latex: - import re - s = re.sub(r'([0-9])\s+([0-9])', r'\1 \\cdot \2', s) - - return s + return self._product_repr_(latex) def _latex_(self): r""" From 63c2cfee88ed2ded272139fd48733fdd6fab0526 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Jun 2021 11:39:42 -0700 Subject: [PATCH 229/336] ConvexSet_base._test_convex_set: Do not test _test_as_set_object here --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index efd6bfd3cd9..47f0a6a5871 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -427,7 +427,7 @@ def _test_convex_set(self, tester=None, **options): ....: return 42 ....: def ambient_dim(self): ....: return 91 - sage: TestSuite(FaultyConvexSet()).run(skip=('_test_pickling', '_test_contains')) + sage: TestSuite(FaultyConvexSet()).run(skip=('_test_pickling', '_test_contains', '_test_as_set_object')) Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -441,7 +441,7 @@ def _test_convex_set(self, tester=None, **options): ....: return QQ^3 ....: def ambient_dim(self): ....: return 3 - sage: TestSuite(BiggerOnTheInside()).run(skip=('_test_pickling', '_test_contains')) + sage: TestSuite(BiggerOnTheInside()).run(skip=('_test_pickling', '_test_contains', '_test_as_set_object')) Failure in _test_convex_set: ... The following tests failed: _test_convex_set From c3682ad600c3e7f24033a87a626bbd12f8ae1153 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 7 Jul 2021 22:44:52 +0200 Subject: [PATCH 230/336] initialize backend ppl from ppl_polyhedron --- src/sage/geometry/polyhedron/backend_ppl.py | 51 ++++++++++++++++++--- src/sage/geometry/polyhedron/base.py | 30 ++++++------ src/sage/geometry/polyhedron/parent.py | 27 ++++++++++- 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index 52d0f04a851..0075aef484c 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -2,6 +2,7 @@ The PPL (Parma Polyhedra Library) backend for polyhedral computations """ +from sage.structure.element import Element from sage.rings.all import ZZ from sage.rings.integer import Integer from sage.arith.functions import LCM_list @@ -34,6 +35,32 @@ class Polyhedron_ppl(Polyhedron_base): sage: TestSuite(p).run() """ + def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, **kwds): + """ + Initializes the polyhedron. + + See :class:`Polyhedron_normaliz` for a description of the input + data. + + TESTS:: + + sage: p = Polyhedron() + sage: TestSuite(p).run() + sage: p = Polyhedron(vertices=[(1, 1)], rays=[(0, 1)]) + sage: TestSuite(p).run() + sage: q = polytopes.cube() + sage: p = q.parent().element_class(q.parent(), None, None, q._ppl_polyhedron) + sage: TestSuite(p).run() + """ + if ppl_polyhedron: + if Hrep is not None or Vrep is not None: + raise ValueError("only one of Vrep, Hrep, or ppl_polyhedron can be different from None") + Element.__init__(self, parent=parent) + minimize = True if 'minimize' in kwds and kwds['minimize'] else False + self._init_from_ppl_polyhedron(ppl_polyhedron, minimize) + else: + Polyhedron_base.__init__(self, parent, Vrep, Hrep, **kwds) + def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbose=False): """ Construct polyhedron from V-representation data. @@ -87,11 +114,10 @@ def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbo dl = [ d*l_i for l_i in l ] gs.insert(line(Linear_Expression(dl, 0))) if gs.empty(): - self._ppl_polyhedron = C_Polyhedron(self.ambient_dim(), 'empty') + ppl_polyhedron = C_Polyhedron(self.ambient_dim(), 'empty') else: - self._ppl_polyhedron = C_Polyhedron(gs) - self._init_Vrepresentation_from_ppl(minimize) - self._init_Hrepresentation_from_ppl(minimize) + ppl_polyhedron = C_Polyhedron(gs) + self._init_from_ppl_polyhedron(ppl_polyhedron, minimize) def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): """ @@ -132,9 +158,22 @@ def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): A = deqn[1:] cs.insert(Linear_Expression(A, b) == 0) if cs.empty(): - self._ppl_polyhedron = C_Polyhedron(self.ambient_dim(), 'universe') + ppl_polyhedron = C_Polyhedron(self.ambient_dim(), 'universe') else: - self._ppl_polyhedron = C_Polyhedron(cs) + ppl_polyhedron = C_Polyhedron(cs) + self._init_from_ppl_polyhedron(ppl_polyhedron, minimize) + + def _init_from_ppl_polyhedron(self, ppl_polyhedron, minimize=True): + """ + Create the V-/Hrepresentation objects from the ppl polyhedron. + + TESTS:: + + sage: p = Polyhedron(backend='ppl') + sage: from sage.geometry.polyhedron.backend_ppl import Polyhedron_ppl + sage: Polyhedron_ppl._init_from_Hrepresentation(p, [], []) # indirect doctest + """ + self._ppl_polyhedron = ppl_polyhedron self._init_Vrepresentation_from_ppl(minimize) self._init_Hrepresentation_from_ppl(minimize) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index cf2e2f24ada..45e28afc373 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6044,29 +6044,29 @@ def wedge(self, face, width=1): sage: W2 = Q.wedge(Q.faces(2)[7]); W2 A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 9 vertices sage: W2.vertices() - (A vertex at (0, 1, 0, 1, 0), - A vertex at (0, 0, 1, 1, 0), - A vertex at (1, 0, 0, 1, -1), - A vertex at (1, 0, 0, 1, 1), - A vertex at (1, 0, 1, 0, 1), + (A vertex at (1, 1, 0, 0, 1), A vertex at (1, 1, 0, 0, -1), - A vertex at (0, 1, 1, 0, 0), + A vertex at (1, 0, 1, 0, 1), A vertex at (1, 0, 1, 0, -1), - A vertex at (1, 1, 0, 0, 1)) + A vertex at (1, 0, 0, 1, 1), + A vertex at (1, 0, 0, 1, -1), + A vertex at (0, 0, 1, 1, 0), + A vertex at (0, 1, 1, 0, 0), + A vertex at (0, 1, 0, 1, 0)) sage: W3 = Q.wedge(Q.faces(1)[11]); W3 A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 10 vertices sage: W3.vertices() - (A vertex at (0, 1, 0, 1, 0), - A vertex at (0, 0, 1, 1, 0), - A vertex at (1, 0, 0, 1, -1), - A vertex at (1, 0, 0, 1, 1), + (A vertex at (1, 1, 0, 0, -2), + A vertex at (1, 1, 0, 0, 2), + A vertex at (1, 0, 1, 0, -2), A vertex at (1, 0, 1, 0, 2), + A vertex at (1, 0, 0, 1, 1), + A vertex at (1, 0, 0, 1, -1), + A vertex at (0, 1, 0, 1, 0), A vertex at (0, 1, 1, 0, 1), - A vertex at (1, 0, 1, 0, -2), - A vertex at (1, 1, 0, 0, 2), - A vertex at (0, 1, 1, 0, -1), - A vertex at (1, 1, 0, 0, -2)) + A vertex at (0, 0, 1, 1, 0), + A vertex at (0, 1, 1, 0, -1)) sage: C_3_7 = polytopes.cyclic_polytope(3,7) sage: P_6 = polytopes.regular_polygon(6) diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 57f3a349bfc..8b365d8d487 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -614,7 +614,7 @@ def _element_constructor_polyhedron(self, polyhedron, **kwds): EXAMPLES:: sage: from sage.geometry.polyhedron.parent import Polyhedra - sage: P = Polyhedra(QQ, 3) + sage: P = Polyhedra(QQ, 3, backend='cdd') sage: p = Polyhedron(vertices=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) sage: p A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices @@ -1081,6 +1081,31 @@ class Polyhedra_ZZ_normaliz(Polyhedra_base): class Polyhedra_QQ_ppl(Polyhedra_base): Element = Polyhedron_QQ_ppl + def _element_constructor_polyhedron(self, polyhedron, **kwds): + """ + The element (polyhedron) constructor for the case of 1 argument, a polyhedron. + + Set up with the ``ppl_polyhedron`` of ``self``, if available. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.parent import Polyhedra + sage: P = Polyhedra(QQ, 3) + sage: p = Polyhedron(vertices=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) + sage: p + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: P(p) + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices + + sage: p = Polyhedron(vertices=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)], backend='cdd') + sage: P(p) + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices + """ + if polyhedron.backend() == "ppl": + return self._element_constructor_(None, None, ppl_polyhedron=polyhedron._ppl_polyhedron) + else: + return Polyhedra_base._element_constructor_polyhedron(self, polyhedron, **kwds) + class Polyhedra_QQ_normaliz(Polyhedra_base): Element = Polyhedron_QQ_normaliz From 9ac183438b80e55d6d994d61f5d92a7c5e056026 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 7 Jul 2021 14:03:56 -0700 Subject: [PATCH 231/336] src/sage/manifolds/chart.py: Add raw string marker --- src/sage/manifolds/chart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 332e4b56fc5..6c6c5d18923 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -410,7 +410,7 @@ def _parse_coordinates(cls, domain, coordinates): ((z1, z2), {'periods': (None, None)}) sage: Chart._parse_coordinates(M, 'z1 z2') ((z1, z2), {'periods': (None, None)}) - sage: Chart._parse_coordinates(M, ['z1:\zeta_1', r'z2:\zeta_2']) + sage: Chart._parse_coordinates(M, [r'z1:\zeta_1', r'z2:\zeta_2']) ((z1, z2), {'periods': (None, None)}) """ if isinstance(coordinates, str): From 5e81afa9584314b5fa9ed84ee54fcffa43a386c3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 7 Jul 2021 23:06:52 +0200 Subject: [PATCH 232/336] outsource ppl conversion --- src/sage/geometry/polyhedron/backend_ppl.py | 99 +++++++++++++++------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index 0075aef484c..1bf5f0b7484 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -91,28 +91,13 @@ def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbo gs = Generator_System() if vertices is None: vertices = [] for v in vertices: - d = LCM_list([denominator(v_i) for v_i in v]) - if d.is_one(): - gs.insert(point(Linear_Expression(v, 0))) - else: - dv = [ d*v_i for v_i in v ] - gs.insert(point(Linear_Expression(dv, 0), d)) + gs.insert(self._convert_generator_to_ppl(v, 2)) if rays is None: rays = [] for r in rays: - d = LCM_list([denominator(r_i) for r_i in r]) - if d.is_one(): - gs.insert(ray(Linear_Expression(r, 0))) - else: - dr = [ d*r_i for r_i in r ] - gs.insert(ray(Linear_Expression(dr, 0))) + gs.insert(self._convert_generator_to_ppl(r, 3)) if lines is None: lines = [] for l in lines: - d = LCM_list([denominator(l_i) for l_i in l]) - if d.is_one(): - gs.insert(line(Linear_Expression(l, 0))) - else: - dl = [ d*l_i for l_i in l ] - gs.insert(line(Linear_Expression(dl, 0))) + gs.insert(self._convert_generator_to_ppl(l, 4)) if gs.empty(): ppl_polyhedron = C_Polyhedron(self.ambient_dim(), 'empty') else: @@ -145,18 +130,10 @@ def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): cs = Constraint_System() if ieqs is None: ieqs = [] for ieq in ieqs: - d = LCM_list([denominator(ieq_i) for ieq_i in ieq]) - dieq = [ ZZ(d*ieq_i) for ieq_i in ieq ] - b = dieq[0] - A = dieq[1:] - cs.insert(Linear_Expression(A, b) >= 0) + cs.insert(self._convert_constraint_to_ppl(ieq, 0)) if eqns is None: eqns = [] for eqn in eqns: - d = LCM_list([denominator(eqn_i) for eqn_i in eqn]) - deqn = [ ZZ(d*eqn_i) for eqn_i in eqn ] - b = deqn[0] - A = deqn[1:] - cs.insert(Linear_Expression(A, b) == 0) + cs.insert(self._convert_constraint_to_ppl(eqn, 1)) if cs.empty(): ppl_polyhedron = C_Polyhedron(self.ambient_dim(), 'universe') else: @@ -263,6 +240,72 @@ def _init_empty_polyhedron(self): super(Polyhedron_ppl, self)._init_empty_polyhedron() self._ppl_polyhedron = C_Polyhedron(self.ambient_dim(), 'empty') + @staticmethod + def _convert_generator_to_ppl(v, typ): + r""" + Convert a generator to ``ppl``. + + INPUT: + + - ``v`` -- a vertex, ray, or line. + + - ``typ`` -- integer; 2 -- vertex; 3 -- ray; 4 -- line + + EXAMPLES:: + + sage: P = Polyhedron() + sage: P._convert_generator_to_ppl([1, 1/2, 3], 2) + point(2/2, 1/2, 6/2) + sage: P._convert_generator_to_ppl([1, 1/2, 3], 3) + ray(2, 1, 6) + sage: P._convert_generator_to_ppl([1, 1/2, 3], 4) + line(2, 1, 6) + """ + if typ == 2: + ob = point + elif typ == 3: + ob = ray + else: + ob = line + + d = LCM_list([denominator(v_i) for v_i in v]) + if d.is_one(): + return ob(Linear_Expression(v, 0)) + else: + dv = [ d*v_i for v_i in v ] + if typ == 2: + return ob(Linear_Expression(dv, 0), d) + else: + return ob(Linear_Expression(dv, 0)) + + @staticmethod + def _convert_constraint_to_ppl(c, typ): + r""" + Convert a constraint to ``ppl``. + + INPUT: + + - ``c`` -- an inequality or equation. + + - ``typ`` -- integer; 0 -- inequality; 3 -- equation + + EXAMPLES:: + + sage: P = Polyhedron() + sage: P._convert_constraint_to_ppl([1, 1/2, 3], 0) + x0+6*x1+2>=0 + sage: P._convert_constraint_to_ppl([1, 1/2, 3], 1) + x0+6*x1+2==0 + """ + d = LCM_list([denominator(c_i) for c_i in c]) + dc = [ ZZ(d*c_i) for c_i in c ] + b = dc[0] + A = dc[1:] + if typ == 0: + return Linear_Expression(A, b) >= 0 + else: + return Linear_Expression(A, b) == 0 + From 86be3ed3e9487b85934a018a5abb950428f5e8d7 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 8 Jul 2021 00:28:26 +0200 Subject: [PATCH 233/336] make ppl polyhedra lazy --- src/sage/geometry/polyhedron/backend_ppl.py | 90 +++++++++++++++++-- src/sage/geometry/polyhedron/base.py | 12 ++- .../geometry/polyhedron/base_interactive.py | 59 ++++++++++++ src/sage/geometry/polyhedron/constructor.py | 1 + src/sage/geometry/polyhedron/parent.py | 1 + 5 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 src/sage/geometry/polyhedron/base_interactive.py diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index 1bf5f0b7484..83b1e999781 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -7,7 +7,7 @@ from sage.rings.integer import Integer from sage.arith.functions import LCM_list from sage.misc.functional import denominator -from .base import Polyhedron_base +from .base_interactive import Polyhedron_interactive from .base_QQ import Polyhedron_QQ from .base_ZZ import Polyhedron_ZZ @@ -19,7 +19,7 @@ ######################################################################### -class Polyhedron_ppl(Polyhedron_base): +class Polyhedron_ppl(Polyhedron_interactive): """ Polyhedra with ppl @@ -35,6 +35,8 @@ class Polyhedron_ppl(Polyhedron_base): sage: TestSuite(p).run() """ + _backend_object_name = "ppl_polyhedron" + def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, **kwds): """ Initializes the polyhedron. @@ -59,7 +61,7 @@ def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, **kwds): minimize = True if 'minimize' in kwds and kwds['minimize'] else False self._init_from_ppl_polyhedron(ppl_polyhedron, minimize) else: - Polyhedron_base.__init__(self, parent, Vrep, Hrep, **kwds) + Polyhedron_interactive.__init__(self, parent, Vrep, Hrep, **kwds) def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbose=False): """ @@ -151,8 +153,52 @@ def _init_from_ppl_polyhedron(self, ppl_polyhedron, minimize=True): sage: Polyhedron_ppl._init_from_Hrepresentation(p, [], []) # indirect doctest """ self._ppl_polyhedron = ppl_polyhedron - self._init_Vrepresentation_from_ppl(minimize) - self._init_Hrepresentation_from_ppl(minimize) + + def Vrepresentation(self, index=None): + """ + Return the objects of the V-representation. Each entry is + either a vertex, a ray, or a line. + + See :mod:`sage.geometry.polyhedron.constructor` for a + definition of vertex/ray/line. + + INPUT: + + - ``index`` -- either an integer or ``None`` + + OUTPUT: + + The optional argument is an index running from ``0`` to + ``self.n_Vrepresentation()-1``. If present, the + V-representation object at the given index will be + returned. Without an argument, returns the list of all + V-representation objects. + + EXAMPLES:: + + sage: p = polytopes.cube() + sage: p.Vrepresentation(0) + sage: p = polytopes.cube() + sage: p.Vrepresentation(0) + A vertex at (1, -1, -1) + sage: p._clear_cache() + sage: p.Vrepresentation() + (A vertex at (1, -1, -1), + A vertex at (1, 1, -1), + A vertex at (1, 1, 1), + A vertex at (1, -1, 1), + A vertex at (-1, -1, 1), + A vertex at (-1, -1, -1), + A vertex at (-1, 1, -1), + A vertex at (-1, 1, 1)) + sage: TestSuite(p).run() + """ + if not hasattr(self, '_Vrepresentation'): + self._init_Vrepresentation_from_ppl(True) + if index is None: + return self._Vrepresentation + else: + return self._Vrepresentation[index] def _init_Vrepresentation_from_ppl(self, minimize): """ @@ -221,6 +267,40 @@ def _init_Hrepresentation_from_ppl(self, minimize): parent._make_Equation(self, (c.inhomogeneous_term(),) + c.coefficients()) self._Hrepresentation = tuple(self._Hrepresentation) + def Hrepresentation(self, index=None): + """ + Return the objects of the H-representation. Each entry is + either an inequality or a equation. + + INPUT: + + - ``index`` -- either an integer or ``None`` + + OUTPUT: + + The optional argument is an index running from ``0`` to + ``self.n_Hrepresentation()-1``. If present, the + H-representation object at the given index will be + returned. Without an argument, returns the list of all + H-representation objects. + + EXAMPLES:: + + sage: p = polytopes.hypercube(3) + sage: p.Hrepresentation(0) + An inequality (-1, 0, 0) x + 1 >= 0 + sage: p.Hrepresentation(0) == p.Hrepresentation()[0] + True + sage: p._clear_cache() + sage: TestSuite(p).run() + """ + if not hasattr(self, '_Hrepresentation'): + self._init_Hrepresentation_from_ppl(True) + if index is None: + return self._Hrepresentation + else: + return self._Hrepresentation[index] + def _init_empty_polyhedron(self): """ Initializes an empty polyhedron. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 45e28afc373..9b1e07ee26e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -733,7 +733,7 @@ def _richcmp_(self, other, op): sage: P > Q False """ - if self._Vrepresentation is None or other._Vrepresentation is None: + if self.Vrepresentation() is None or other.Vrepresentation() is None: raise RuntimeError('some V representation is missing') # make sure deleted polyhedra are not used in cache @@ -1706,10 +1706,10 @@ def Hrepresentation(self, index=None): EXAMPLES:: - sage: p = polytopes.hypercube(3) + sage: p = polytopes.hypercube(3, backend='field') sage: p.Hrepresentation(0) An inequality (-1, 0, 0) x + 1 >= 0 - sage: p.Hrepresentation(0) == p.Hrepresentation() [0] + sage: p.Hrepresentation(0) == p.Hrepresentation()[0] True """ if index is None: @@ -5637,7 +5637,11 @@ def intersection(self, other): new_eqns = self.equations() + other.equations() parent = self.parent() try: - return parent.element_class(parent, None, [new_ieqs, new_eqns]) + intersection = parent.element_class(parent, None, [new_ieqs, new_eqns]) + + # Force calculation of the vertices. + _ = intersection.n_vertices() + return intersection except TypeError as msg: if self.base_ring() is ZZ: parent = parent.base_extend(QQ) diff --git a/src/sage/geometry/polyhedron/base_interactive.py b/src/sage/geometry/polyhedron/base_interactive.py new file mode 100644 index 00000000000..a4de3ef7d71 --- /dev/null +++ b/src/sage/geometry/polyhedron/base_interactive.py @@ -0,0 +1,59 @@ +r""" +Base class for interactive and lazy polyhedra. +""" + +from .base import Polyhedron_base +from sage.misc.lazy_attribute import lazy_attribute + + +class Polyhedron_interactive(Polyhedron_base): + + def _clear_cache(self): + r""" + Clear the Vrepresentation and Hrepresentation data of ``self``. + + TESTS:: + + sage: P = polytopes.permutahedron(4) + sage: TestSuite(P).run() + sage: P._clear_cache() + sage: TestSuite(P).run() + """ + self.parent().recycle(self) + backend_object = self.__dict__["_" + self._backend_object_name] + del self.__dict__ + self.__dict__["_" + self._backend_object_name] = backend_object + + def Vrepresentation(self): + r""" + A derived class must overwrite such that it restores Vrepresentation + after clearing it. + + TESTS:: + + sage: from sage.geometry.polyhedron.base_interactive import Polyhedron_interactive + sage: p = polytopes.cube() + sage: Polyhedron_interactive.Vrepresentation(p) + Traceback (most recent call last): + ... + NotImplementedError: a derived class must implement this + """ + # A derived class must implemented it to recalculate, if necessary. + raise NotImplementedError("a derived class must implement this") + + def Hrepresentation(self): + r""" + A derived class must overwrite such that it restores Hrepresentation + after clearing it. + + TESTS:: + + sage: from sage.geometry.polyhedron.base_interactive import Polyhedron_interactive + sage: p = polytopes.cube() + sage: Polyhedron_interactive.Hrepresentation(p) + Traceback (most recent call last): + ... + NotImplementedError: a derived class must implement this + """ + # A derived class must implemented it to recalculate, if necessary. + raise NotImplementedError("a derived class must implement this") diff --git a/src/sage/geometry/polyhedron/constructor.py b/src/sage/geometry/polyhedron/constructor.py index 29d84875ac2..805e33b0405 100644 --- a/src/sage/geometry/polyhedron/constructor.py +++ b/src/sage/geometry/polyhedron/constructor.py @@ -505,6 +505,7 @@ def Polyhedron(vertices=None, rays=None, lines=None, ....: (2, 3, 1), (3, 1, 2), (3, 2, 1)], ....: rays=[[1, 1, 1]], lines=[[1, 2, 3]], backend='ppl', ....: base_ring=ZZ) + sage: Q.n_vertices() Traceback (most recent call last): ... TypeError: no conversion of this rational to integer diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 8b365d8d487..9a7cc3bef26 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -278,6 +278,7 @@ def recycle(self, polyhedron): TESTS:: sage: p = Polyhedron([(0,0),(1,0),(0,1)]) + sage: _ = p.vertices() sage: n = len(p.parent()._Vertex_pool) sage: p._delete() sage: len(p.parent()._Vertex_pool) - n From 0f602323e887305512a471771a575aef773fdc0b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 7 Jul 2021 17:01:58 -0700 Subject: [PATCH 234/336] ConditionSet: Do not sort the conditions, just use _stable_uniq --- src/sage/sets/condition_set.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index ae535c5fbb3..a1910b63e9c 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -17,6 +17,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets from sage.misc.cachefunc import cached_method +from sage.misc.misc import _stable_uniq from sage.symbolic.expression import is_Expression from sage.symbolic.callable import is_CallableSymbolicExpression from sage.symbolic.ring import SymbolicRing, SR, is_SymbolicVariable @@ -155,7 +156,7 @@ def __classcall_private__(cls, universe, *predicates, vars=None, names=None, cat else: other_predicates.append(predicate) - predicates = sorted(set(callable_symbolic_predicates)) + other_predicates + predicates = list(_stable_uniq(callable_symbolic_predicates + other_predicates)) if not other_predicates and not callable_symbolic_predicates: if names is None and category is None: @@ -400,7 +401,7 @@ def _sympy_(self): False sage: Interval = ConditionSet(RR, x >= -7, x <= 4, vars=[x]); Interval - { x ∈ Real Field with 53 bits of precision : x <= 4, x >= -7 } + { x ∈ Real Field with 53 bits of precision : x >= -7, x <= 4 } sage: Interval._sympy_() ConditionSet(x, (x >= -7) & (x <= 4), SageSet(Real Field with 53 bits of precision)) From 69d045a4556bc20f6b3d6aaf65c0e1241ecfbdc2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 7 Jul 2021 17:09:59 -0700 Subject: [PATCH 235/336] ConditionSet: In doctests, do not rename ZZ^2 etc. --- src/sage/sets/condition_set.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index a1910b63e9c..8119784a6b2 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -211,7 +211,8 @@ def _repr_(self): t sage: ZeroDimButNotNullary = ConditionSet(ZZ^0, t > 0, vars=("q")) sage: ZeroDimButNotNullary._repr_() - '{ q ∈ Ambient free module of rank 0 over the principal ideal domain Integer Ring : t > 0 }' + '{ q ∈ Ambient free module of rank 0 + over the principal ideal domain Integer Ring : t > 0 }' """ s = "{ " names = self.variable_names() @@ -323,9 +324,9 @@ def _call_predicate(self, predicate, element): sage: TripleDigits = ZZ^3 sage: predicate(x, y, z) = sqrt(x^2 + y^2 + z^2) < 12; predicate (x, y, z) |--> sqrt(x^2 + y^2 + z^2) < 12 - sage: TripleDigits.rename('ZZ^3') sage: SmallTriples = ConditionSet(ZZ^3, predicate); SmallTriples - { (x, y, z) ∈ ZZ^3 : sqrt(x^2 + y^2 + z^2) < 12 } + { (x, y, z) ∈ Ambient free module of rank 3 over the principal + ideal domain Integer Ring : sqrt(x^2 + y^2 + z^2) < 12 } sage: predicate = SmallTriples._predicates[0] sage: element = TripleDigits((1, 2, 3)) sage: SmallTriples._call_predicate(predicate, element) @@ -356,9 +357,9 @@ def _an_element_(self): sage: TripleDigits = ZZ^3 sage: predicate(x, y, z) = sqrt(x^2 + y^2 + z^2) < 12; predicate (x, y, z) |--> sqrt(x^2 + y^2 + z^2) < 12 - sage: TripleDigits.rename('ZZ^3') sage: SmallTriples = ConditionSet(ZZ^3, predicate); SmallTriples - { (x, y, z) ∈ ZZ^3 : sqrt(x^2 + y^2 + z^2) < 12 } + { (x, y, z) ∈ Ambient free module of rank 3 over the principal + ideal domain Integer Ring : sqrt(x^2 + y^2 + z^2) < 12 } sage: SmallTriples.an_element() # indirect doctest (1, 0, 0) """ @@ -389,9 +390,9 @@ def _sympy_(self): sage: predicate(x, y, z) = sqrt(x^2 + y^2 + z^2) < 12; predicate (x, y, z) |--> sqrt(x^2 + y^2 + z^2) < 12 - sage: (ZZ^3).rename('ZZ^3') sage: SmallTriples = ConditionSet(ZZ^3, predicate); SmallTriples - { (x, y, z) ∈ ZZ^3 : sqrt(x^2 + y^2 + z^2) < 12 } + { (x, y, z) ∈ Ambient free module of rank 3 over the principal + ideal domain Integer Ring : sqrt(x^2 + y^2 + z^2) < 12 } sage: ST = SmallTriples._sympy_(); ST ConditionSet((x, y, z), sqrt(x**2 + y**2 + z**2) < 12, ProductSet(Integers, Integers, Integers)) @@ -444,14 +445,14 @@ def intersection(self, X): EXAMPLES:: - sage: (ZZ^2).rename("ZZ^2"); (QQ^2).rename("QQ^2") sage: in_small_oblong(x, y) = x^2 + 3 * y^2 <= 42 sage: SmallOblongUniverse = ConditionSet(QQ^2, in_small_oblong) sage: SmallOblongUniverse - { (x, y) ∈ QQ^2 : x^2 + 3*y^2 <= 42 } + { (x, y) ∈ Vector space of dimension 2 over Rational Field : x^2 + 3*y^2 <= 42 } sage: parity_check(x, y) = abs(sin(pi/2*(x + y))) < 1/1000 sage: EvenUniverse = ConditionSet(ZZ^2, parity_check); EvenUniverse - { (x, y) ∈ ZZ^2 : abs(sin(1/2*pi*x + 1/2*pi*y)) < (1/1000) } + { (x, y) ∈ Ambient free module of rank 2 over the principal ideal + domain Integer Ring : abs(sin(1/2*pi*x + 1/2*pi*y)) < (1/1000) } sage: SmallOblongUniverse & EvenUniverse { (x, y) ∈ Free module of degree 2 and rank 2 over Integer Ring Echelon basis matrix: @@ -463,11 +464,11 @@ def intersection(self, X): sage: SmallMirrorUniverse = ConditionSet(QQ^2, in_small_oblong, vars=(y, x)) sage: SmallMirrorUniverse - { (y, x) ∈ QQ^2 : 3*x^2 + y^2 <= 42 } + { (y, x) ∈ Vector space of dimension 2 over Rational Field : 3*x^2 + y^2 <= 42 } sage: SmallOblongUniverse & SmallMirrorUniverse - { (x, y) ∈ QQ^2 : x^2 + 3*y^2 <= 42 } + { (x, y) ∈ Vector space of dimension 2 over Rational Field : x^2 + 3*y^2 <= 42 } sage: SmallMirrorUniverse & SmallOblongUniverse - { (y, x) ∈ QQ^2 : 3*x^2 + y^2 <= 42 } + { (y, x) ∈ Vector space of dimension 2 over Rational Field : 3*x^2 + y^2 <= 42 } """ if isinstance(X, ConditionSet): return ConditionSet(self.ambient().intersection(X.ambient()), From ecd49fb5900e8cc8139efc3c50b6f8aed14c16df Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 7 Jul 2021 18:15:28 -0700 Subject: [PATCH 236/336] build/bin/sage-dist-helpers, src/doc/en/developer/packaging.rst: Document sdh_pip_uninstall --- build/bin/sage-dist-helpers | 4 ++++ src/doc/en/developer/packaging.rst | 3 +++ 2 files changed, 7 insertions(+) diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index f849c65b5d9..09cfef8e5d5 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -64,6 +64,10 @@ # If $SAGE_DESTDIR is not set then the command is run with $SAGE_SUDO, if # set. # +# - sdh_pip_uninstall [...] +# +# Runs `pip uninstall` with the given arguments. If unsuccessful, it displays a warning. +# # - sdh_cmake [...] # # Runs `cmake` in the current directory with the given arguments, as well as diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index 5032d95744c..b26c5da1d25 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -411,6 +411,9 @@ begin with ``sdh_``, which stands for "Sage-distribution helper". creating a wheel file in ``dist/``, followed by ``sdh_store_and_pip_install_wheel`` (see below). +- ``sdh_pip_uninstall [...]``: Runs ``pip uninstall`` with the given arguments. + If unsuccessful, it displays a warning. + - ``sdh_store_and_pip_install_wheel .``: The current directory, indicated by the required argument ``.``, must have a subdirectory ``dist`` containing a unique wheel file (``*.whl``). From c3eeb69f78412ed0d8ad4c7aebdb834b512aad3c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 7 Jul 2021 18:16:51 -0700 Subject: [PATCH 237/336] build/bin/sage-spkg: Fix typo in comment, unindent a block --- build/bin/sage-spkg | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index 9415e4c9e03..7f390e3a0f2 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -446,15 +446,15 @@ fi # Extract the package ################################################################## - echo "Setting up build directory for $PKG_NAME" - cp -RLp "$PKG_SCRIPTS" "$PKG_NAME" - cd "$PKG_NAME" || exit $? +echo "Setting up build directory for $PKG_NAME" +cp -RLp "$PKG_SCRIPTS" "$PKG_NAME" +cd "$PKG_NAME" || exit $? - sage-uncompress-spkg -d src "$PKG_SRC" - if [ $? -ne 0 ]; then - error_msg "Error: failed to extract $PKG_SRC" - exit 1 - fi +sage-uncompress-spkg -d src "$PKG_SRC" +if [ $? -ne 0 ]; then + error_msg "Error: failed to extract $PKG_SRC" + exit 1 +fi echo "Finished extraction" @@ -535,7 +535,7 @@ __EOF__ INSTALLED_SCRIPTS="prerm piprm postrm" WRAPPED_SCRIPTS="build install check preinst postinst $INSTALLED_SCRIPTS" -# Prepare script for uninstallation of pacakges that use sdh_pip_install +# Prepare script for uninstallation of packages that use sdh_pip_install # or sdh_store_and_pip_install_wheel. echo 'sdh_pip_uninstall -r $SAGE_SPKG_SCRIPTS/$PKG_BASE/spkg-piprm-requirements.txt' > spkg-piprm.in From 4f3e1b44d6d9bda955f782ba515221e5372db897 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 8 Jul 2021 09:33:36 +0100 Subject: [PATCH 238/336] make the test robust --- src/sage/symbolic/integration/external.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 550406fa5a3..11d00cd8328 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -415,9 +415,9 @@ def fricas_integrator(expression, v, a=None, b=None, noPole=True): sage: var("a c d"); f = (I*a*tan(d*x + c) + a)*sec(d*x + c)^10 (a, c, d) - sage: integrate(f, x, algorithm="fricas") # optional - fricas - 1/315*(64512*I*a*e^(10*I*d*x + 10*I*c) + 53760*I*a*e^(8*I*d*x + 8*I*c) + 30720*I*a*e^(6*I*d*x + 6*I*c) + 11520*I*a*e^(4*I*d*x + 4*I*c) + 2560*I*a*e^(2*I*d*x + 2*I*c) + 256*I*a)/(d*e^(20*I*d*x + 20*I*c) + 10*d*e^(18*I*d*x + 18*I*c) + 45*d*e^(16*I*d*x + 16*I*c) + 120*d*e^(14*I*d*x + 14*I*c) + 210*d*e^(12*I*d*x + 12*I*c) + 252*d*e^(10*I*d*x + 10*I*c) + 210*d*e^(8*I*d*x + 8*I*c) + 120*d*e^(6*I*d*x + 6*I*c) + 45*d*e^(4*I*d*x + 4*I*c) + 10*d*e^(2*I*d*x + 2*I*c) + d) - + sage: ii = integrate(f, x, algorithm="fricas") # optional - fricas + sage: 1/315*(64512*I*a*e^(10*I*d*x + 10*I*c) + 53760*I*a*e^(8*I*d*x + 8*I*c) + 30720*I*a*e^(6*I*d*x + 6*I*c) + 11520*I*a*e^(4*I*d*x + 4*I*c) + 2560*I*a*e^(2*I*d*x + 2*I*c) + 256*I*a)/(d*e^(20*I*d*x + 20*I*c) + 10*d*e^(18*I*d*x + 18*I*c) + 45*d*e^(16*I*d*x + 16*I*c) + 120*d*e^(14*I*d*x + 14*I*c) + 210*d*e^(12*I*d*x + 12*I*c) + 252*d*e^(10*I*d*x + 10*I*c) + 210*d*e^(8*I*d*x + 8*I*c) + 120*d*e^(6*I*d*x + 6*I*c) + 45*d*e^(4*I*d*x + 4*I*c) + 10*d*e^(2*I*d*x + 2*I*c) + d) - ii # optional - fricas + 0 """ if not isinstance(expression, Expression): expression = SR(expression) From ca6be1ef774de472f912ad45fc4365b178a9c36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Jul 2021 14:03:37 +0200 Subject: [PATCH 239/336] get rid of EMBEDDED_MODE and server.support --- src/sage/databases/oeis.py | 17 +++-------- src/sage/databases/sql_db.py | 7 ++--- src/sage/graphs/graph_editor.py | 13 ++++----- src/sage/interfaces/maxima_abstract.py | 4 --- src/sage/interfaces/r.py | 9 +----- src/sage/misc/misc.py | 14 --------- src/sage/misc/pager.py | 12 ++------ src/sage/misc/sagedoc.py | 40 ++++++++++---------------- src/sage/misc/sageinspect.py | 35 ++++------------------ src/sage/misc/session.pyx | 19 ------------ src/sage/repl/interpreter.py | 14 --------- src/sage/server/support.py | 11 ------- 12 files changed, 36 insertions(+), 159 deletions(-) delete mode 100644 src/sage/server/support.py diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index 4ad63ab25d9..082d84a432e 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -170,7 +170,6 @@ from sage.misc.flatten import flatten from sage.misc.temporary_file import tmp_filename from sage.misc.unknown import Unknown -from sage.misc.misc import embedded from sage.misc.html import HtmlFragment from sage.repl.preparse import preparse @@ -1575,10 +1574,7 @@ def url_absolute(s): return re.sub(r'\"\/', '\"' + oeis_url, s) if browse is None: if format == 'guess': - if embedded(): - return self.links(format='html') - else: - return self.links(format='url') + return self.links(format='url') elif format == 'raw': return FancyTuple(self._field('H')) elif format == 'html': @@ -1840,15 +1836,10 @@ def show(self): 'links', 'formulas', 'examples', 'cross_references', 'programs', 'keywords', 'offsets', 'url', 'old_IDs', 'author', 'extensions_or_errors']: - if embedded() and s == 'links': + result = getattr(self, s)() + if result != '' and result != ('',) and result != (): print(re.sub('_', ' ', s).upper()) - getattr(self, s)() - print('\n') - else: - result = getattr(self, s)() - if result != '' and result != ('',) and result != (): - print(re.sub('_', ' ', s).upper()) - print(str(result) + '\n') + print(str(result) + '\n') def programs(self, language='all', preparsing=True, keep_comments=False): r""" diff --git a/src/sage/databases/sql_db.py b/src/sage/databases/sql_db.py index 6189e4d3d91..5f07f2fed38 100644 --- a/src/sage/databases/sql_db.py +++ b/src/sage/databases/sql_db.py @@ -363,8 +363,8 @@ def row_str(row, html): + '\n' p += 1 else: - raise NotImplementedError('Cannot display plot on ' \ - + 'command line.') + raise NotImplementedError('Cannot display plot on ' + 'command line.') else: if index in fcol_index: if id_col_index is None: @@ -382,8 +382,7 @@ def row_str(row, html): cur_str.append(field_val) return ' '.join(cur_str) - from sage.server.support import EMBEDDED_MODE - if EMBEDDED_MODE or ('html_table' in kwds and kwds['html_table']): + if 'html_table' in kwds and kwds['html_table']: # Notebook Version ret = '\n' ret += ' \n' diff --git a/src/sage/graphs/graph_editor.py b/src/sage/graphs/graph_editor.py index 1d6830535a6..e56c443b8f5 100644 --- a/src/sage/graphs/graph_editor.py +++ b/src/sage/graphs/graph_editor.py @@ -1,7 +1,7 @@ r""" Graph editor """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009 Radoslav Kirov # # Distributed under the terms of the GNU General Public License (GPL) @@ -13,15 +13,13 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import sys from .graph_generators import graphs from sage.misc.html import html -from sage.server.support import EMBEDDED_MODE - def graph_to_js(g): """ @@ -108,10 +106,9 @@ def graph_editor(graph=None, graph_name=None, if graph is None: graph = graphs.CompleteGraph(2) - if not EMBEDDED_MODE: - return "This graph editor only runs in the Sage notebook." + return "This graph editor only runs in the deprecated Sage notebook." - graph.layout(save_pos = True, **layout_options) + graph.layout(save_pos=True, **layout_options) if graph_name is None: graph_name = '' diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index d8102801ba8..ecd790c7571 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -62,7 +62,6 @@ from sage.misc.misc import ECL_TMP from sage.misc.multireplace import multiple_replace from sage.structure.richcmp import richcmp, rich_to_bool -import sage.server.support from .interface import (Interface, InterfaceElement, InterfaceFunctionElement, InterfaceFunction, AsciiArtString) @@ -166,9 +165,6 @@ def _command_runner(self, command, s, redirect=True): ... """ cmd = '{} --very-quiet --batch-string="{}({});" '.format(MAXIMA, command, s) - if sage.server.support.EMBEDDED_MODE: - cmd += '< /dev/null' - env = os.environ.copy() env['TMPDIR'] = str(ECL_TMP) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 1544af8026b..211956848a5 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -635,12 +635,8 @@ def png(self, *args, **kwds): #Check to see if R has PNG support s = self.eval('capabilities("png")') t = self.eval('capabilities("aqua")') - if "TRUE" not in s+t: + if "TRUE" not in s + t: raise RuntimeError("R was not compiled with PNG support") - - from sage.server.support import EMBEDDED_MODE - if EMBEDDED_MODE: - self.setwd('"%s"'%os.path.abspath('.')) return RFunction(self, 'png')(*args, **kwds) def convert_r_list(self, l): @@ -1310,9 +1306,6 @@ def plot(self, *args, **kwds): """ # We have to define this to override the plot function defined in the # superclass. - from sage.server.support import EMBEDDED_MODE - if EMBEDDED_MODE: - self.setwd('"%s"'%os.path.abspath('.')) RFunction(self, 'plot')(*args, **kwds) return RFunction(self, 'dev.off')() diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index 4580f836f18..16c829b64c6 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -45,7 +45,6 @@ import warnings import sage.misc.prandom as random from .lazy_string import lazy_string -import sage.server.support from sage.misc.lazy_import import lazy_import @@ -1268,19 +1267,6 @@ def pad_zeros(s, size=3): return "0" * (size - len(str(s))) + str(s) -def embedded(): - """ - Return ``True`` if this copy of Sage is running embedded in the Sage - notebook. - - EXAMPLES:: - - sage: sage.misc.misc.embedded() # output True if in the notebook - False - """ - return sage.server.support.EMBEDDED_MODE - - def is_in_string(line, pos): r""" Return ``True`` if the character at position ``pos`` in ``line`` occurs diff --git a/src/sage/misc/pager.py b/src/sage/misc/pager.py index 26e9c33bd74..84833d6135a 100644 --- a/src/sage/misc/pager.py +++ b/src/sage/misc/pager.py @@ -1,7 +1,7 @@ """ Pager for showing strings -Currently we just use the IPython pager when not in embedded mode. +Currently we just use the IPython pager. If we want to use something else, we can just change this function. Any code in sage that uses a pager should use this pager. @@ -15,12 +15,6 @@ # --------------------------------------------------------------------------- -EMBEDDED_MODE = False - - def pager(): - if EMBEDDED_MODE: - return print - else: - import IPython.core.page - return IPython.core.page.page + import IPython.core.page + return IPython.core.page.page diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 33f28ada452..49926223de7 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -698,13 +698,12 @@ def format(s, embedded=False): else: first_line = s # Moreover, we must strip blank space in order to get the directives - directives = [ d.strip().lower() for d in first_line.split(',') ] + directives = [d.strip().lower() for d in first_line.split(',')] if 'noreplace' in directives or 'nodetex' in directives: - s = s[first_newline+len(os.linesep):] + s = s[first_newline + len(os.linesep):] import sage.all - import sage.server.support docs = set([]) if 'noreplace' not in directives: i_0 = 0 @@ -945,22 +944,17 @@ def _search_src_or_doc(what, string, extra1='', extra2='', extra3='', html_results = format_search_as_html(title, results, [string] + extras) - from sage.server.support import EMBEDDED_MODE - if EMBEDDED_MODE: - # Running from the legacy Sage Notebook - print(html_results) - else: - # Pass through the IPython pager in a mime bundle - from IPython.core.page import page - if not isinstance(text_results, str): - text_results = text_results.decode('utf-8', 'replace') + # Pass through the IPython pager in a mime bundle + from IPython.core.page import page + if not isinstance(text_results, str): + text_results = text_results.decode('utf-8', 'replace') - page({ - 'text/plain': text_results, - # 'text/html': html_results # don't return HTML results since - # they currently are not correctly - # formatted for Jupyter use - }) + page({ + 'text/plain': text_results, + # 'text/html': html_results # don't return HTML results since + # they currently are not correctly + # formatted for Jupyter use + }) def search_src(string, extra1='', extra2='', extra3='', extra4='', @@ -1422,8 +1416,8 @@ def __call__(self, obj, output='html', view=True): """ if output != 'html' and view: view = False - # much of the following is taken from 'docstring' in server/support.py - s = '' + + s = '' newline = "\n\n" # blank line to start new paragraph try: @@ -1551,11 +1545,7 @@ def _open(self, name, testing=False): if testing: return (url, path) - from sage.server.support import EMBEDDED_MODE - if EMBEDDED_MODE: - os.system(browser() + " " + url) - else: - os.system(browser() + " " + path) + os.system(browser() + " " + path) def tutorial(self): """ diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 35125721f27..cbb4483b006 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -119,7 +119,6 @@ def foo(unsigned int x=1, a=')"', b={not (2+1==3):'bar'}, *args, **kwds): return import os import tokenize import re -EMBEDDED_MODE = False from sage.env import SAGE_LIB try: @@ -1803,33 +1802,11 @@ def sage_getdef(obj, obj_name=''): # sometimes s contains "*args" or "**keywds", and the # asterisks confuse ReST/sphinx/docutils, so escape them: # change * to \*, and change ** to \**. - if EMBEDDED_MODE: - s = s.replace('**', '\\**') # replace ** with \** - t = '' - while True: # replace * with \* - i = s.find('*') - if i == -1: - break - elif i > 0 and s[i-1] == '\\': - if s[i+1] == "*": - t += s[:i+2] - s = s[i+2:] - else: - t += s[:i+1] - s = s[i+1:] - continue - elif i > 0 and s[i-1] == '*': - t += s[:i+1] - s = s[i+1:] - continue - else: - t += s[:i] + '\\*' - s = s[i+1:] - s = t + s return obj_name + '(' + s + ')' except (AttributeError, TypeError, ValueError): return '%s( [noargspec] )'%obj_name + def _sage_getdoc_unformatted(obj): r""" Return the unformatted docstring associated to ``obj`` as a @@ -1989,17 +1966,15 @@ def sage_getdoc_original(obj): return s -def sage_getdoc(obj, obj_name='', embedded_override=False): +def sage_getdoc(obj, obj_name='', embeddeded=False): r""" Return the docstring associated to ``obj`` as a string. If ``obj`` is a Cython object with an embedded position in its docstring, the embedded position is stripped. - If optional argument ``embedded_override`` is False (its default - value), then the string is formatted according to the value of - EMBEDDED_MODE. If this argument is True, then it is formatted as - if EMBEDDED_MODE were True. + The optional boolean argument ``embeddeded`` controls the + string formatting. It is False by default. INPUT: @@ -2030,7 +2005,7 @@ def sage_getdoc(obj, obj_name='', embedded_override=False): if obj is None: return '' r = sage_getdoc_original(obj) - s = sage.misc.sagedoc.format(r, embedded=(embedded_override or EMBEDDED_MODE)) + s = sage.misc.sagedoc.format(r, embedded=embedded) # Fix object naming if obj_name != '': diff --git a/src/sage/misc/session.pyx b/src/sage/misc/session.pyx index 2036e7f3db5..610dbdc0f2e 100644 --- a/src/sage/misc/session.pyx +++ b/src/sage/misc/session.pyx @@ -67,7 +67,6 @@ import types cdef caller_locals = builtins.locals # Sage imports -from .misc import embedded from sage.misc.persist import load, save, loads, dumps # This module-scope variables is used to save the @@ -326,17 +325,6 @@ def save_session(name='sage_session', verbose=False): print("Not saving {}: {}".format(k, msg)) pass save(D, name) - if embedded(): - # Also save D to the data directory if we're using the notebook. - # This is broken for now. Simply print some information to the user - # if the user does not save it in the DATA directory. - # save(D, '../../data/' + name) - if name.find('.sagenb/') <= 0 or name.find('/data/') <= 0: - print("To store the session in a common directory that the " - "entire worksheet can access, save it using the command:\n" - "save_session(DATA + '{0}')\n" - "You can later load it by running in any cell:\n" - "load_session(DATA + '{0}')".format(name.rsplit('/', 1)[-1])) def load_session(name='sage_session', verbose=False): @@ -382,13 +370,6 @@ def load_session(name='sage_session', verbose=False): """ state = caller_locals() - if embedded(): - if not os.path.exists(name): - nm = '../../data/' + name - if not nm.endswith('.sobj'): nm += '.sobj' - if os.path.exists(nm): - name = nm D = load(name) for k, x in D.items(): state[k] = x - diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py index f3234b2000d..71dbe429fdd 100644 --- a/src/sage/repl/interpreter.py +++ b/src/sage/repl/interpreter.py @@ -154,20 +154,6 @@ from IPython.core.crashhandler import CrashHandler -def embedded(): - """ - Return True if Sage is being run from the notebook. - - EXAMPLES:: - - sage: from sage.repl.interpreter import embedded - sage: embedded() - False - """ - import sage.server.support - return sage.server.support.EMBEDDED_MODE - - # TODO: This global variable _do_preparse should be associated with an # IPython InteractiveShell as opposed to a global variable in this # module. diff --git a/src/sage/server/support.py b/src/sage/server/support.py deleted file mode 100644 index ac7b5ee714b..00000000000 --- a/src/sage/server/support.py +++ /dev/null @@ -1,11 +0,0 @@ -""" -The EMBEDDED_MODE variable - -TESTS:: - - sage: from sage.server.support import EMBEDDED_MODE - sage: EMBEDDED_MODE - False -""" - -EMBEDDED_MODE = False From 51248442053ebf1ead7ef97d6276f030795a7026 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 8 Jul 2021 14:38:32 +0200 Subject: [PATCH 240/336] add _element_constructore_polyhedron for ppl over ZZ --- src/sage/geometry/polyhedron/parent.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 8b365d8d487..f2be3b68d09 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -1075,6 +1075,31 @@ def _make_Line(self, polyhedron, data): class Polyhedra_ZZ_ppl(Polyhedra_base): Element = Polyhedron_ZZ_ppl + def _element_constructor_polyhedron(self, polyhedron, **kwds): + """ + The element (polyhedron) constructor for the case of 1 argument, a polyhedron. + + Set up with the ``ppl_polyhedron`` of ``self``, if available. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.parent import Polyhedra + sage: P = Polyhedra(ZZ, 3) + sage: p = Polyhedron(vertices=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)], base_ring=QQ) + sage: p + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices + sage: P(p) + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices + + sage: p = Polyhedron(vertices=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)], backend='cdd') + sage: P(p) + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices + """ + if polyhedron.backend() == "ppl": + return self._element_constructor_(None, None, ppl_polyhedron=polyhedron._ppl_polyhedron) + else: + return Polyhedra_base._element_constructor_polyhedron(self, polyhedron, **kwds) + class Polyhedra_ZZ_normaliz(Polyhedra_base): Element = Polyhedron_ZZ_normaliz From 7c003dba37f63144d36ec37109deb5b9dadeb9eb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 8 Jul 2021 07:52:05 -0700 Subject: [PATCH 241/336] Chart.__init__: Restore default of calc_method argument for consistency --- src/sage/manifolds/chart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 5d6e0547891..99bdf35cb21 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -323,7 +323,7 @@ def __classcall__(cls, domain, coordinates='', domain._charts_by_coord[coord_string] = self return self - def __init__(self, domain, coordinates, calc_method, periods=None, coord_restrictions=None): + def __init__(self, domain, coordinates, calc_method=None, periods=None, coord_restrictions=None): r""" Construct a chart. From f85e71030990cea8ba757facbda9e725e11c47fe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 8 Jul 2021 08:01:31 -0700 Subject: [PATCH 242/336] Chart: in the description of the argument coord_restrictions, replace all instances of 'restrictions' by 'coord_restrictions' --- src/sage/manifolds/chart.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 99bdf35cb21..70fc558bd04 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -87,15 +87,16 @@ class Chart(UniqueRepresentation, SageObject): - ``coord_restrictions``: Additional restrictions on the coordinates. A restriction can be any symbolic equality or inequality involving the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items - of the list (or set or frozenset) ``restrictions`` are combined + of the list (or set or frozenset) ``coord_restrictions`` are combined with the ``and`` operator; if some restrictions are to be combined with the ``or`` operator instead, they have to be passed as a tuple in some - single item of the list (or set or frozenset) ``restrictions``. For example:: + single item of the list (or set or frozenset) ``coord_restrictions``. + For example:: coord_restrictions=[x > y, (x != 0, y != 0), z^2 < x] means ``(x > y) and ((x != 0) or (y != 0)) and (z^2 < x)``. - If the list ``restrictions`` contains only one item, this + If the list ``coord_restrictions`` contains only one item, this item can be passed as such, i.e. writing ``x > y`` instead of the single element list ``[x > y]``. If the chart variables have not been declared as variables yet, ``coord_restrictions`` must From c82c14410cb123c79cf5107865864298c1be94d0 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 8 Jul 2021 17:01:20 +0200 Subject: [PATCH 243/336] remove copy paste typo --- src/sage/geometry/polyhedron/backend_ppl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index 1bf5f0b7484..f23a728b995 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -39,7 +39,7 @@ def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, **kwds): """ Initializes the polyhedron. - See :class:`Polyhedron_normaliz` for a description of the input + See :class:`Polyhedron_ppl` for a description of the input data. TESTS:: From 80f6195ad1e9156516a45fdefdc183661b8c897c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 8 Jul 2021 08:05:29 -0700 Subject: [PATCH 244/336] Chart, RealChart: In class docstring, order arguments as they appear in __classcall__/__init__ --- src/sage/manifolds/chart.py | 51 ++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 70fc558bd04..4ab90986aa6 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -84,6 +84,19 @@ class Chart(UniqueRepresentation, SageObject): If no period and no LaTeX spelling are to be set for any coordinate, the argument ``coordinates`` can be omitted when the shortcut operator ``<,>`` is used to declare the chart (see examples below). + - ``calc_method`` -- (default: ``None``) string defining the calculus + method for computations involving coordinates of the chart; must be + one of + + - ``'SR'``: Sage's default symbolic engine (Symbolic Ring) + - ``'sympy'``: SymPy + - ``None``: the default of + :class:`~sage.manifolds.calculus_method.CalculusMethod` will be + used + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used) - ``coord_restrictions``: Additional restrictions on the coordinates. A restriction can be any symbolic equality or inequality involving the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items @@ -101,19 +114,6 @@ class Chart(UniqueRepresentation, SageObject): of the single element list ``[x > y]``. If the chart variables have not been declared as variables yet, ``coord_restrictions`` must be ``lambda``-quoted. - - ``names`` -- (default: ``None``) unused argument, except if - ``coordinates`` is not provided; it must then be a tuple containing - the coordinate symbols (this is guaranteed if the shortcut operator - ``<,>`` is used) - - ``calc_method`` -- (default: ``None``) string defining the calculus - method for computations involving coordinates of the chart; must be - one of - - - ``'SR'``: Sage's default symbolic engine (Symbolic Ring) - - ``'sympy'``: SymPy - - ``None``: the default of - :class:`~sage.manifolds.calculus_method.CalculusMethod` will be - used EXAMPLES: @@ -1474,10 +1474,6 @@ class RealChart(Chart): If interval range, no period and no LaTeX spelling are to be set for any coordinate, the argument ``coordinates`` can be omitted when the shortcut operator ``<,>`` is used to declare the chart (see examples below). - - ``names`` -- (default: ``None``) unused argument, except if - ``coordinates`` is not provided; it must then be a tuple containing - the coordinate symbols (this is guaranteed if the shortcut operator - ``<,>`` is used) - ``calc_method`` -- (default: ``None``) string defining the calculus method for computations involving coordinates of the chart; must be one of @@ -1487,6 +1483,27 @@ class RealChart(Chart): - ``None``: the default of :class:`~sage.manifolds.calculus_method.CalculusMethod` will be used + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used) + - ``coord_restrictions``: Additional restrictions on the coordinates. + A restriction can be any symbolic equality or inequality involving + the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items + of the list (or set or frozenset) ``coord_restrictions`` are combined + with the ``and`` operator; if some restrictions are to be combined with + the ``or`` operator instead, they have to be passed as a tuple in some + single item of the list (or set or frozenset) ``coord_restrictions``. + For example:: + + coord_restrictions=[x > y, (x != 0, y != 0), z^2 < x] + + means ``(x > y) and ((x != 0) or (y != 0)) and (z^2 < x)``. + If the list ``coord_restrictions`` contains only one item, this + item can be passed as such, i.e. writing ``x > y`` instead + of the single element list ``[x > y]``. If the chart variables have + not been declared as variables yet, ``coord_restrictions`` must + be ``lambda``-quoted. EXAMPLES: From a39e6fcd968af4a05c777357cdd470e61c244114 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 8 Jul 2021 08:08:20 -0700 Subject: [PATCH 245/336] DiffChart, RealDiffChart: In class docstring, order arguments as they appear in __classcall__/__init__; add description of argument coord_restrictions --- src/sage/manifolds/differentiable/chart.py | 50 ++++++++++++++++++---- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 5466062424a..bfc8198ef1e 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -72,10 +72,6 @@ class DiffChart(Chart): If no period and no LaTeX spelling are to be set for any coordinate, the argument ``coordinates`` can be omitted when the shortcut operator ``<,>`` is used to declare the chart (see examples below). - - ``names`` -- (default: ``None``) unused argument, except if - ``coordinates`` is not provided; it must then be a tuple containing - the coordinate symbols (this is guaranteed if the shortcut operator - ``<,>`` is used). - ``calc_method`` -- (default: ``None``) string defining the calculus method for computations involving coordinates of the chart; must be one of @@ -85,6 +81,27 @@ class DiffChart(Chart): - ``None``: the default of :class:`~sage.manifolds.calculus_method.CalculusMethod` will be used + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used). + - ``coord_restrictions``: Additional restrictions on the coordinates. + A restriction can be any symbolic equality or inequality involving + the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items + of the list (or set or frozenset) ``coord_restrictions`` are combined + with the ``and`` operator; if some restrictions are to be combined with + the ``or`` operator instead, they have to be passed as a tuple in some + single item of the list (or set or frozenset) ``coord_restrictions``. + For example:: + + coord_restrictions=[x > y, (x != 0, y != 0), z^2 < x] + + means ``(x > y) and ((x != 0) or (y != 0)) and (z^2 < x)``. + If the list ``coord_restrictions`` contains only one item, this + item can be passed as such, i.e. writing ``x > y`` instead + of the single element list ``[x > y]``. If the chart variables have + not been declared as variables yet, ``coord_restrictions`` must + be ``lambda``-quoted. EXAMPLES: @@ -709,10 +726,6 @@ class RealDiffChart(DiffChart, RealChart): If interval range, no period and no LaTeX spelling are to be set for any coordinate, the argument ``coordinates`` can be omitted when the shortcut operator ``<,>`` is used to declare the chart (see examples below). - - ``names`` -- (default: ``None``) unused argument, except if - ``coordinates`` is not provided; it must then be a tuple containing - the coordinate symbols (this is guaranteed if the shortcut operator - ``<,>`` is used). - ``calc_method`` -- (default: ``None``) string defining the calculus method for computations involving coordinates of the chart; must be one of @@ -722,6 +735,27 @@ class RealDiffChart(DiffChart, RealChart): - ``None``: the default of :class:`~sage.manifolds.calculus_method.CalculusMethod` will be used + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used). + - ``coord_restrictions``: Additional restrictions on the coordinates. + A restriction can be any symbolic equality or inequality involving + the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items + of the list (or set or frozenset) ``coord_restrictions`` are combined + with the ``and`` operator; if some restrictions are to be combined with + the ``or`` operator instead, they have to be passed as a tuple in some + single item of the list (or set or frozenset) ``coord_restrictions``. + For example:: + + coord_restrictions=[x > y, (x != 0, y != 0), z^2 < x] + + means ``(x > y) and ((x != 0) or (y != 0)) and (z^2 < x)``. + If the list ``coord_restrictions`` contains only one item, this + item can be passed as such, i.e. writing ``x > y`` instead + of the single element list ``[x > y]``. If the chart variables have + not been declared as variables yet, ``coord_restrictions`` must + be ``lambda``-quoted. EXAMPLES: From cdf20b04b24497e66279b5b745f4ef1ca9e7b555 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 8 Jul 2021 08:14:08 -0700 Subject: [PATCH 246/336] TopologicalManifold.chart: Add description of argument coord_restrictions --- src/sage/manifolds/manifold.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 8979be0c5b9..8a72a3ed505 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -1462,6 +1462,8 @@ def chart(self, coordinates='', names=None, calc_method=None, - ``'sympy'``: SymPy - ``None``: the current calculus method defined on the manifold is used (cf. :meth:`set_calculus_method`) + - ``coord_restrictions``: Additional restrictions on the coordinates. + See below. The coordinates declared in the string ``coordinates`` are separated by ``' '`` (whitespace) and each coordinate has at most four @@ -1494,6 +1496,26 @@ def chart(self, coordinates='', names=None, calc_method=None, omitted when the shortcut operator ``<,>`` is used to declare the chart (see examples below). + Additional restrictions on the coordinates can be set using the + argument ``coord_restrictions``. + + A restriction can be any symbolic equality or inequality involving + the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items + of the list (or set or frozenset) ``coord_restrictions`` are combined + with the ``and`` operator; if some restrictions are to be combined with + the ``or`` operator instead, they have to be passed as a tuple in some + single item of the list (or set or frozenset) ``coord_restrictions``. + For example:: + + coord_restrictions=[x > y, (x != 0, y != 0), z^2 < x] + + means ``(x > y) and ((x != 0) or (y != 0)) and (z^2 < x)``. + If the list ``coord_restrictions`` contains only one item, this + item can be passed as such, i.e. writing ``x > y`` instead + of the single element list ``[x > y]``. If the chart variables have + not been declared as variables yet, ``coord_restrictions`` must + be ``lambda``-quoted. + OUTPUT: - the created chart, as an instance of From 741fd2e44276baaad155e2af6b9f2e7b4c1d9b82 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 8 Jul 2021 08:29:42 -0700 Subject: [PATCH 247/336] TopologicalManifold.chart: Add an example of using coord_restrictions --- src/sage/manifolds/manifold.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 8a72a3ed505..e47703943f1 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -1583,6 +1583,16 @@ def chart(self, coordinates='', names=None, calc_method=None, sage: latex(P) \left(U,(r, {\phi})\right) + Using ``coord_restrictions``:: + + sage: D = Manifold(2, 'D', structure='topological') + sage: X. = D.chart(coord_restrictions=lambda x,y: [x^2+y^2<1, x>0]); X + Chart (D, (x, y)) + sage: X.valid_coordinates(0, 0) + False + sage: X.valid_coordinates(1/2, 0) + True + See the documentation of classes :class:`~sage.manifolds.chart.Chart` and :class:`~sage.manifolds.chart.RealChart` for more examples, From 3519cf1401505eb6f8f0da39640b615323a05d26 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 6 Jul 2021 13:48:57 -0500 Subject: [PATCH 248/336] Add _test_invariant for TestSuite --- src/sage/modules/with_basis/invariant.py | 63 ++++++++++++++++-------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 5f8749161c2..eef88233cc9 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -229,11 +229,48 @@ def semigroup_representation(self): return self._semigroup_representation - #def _test_ + def _test_invariant(self,**options): + """ + Check (on some elements) that ``self`` is invariant. + + EXAMPLES:: + + sage: G = SymmetricGroup(3) + sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') + sage: action = lambda g,x: M.term(g(x)) + sage: from sage.modules.with_basis.representation import Representation + sage: R = Representation(G, M, action); R + Representation of Symmetric group of order 3! as a permutation group indexed by {1, 2, 3} over Rational Field + sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule + sage: I = FiniteDimensionalInvariantModule(R) + sage: I._test_invariant() + + sage: G = SymmetricGroup(10) + sage: M = CombinatorialFreeModule(QQ, list(range(1,11)), prefix='M') + sage: action = lambda g,x: M.term(g(x)) + sage: R = Representation(G, M, action) + sage: I._test_invariant(max_runs=20) + + """ + tester = self._tester(**options) + S = tester.some_elements() + L = [] + max_len = int(tester._max_runs) + 1 + for i,x in enumerate(self._semigroup): + L.append(x) + if i >= max_len: + break + for x in L: + for elt in S: + if self._semigroup_representation.side() == 'left': + tester.assertEqual(x*elt, elt) + else: + tester.assertEqual(elt*x, elt) + class Element(SubmoduleWithBasis.Element): - def _mul_(self, other): + def _mul_(self, other): #FIX THIS TO ASSUME OTHER IS IN SAME PARENT """ EXAMPLES:: @@ -319,10 +356,8 @@ def _mul_(self, other): """ P = self.parent() - try: - return P.retract(P.lift(self) * P.lift(other)) - except: - return P.retract(P.lift(self)*other) + return P.retract(P.lift(self) * P.lift(other)) + def _lmul_(self, right): """ @@ -366,15 +401,8 @@ def _lmul_(self, right): 3*B[0] + 2*B[1] """ - if right in self.parent()._semigroup and self.parent()._semigroup_representation.side() == 'right': return self - - elif right in self.parent()._semigroup_representation._module.base_ring(): - # This preserves the structure of the invariant as a - # ``.base_ring()``-module - return self._mul_(right) - return super()._lmul_(right) def _rmul_(self, left): @@ -419,10 +447,6 @@ def _rmul_(self, left): """ if left in self.parent()._semigroup and self.parent()._semigroup_representation.side() == 'left': return self - - elif left in self.parent()._semigroup_representation._module.base_ring(): - return self._mul_(left) - return super()._rmul_(left) def _acted_upon_(self, scalar, self_on_left = False): @@ -467,9 +491,6 @@ def _acted_upon_(self, scalar, self_on_left = False): True """ - if scalar in self.parent()._semigroup and self_on_left == (self.parent()._semigroup_representation.side() == 'right'): - return self - - return None \ No newline at end of file + return super()._acted_upon_(scalar, self_on_left) \ No newline at end of file From 8907bef91be430a0763db3a2648656351d50c315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Jul 2021 18:11:43 +0200 Subject: [PATCH 249/336] adding typing annotations in some combinat/ files --- src/sage/combinat/dyck_word.py | 174 ++++++++++-------- src/sage/combinat/e_one_star.py | 94 +++++----- .../non_decreasing_parking_function.py | 33 ++-- src/sage/combinat/tamari_lattices.py | 13 +- 4 files changed, 164 insertions(+), 150 deletions(-) diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 6c36c354ed4..10f8a588a8b 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -77,6 +77,8 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +from __future__ import annotations +from typing import Union from .combinat import CombinatorialElement, catalan_number from sage.combinat.combinatorial_map import combinatorial_map @@ -454,7 +456,7 @@ def latex_options(self): d["valleys"] = self.parent().options.latex_valleys return d - def _repr_(self): + def _repr_(self) -> str: r""" Return a string representation of ``self`` depending on :meth:`DyckWords.options`. @@ -476,7 +478,7 @@ def _repr_(self): """ return self.parent().options._dispatch(self, '_repr_', 'display') - def _repr_list(self): + def _repr_list(self) -> str: r""" Return a string representation of ``self`` as a list. @@ -491,7 +493,7 @@ def _repr_list(self): """ return super(DyckWord, self)._repr_() - def _repr_lattice(self, type=None, labelling=None, underpath=True): + def _repr_lattice(self, type=None, labelling=None, underpath=True) -> str: r""" See :meth:`pretty_print()`. @@ -591,7 +593,7 @@ def _unicode_art_(self): from sage.typeset.unicode_art import UnicodeArt return UnicodeArt(self.to_path_string(unicode=True).splitlines()) - def __str__(self): + def __str__(self) -> str: r""" Return a string consisting of matched parentheses corresponding to the Dyck word. @@ -605,7 +607,7 @@ def __str__(self): """ return "".join(map(replace_symbols, [x for x in self])) - def to_path_string(self, unicode=False): + def to_path_string(self, unicode=False) -> str: r""" Return a path representation of the Dyck word consisting of steps ``/`` and ``\`` . @@ -810,7 +812,7 @@ def pretty_print(self, type=None, labelling=None, underpath=True): pp = pretty_print - def _latex_(self): + def _latex_(self) -> str: r""" A latex representation of ``self`` using the tikzpicture package. @@ -859,7 +861,7 @@ def _latex_(self): ht.append((a + 1, b - 1)) if i < len(heights) - 1 and heights[i + 1] > heights[i]: valleys.append(ht[-1]) - ht = iter(ht) + hti = iter(ht) if diagonal: grid = [((0, i), (i, i + 1)) for i in range(self.number_of_open_symbols())] @@ -886,8 +888,8 @@ def _latex_(self): if diagonal: res += " \\draw (0,0) -- %s;\n" % str((self.number_of_open_symbols(), self.number_of_open_symbols())) res += " \\draw[rounded corners=1, color=%s, line width=%s] (0, 0)" % (latex_options['color'], str(latex_options['line width'])) - next(ht) - for i, j in ht: + next(hti) + for i, j in hti: res += " -- (%s, %s)" % (i, j) res += ";\n" res += "\\end{tikzpicture}$}}" @@ -912,7 +914,7 @@ def plot(self, **kwds): list_sigma.append(sigma) return list_plot(list_sigma, plotjoined=True, **kwds) - def length(self): + def length(self) -> int: r""" Return the length of ``self``. @@ -930,7 +932,7 @@ def length(self): """ return len(self) - def number_of_open_symbols(self): + def number_of_open_symbols(self) -> int: r""" Return the number of open symbols in ``self``. @@ -948,7 +950,7 @@ def number_of_open_symbols(self): """ return len([x for x in self if x == open_symbol]) - def number_of_close_symbols(self): + def number_of_close_symbols(self) -> int: r""" Return the number of close symbols in ``self``. @@ -966,7 +968,7 @@ def number_of_close_symbols(self): """ return len([x for x in self if x == close_symbol]) - def is_complete(self): + def is_complete(self) -> bool: r""" Return ``True`` if ``self`` is complete. @@ -986,7 +988,7 @@ def is_complete(self): """ return self.number_of_open_symbols() == self.number_of_close_symbols() - def height(self): + def height(self) -> int: r""" Return the height of ``self``. @@ -1027,7 +1029,7 @@ def height(self): height -= 1 return height_max - def heights(self): + def heights(self) -> tuple: r""" Return the heights of ``self``. @@ -1068,7 +1070,7 @@ def heights(self): heights[i + 1] = height return tuple(heights) - def associated_parenthesis(self, pos): + def associated_parenthesis(self, pos) -> Union[int, None]: r""" Report the position for the parenthesis in ``self`` that matches the one at position ``pos``. @@ -1128,7 +1130,7 @@ def associated_parenthesis(self, pos): height -= 1 return pos - def ascent_prime_decomposition(self): + def ascent_prime_decomposition(self) -> list: r""" Decompose this Dyck word into a sequence of ascents and prime Dyck paths. @@ -1182,7 +1184,7 @@ def ascent_prime_decomposition(self): result.append(DyckWord([open_symbol] * up)) return result - def catalan_factorization(self): + def catalan_factorization(self) -> list: r""" Decompose this Dyck word into a sequence of complete Dyck words. @@ -1190,8 +1192,7 @@ def catalan_factorization(self): Each element of the list returned is a (possibly empty) complete Dyck word. The original word is obtained by placing an up step between each of these complete Dyck words. Thus, - the number of words returned is one more than the final - height. + the number of words returned is one more than the final height. See Section 1.2 of [CC1982]_ or Lemma 9.1.1 of [Lot2005]_. @@ -1225,7 +1226,7 @@ def catalan_factorization(self): j -= 1 return result - def number_of_initial_rises(self): + def number_of_initial_rises(self) -> int: r""" Return the length of the initial run of ``self``. @@ -1258,7 +1259,7 @@ def number_of_initial_rises(self): i += 1 return i - def peaks(self): + def peaks(self) -> list: r""" Return a list of the positions of the peaks of a Dyck word. @@ -1279,7 +1280,7 @@ def peaks(self): return [i for i in range(len(self) - 1) if self[i] == open_symbol and self[i + 1] == close_symbol] - def number_of_peaks(self): + def number_of_peaks(self) -> int: r""" Return the number of peaks of the Dyck path associated to ``self`` . @@ -1298,7 +1299,7 @@ def number_of_peaks(self): """ return len(self.peaks()) - def valleys(self): + def valleys(self) -> list: r""" Return a list of the positions of the valleys of a Dyck word. @@ -1318,7 +1319,7 @@ def valleys(self): return [i for i in range(len(self) - 1) if self[i] == close_symbol and self[i + 1] == open_symbol] - def number_of_valleys(self): + def number_of_valleys(self) -> int: r""" Return the number of valleys of ``self``. @@ -1344,7 +1345,7 @@ def number_of_valleys(self): """ return len(self.valleys()) - def position_of_first_return(self): + def position_of_first_return(self) -> int: r""" Return the number of vertical steps before the Dyck path returns to the main diagonal. @@ -1368,7 +1369,7 @@ def position_of_first_return(self): else: return touches[0] - def positions_of_double_rises(self): + def positions_of_double_rises(self) -> list: r""" Return a list of positions in ``self`` where there are two consecutive `1`'s. @@ -1385,7 +1386,7 @@ def positions_of_double_rises(self): return [i for i in range(len(self) - 1) if self[i] == self[i + 1] == open_symbol] - def number_of_double_rises(self): + def number_of_double_rises(self) -> int: r""" Return a the number of positions in ``self`` where there are two consecutive `1`'s. @@ -1401,7 +1402,7 @@ def number_of_double_rises(self): """ return len(self.positions_of_double_rises()) - def returns_to_zero(self): + def returns_to_zero(self) -> list: r""" Return a list of positions where ``self`` has height `0`, excluding the position `0`. @@ -1428,7 +1429,7 @@ def returns_to_zero(self): points.append(i + 1) return points - def touch_points(self): + def touch_points(self) -> list: r""" Return the abscissae (or, equivalently, ordinates) of the points where the Dyck path corresponding to ``self`` (comprising @@ -1485,7 +1486,7 @@ def touch_composition(self): return Composition([]) return Composition(descents=[i - 1 for i in self.touch_points()]) - def number_of_touch_points(self): + def number_of_touch_points(self) -> int: r""" Return the number of touches of ``self`` at the main diagonal. @@ -1577,7 +1578,7 @@ def to_standard_tableau(self): from sage.combinat.tableau import StandardTableau return StandardTableau([x for x in [open_positions, close_positions] if x]) - def to_tamari_sorting_tuple(self): + def to_tamari_sorting_tuple(self) -> list: """ Convert a Dyck word to a Tamari sorting tuple. @@ -1755,7 +1756,7 @@ def tamari_interval(self, other): from sage.combinat.interval_posets import TamariIntervalPosets return TamariIntervalPosets.from_dyck_words(self, other) - def to_area_sequence(self): + def to_area_sequence(self) -> list: r""" Return the area sequence of the Dyck word ``self``. @@ -1825,7 +1826,7 @@ class DyckWord_complete(DyckWord): For further information on Dyck words, see :class:`DyckWords_class`. """ - def semilength(self): + def semilength(self) -> int: r""" Return the semilength of ``self``. @@ -1886,7 +1887,7 @@ def to_partition(self): res.append(n) return Partition(res) - def number_of_parking_functions(self): + def number_of_parking_functions(self) -> int: r""" Return the number of parking functions with ``self`` as the supporting Dyck path. @@ -1931,7 +1932,7 @@ def list_parking_functions(self): # TODO: upon implementation of ParkingFunction class # map(ParkingFunction, Permutations([i - alist[i]+1 for i in range(len(alist))])) - def reading_permutation(self): + def reading_permutation(self) -> Permutation: r""" Return the reading permutation of ``self``. @@ -2009,7 +2010,7 @@ def characteristic_symmetric_function(self, q=None, s = SymmetricFunctions(R).s() return s(QSexpr.to_symmetric_function()) - def to_pair_of_standard_tableaux(self): + def to_pair_of_standard_tableaux(self) -> tuple: r""" Convert ``self`` to a pair of standard tableaux of the same shape and of length less than or equal to two. @@ -2045,11 +2046,12 @@ def to_pair_of_standard_tableaux(self): return (Tableau(left), Tableau(right)) @combinatorial_map(name='to 312 avoiding permutation') - def to_312_avoiding_permutation(self): + def to_312_avoiding_permutation(self) -> Permutation: r""" - Convert ``self`` to a `312`-avoiding permutation using the bijection by - Bandlow and Killpatrick in [BK2001]_. Sends the area to the - inversion number. + Convert ``self`` to a `312`-avoiding permutation using the bijection + by Bandlow and Killpatrick in [BK2001]_. + + This sends the area to the inversion number. EXAMPLES:: @@ -2081,7 +2083,7 @@ def to_312_avoiding_permutation(self): return pi @combinatorial_map(name='to non-crossing permutation') - def to_noncrossing_permutation(self): + def to_noncrossing_permutation(self) -> Permutation: r""" Use the bijection by C. Stump in [Stu2008]_ to send ``self`` to a non-crossing permutation. @@ -2127,7 +2129,7 @@ def to_noncrossing_permutation(self): return Permutations()(pi, check_input=False) @combinatorial_map(name='to 321 avoiding permutation') - def to_321_avoiding_permutation(self): + def to_321_avoiding_permutation(self) -> Permutation: r""" Use the bijection (pp. 60-61 of [Knu1973]_ or section 3.1 of [CK2008]_) to send ``self`` to a `321`-avoiding permutation. @@ -2182,7 +2184,7 @@ def to_321_avoiding_permutation(self): return RSK_inverse(A, B, output='permutation') @combinatorial_map(name='to 132 avoiding permutation') - def to_132_avoiding_permutation(self): + def to_132_avoiding_permutation(self) -> Permutation: r""" Use the bijection by C. Krattenthaler in [Kra2001]_ to send ``self`` to a `132`-avoiding permutation. @@ -2217,7 +2219,7 @@ def to_132_avoiding_permutation(self): values.remove(v) return Permutation(pi) - def to_permutation(self, map): + def to_permutation(self, map) -> Permutation: r""" This is simply a method collecting all implemented maps from Dyck words to permutations. @@ -2250,6 +2252,14 @@ def to_permutation(self, map): [1, 2, 4, 3] sage: D.to_permutation(map="Krattenthaler") [2, 1, 3, 4] + + TESTS:: + + sage: D = DyckWord([1,0,1,0]) + sage: D.to_permutation('Banana') + Traceback (most recent call last): + ... + ValueError: the given map is not valid """ if map == "Bandlow-Killpatrick": return self.to_312_avoiding_permutation() @@ -2260,7 +2270,7 @@ def to_permutation(self, map): elif map == "Stump": return self.to_noncrossing_permutation() else: - raise ValueError("The given map is not valid.") + raise ValueError("the given map is not valid") def to_noncrossing_partition(self, bijection=None): r""" @@ -2340,7 +2350,7 @@ def to_noncrossing_partition(self, bijection=None): return partition - def to_Catalan_code(self): + def to_Catalan_code(self) -> list: r""" Return the Catalan code associated to ``self``. @@ -2381,6 +2391,8 @@ def to_Catalan_code(self): if not self: return [] cut = self.associated_parenthesis(0) + if cut is None: + raise ValueError('not valid for incomplete Dyck words') recdw = DyckWord(self[1:cut] + self[cut + 1:]) returns = [0] + recdw.returns_to_zero() res = recdw.to_Catalan_code() @@ -2429,7 +2441,7 @@ def to_ordered_tree(self): root.set_immutable() return root - def to_triangulation(self): + def to_triangulation(self) -> list: r""" Map ``self`` to a triangulation. @@ -2548,7 +2560,7 @@ def to_non_decreasing_parking_function(self): from sage.combinat.non_decreasing_parking_function import NonDecreasingParkingFunction return NonDecreasingParkingFunction.from_dyck_word(self) - def major_index(self): + def major_index(self) -> int: r""" Return the major index of ``self`` . @@ -2576,7 +2588,7 @@ def major_index(self): valleys = self.valleys() return sum(valleys) + len(valleys) - def pyramid_weight(self): + def pyramid_weight(self) -> int: r""" Return the pyramid weight of ``self``. @@ -2608,8 +2620,8 @@ def pyramid_weight(self): if bseq[i + 1] <= bseq[i]: bpeak.append(i) out = 0 - for i in range(len(apeak)): - out += min(aseq[apeak[i]] - aseq[apeak[i] + 1] + 1, + for i, apeaki in enumerate(apeak): + out += min(aseq[apeaki] - aseq[apeaki + 1] + 1, bseq[bpeak[-i - 1]] - bseq[bpeak[-i - 1] + 1] + 1) return out @@ -2632,7 +2644,7 @@ def tunnels(self): if height < heights[i + 1]: yield (i, i + 1 + heights[i + 1:].index(height)) - def number_of_tunnels(self, tunnel_type='centered'): + def number_of_tunnels(self, tunnel_type='centered') -> int: r""" Return the number of tunnels of ``self`` of type ``tunnel_type``. @@ -2674,7 +2686,7 @@ def number_of_tunnels(self, tunnel_type='centered'): raise ValueError("The given tunnel_type is not valid.") @combinatorial_map(order=2, name="Reverse path") - def reverse(self): + def reverse(self) -> DyckWord: r""" Return the reverse and complement of ``self``. @@ -2704,7 +2716,7 @@ def reverse(self): list.reverse() return DyckWord(list) - def first_return_decomposition(self): + def first_return_decomposition(self) -> tuple: r""" Decompose a Dyck word into a pair of Dyck words (potentially empty) where the first word consists of the word after the first up step and @@ -2722,7 +2734,7 @@ def first_return_decomposition(self): k = self.position_of_first_return() * 2 return DyckWord(self[1:k - 1]), DyckWord(self[k:]) - def decomposition_reverse(self): + def decomposition_reverse(self) -> DyckWord: r""" Return the involution of ``self`` with a recursive definition. @@ -2749,7 +2761,7 @@ def decomposition_reverse(self): + [0] + list(D1.decomposition_reverse())) @combinatorial_map(name="Area-dinv to bounce-area") - def area_dinv_to_bounce_area_map(self): + def area_dinv_to_bounce_area_map(self) -> DyckWord: r""" Return the image of ``self`` under the map which sends a Dyck word with ``area`` equal to `r` and ``dinv`` equal to `s` to a @@ -2795,7 +2807,7 @@ def area_dinv_to_bounce_area_map(self): return DyckWord(image) @combinatorial_map(name="Bounce-area to area-dinv") - def bounce_area_to_area_dinv_map(self): + def bounce_area_to_area_dinv_map(self) -> DyckWord: r""" Return the image of the Dyck word under the map which sends a Dyck word with ``bounce`` equal to `r` and ``area`` equal to `s` to a @@ -2841,7 +2853,7 @@ def bounce_area_to_area_dinv_map(self): zeros = [0] + [j + len(out) - p for j in zeros[:aseq[i]]] return DyckWord(out) - def area(self): + def area(self) -> int: r""" Return the area for ``self`` corresponding to the area of the Dyck path. @@ -2911,7 +2923,7 @@ def area(self): a += above - diagonal return a - def bounce_path(self): + def bounce_path(self) -> DyckWord: r""" Return the bounce path of ``self`` formed by starting at `(n,n)` and traveling West until encountering the first vertical step of ``self``, @@ -2953,7 +2965,7 @@ def bounce_path(self): i -= 1 return DyckWord(area_sequence=area_seq) - def bounce(self): + def bounce(self) -> int: r""" Return the bounce statistic of ``self`` due to J. Haglund, see [Hag2008]_. @@ -3047,7 +3059,7 @@ def bounce(self): return b - def dinv(self, labeling=None): + def dinv(self, labeling=None) -> int: r""" Return the dinv statistic of ``self`` due to M. Haiman, see [Hag2008]_. @@ -3316,7 +3328,7 @@ def _element_constructor_(self, word): return word return self.element_class(self, list(word)) - def __contains__(self, x): + def __contains__(self, x) -> bool: r""" TESTS:: @@ -3338,7 +3350,7 @@ def __contains__(self, x): return is_a(x) - def from_heights(self, heights): + def from_heights(self, heights) -> DyckWord: r""" Compute a Dyck word knowing its heights. @@ -3400,7 +3412,7 @@ def from_heights(self, heights): raise ValueError("consecutive heights must differ by exactly 1: %s" % (heights,)) return self.element_class(self, res) - def min_from_heights(self, heights): + def min_from_heights(self, heights) -> DyckWord: r""" Compute the smallest Dyck word which achieves or surpasses a given sequence of heights. @@ -3480,7 +3492,7 @@ def __init__(self): """ DyckWords.__init__(self, category=InfiniteEnumeratedSets()) - def _repr_(self): + def _repr_(self) -> str: r""" TESTS:: @@ -3489,7 +3501,7 @@ def _repr_(self): """ return "Dyck words" - def _an_element_(self): + def _an_element_(self) -> DyckWord: r""" TESTS:: @@ -3623,7 +3635,7 @@ def __init__(self, k1, k2): self.k2 = ZZ(k2) DyckWords.__init__(self, category=FiniteEnumeratedSets()) - def _repr_(self): + def _repr_(self) -> str: r""" TESTS:: @@ -3632,7 +3644,7 @@ def _repr_(self): """ return "Dyck words with %s opening parentheses and %s closing parentheses" % (self.k1, self.k2) - def __contains__(self, x): + def __contains__(self, x) -> bool: r""" EXAMPLES:: @@ -3679,7 +3691,7 @@ def __iter__(self): for w in DyckWordBacktracker(self.k1, self.k2): yield self.element_class(self, w) - def cardinality(self): + def cardinality(self) -> int: r""" Return the number of Dyck words with `k_1` openers and `k_2` closers. @@ -3715,7 +3727,7 @@ class CompleteDyckWords(DyckWords): """ Element = DyckWord_complete - def __contains__(self, x): + def __contains__(self, x) -> bool: r""" TESTS:: @@ -3737,7 +3749,7 @@ def __contains__(self, x): return is_a(x, len(x) // 2) - def from_Catalan_code(self, code): + def from_Catalan_code(self, code) -> DyckWord: r""" Return the Dyck word associated to the given Catalan code ``code``. @@ -3778,7 +3790,7 @@ def from_Catalan_code(self, code): lst = [1] + res[:cuts[code[-1]]] + [0] + res[cuts[code[-1]]:] return self.element_class(self, lst) - def from_area_sequence(self, code): + def from_area_sequence(self, code) -> DyckWord: r""" Return the Dyck word associated to the given area sequence ``code``. @@ -3816,7 +3828,7 @@ def from_area_sequence(self, code): dyck_word.extend([close_symbol] * (2 * len(code) - len(dyck_word))) return self.element_class(self, dyck_word) - def from_noncrossing_partition(self, ncp): + def from_noncrossing_partition(self, ncp) -> DyckWord: r""" Convert a noncrossing partition ``ncp`` to a Dyck word. @@ -3844,7 +3856,7 @@ def from_noncrossing_partition(self, ncp): res += [open_symbol] + [close_symbol] * i return self.element_class(self, res) - def from_non_decreasing_parking_function(self, pf): + def from_non_decreasing_parking_function(self, pf) -> DyckWord: r""" Bijection from :class:`non-decreasing parking functions`. @@ -3885,7 +3897,7 @@ class CompleteDyckWords_all(CompleteDyckWords, DyckWords_all): """ All complete Dyck words. """ - def _repr_(self): + def _repr_(self) -> str: r""" TESTS:: @@ -4003,7 +4015,7 @@ def __init__(self, k): CompleteDyckWords.__init__(self, category=FiniteEnumeratedSets()) DyckWords_size.__init__(self, k, k) - def __contains__(self, x): + def __contains__(self, x) -> bool: r""" TESTS:: @@ -4018,7 +4030,7 @@ def __contains__(self, x): """ return CompleteDyckWords.__contains__(self, x) and len(x) // 2 == self.k1 - def cardinality(self): + def cardinality(self) -> int: r""" Return the number of complete Dyck words of semilength `n`, i.e. the `n`-th :func:`Catalan number`. @@ -4034,7 +4046,7 @@ def cardinality(self): """ return catalan_number(self.k1) - def random_element(self): + def random_element(self) -> DyckWord: """ Return a random complete Dyck word of semilength `n`. @@ -4125,7 +4137,7 @@ def _iter_by_recursion(self): yield self.element_class(self, list_1p0 + list(s)) -def is_area_sequence(seq): +def is_area_sequence(seq) -> bool: r""" Test if a sequence `l` of integers satisfies `l_0 = 0` and `0 \leq l_{i+1} \leq l_i + 1` for `i > 0`. @@ -4150,7 +4162,7 @@ def is_area_sequence(seq): for i in range(len(seq) - 1)) -def is_a(obj, k1=None, k2=None): +def is_a(obj, k1=None, k2=None) -> bool: r""" Test if ``obj`` is a Dyck word with exactly ``k1`` open symbols and exactly ``k2`` close symbols. diff --git a/src/sage/combinat/e_one_star.py b/src/sage/combinat/e_one_star.py index 8959223d681..f37b4f59074 100644 --- a/src/sage/combinat/e_one_star.py +++ b/src/sage/combinat/e_one_star.py @@ -201,13 +201,15 @@ # Vincent Delecroix <20100.delecroix@gmail.com> # Timo Jolivet # Stepan Starosta -# Sebastien Labbe +# Sébastien Labbé # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** +from __future__ import annotations +from typing import Union from sage.misc.functional import det from sage.structure.sage_object import SageObject @@ -299,7 +301,7 @@ def __init__(self, v, t, color=None): color = Color() self._color = Color(color) - def __repr__(self): + def __repr__(self) -> str: r""" String representation of a face. @@ -320,7 +322,7 @@ def __repr__(self): __richcmp__ = richcmp_by_eq_and_lt('_eq', '_lt') - def _eq(self, other): + def _eq(self, other) -> bool: r""" Equality of faces. @@ -336,7 +338,7 @@ def _eq(self, other): self.vector() == other.vector() and self.type() == other.type()) - def _lt(self, other): + def _lt(self, other) -> bool: r""" Compare ``self`` and ``other``. @@ -357,8 +359,9 @@ def _lt(self, other): return True if self.vector() == other.vector(): return self.type() < other.type() + return False - def __hash__(self): + def __hash__(self) -> int: r""" EXAMPLES:: @@ -433,7 +436,7 @@ def type(self): """ return self._type - def color(self, color=None): + def color(self, color=None) -> Color: r""" Return or change the color of the face. @@ -445,7 +448,7 @@ def color(self, color=None): OUTPUT: - color + color EXAMPLES:: @@ -454,16 +457,13 @@ def color(self, color=None): sage: f.color() RGB color (0.0, 0.0, 1.0) sage: f.color('red') - sage: f.color() RGB color (1.0, 0.0, 0.0) - """ - if color is None: - return self._color - else: + if color is not None: self._color = Color(color) + return self._color - def _plot(self, projmat, face_contour, opacity): + def _plot(self, projmat, face_contour, opacity) -> Graphics: r""" Return a 2D graphic object representing the face. @@ -476,7 +476,7 @@ def _plot(self, projmat, face_contour, opacity): OUTPUT: - 2D graphic object + 2D graphic object EXAMPLES:: @@ -578,7 +578,9 @@ class Patch(SageObject): """ def __init__(self, faces, face_contour=None): r""" - Constructor of a patch (set of faces). See class doc for more information. + Constructor of a patch (set of faces). + + See class doc for more information. EXAMPLES:: @@ -597,9 +599,9 @@ def __init__(self, faces, face_contour=None): sage: next(iter(P)).color() RGB color (0.0, 1.0, 0.0) sage: next(iter(Q)).color('yellow') + RGB color (1.0, 1.0, 0.0) sage: next(iter(P)).color() RGB color (0.0, 1.0, 0.0) - """ self._faces = frozenset(Face(f.vector(), f.type(), f.color()) for f in faces) @@ -623,7 +625,7 @@ def __init__(self, faces, face_contour=None): (1, 1, 0), (0, 1, 0)]] } - def __eq__(self, other): + def __eq__(self, other) -> bool: r""" Equality test for Patch. @@ -655,7 +657,7 @@ def __eq__(self, other): """ return (isinstance(other, Patch) and self._faces == other._faces) - def __hash__(self): + def __hash__(self) -> int: r""" Hash function of Patch. @@ -689,13 +691,13 @@ def __hash__(self): """ return hash(self._faces) - def __len__(self): + def __len__(self) -> int: r""" Return the number of faces contained in the patch. OUTPUT: - integer + integer EXAMPLES:: @@ -713,7 +715,7 @@ def __iter__(self): OUTPUT: - iterator + iterator EXAMPLES:: @@ -775,7 +777,7 @@ def __sub__(self, other): """ return self.difference(other) - def __repr__(self): + def __repr__(self) -> str: r""" String representation of a patch. @@ -804,7 +806,7 @@ def __repr__(self): else: return "Patch of %s faces" % len(self) - def union(self, other): + def union(self, other) -> Patch: r""" Return a Patch consisting of the union of self and other. @@ -826,7 +828,7 @@ def union(self, other): else: return Patch(self._faces.union(other)) - def difference(self, other): + def difference(self, other) -> Patch: r""" Return the difference of self and other. @@ -848,7 +850,7 @@ def difference(self, other): else: return Patch(self._faces.difference(other)) - def dimension(self): + def dimension(self) -> Union[None, int]: r""" Return the dimension of the vectors of the faces of self @@ -879,7 +881,7 @@ def dimension(self): """ return self._dimension - def faces_of_vector(self, v): + def faces_of_vector(self, v) -> list: r""" Return a list of the faces whose vector is ``v``. @@ -897,7 +899,7 @@ def faces_of_vector(self, v): v = vector(v) return [f for f in self if f.vector() == v] - def faces_of_type(self, t): + def faces_of_type(self, t) -> list: r""" Return a list of the faces that have type ``t``. @@ -914,7 +916,7 @@ def faces_of_type(self, t): """ return [f for f in self if f.type() == t] - def faces_of_color(self, color): + def faces_of_color(self, color) -> list: r""" Return a list of the faces that have the given color. @@ -932,7 +934,7 @@ def faces_of_color(self, color): color = tuple(Color(color)) return [f for f in self if tuple(f.color()) == color] - def translate(self, v): + def translate(self, v) -> Patch: r""" Return a translated copy of self by vector ``v``. @@ -950,7 +952,7 @@ def translate(self, v): v = vector(v) return Patch(Face(f.vector() + v, f.type(), f.color()) for f in self) - def occurrences_of(self, other): + def occurrences_of(self, other) -> list: r""" Return all positions at which other appears in self, that is, all vectors v such that ``set(other.translate(v)) <= set(self)``. @@ -961,7 +963,7 @@ def occurrences_of(self, other): OUTPUT: - a list of vectors + a list of vectors EXAMPLES:: @@ -995,9 +997,9 @@ def occurrences_of(self, other): positions.append(y - x) return positions - def repaint(self, cmap='Set1'): + def repaint(self, cmap='Set1') -> None: r""" - Repaints all the faces of self from the given color map. + Repaint all the faces of self from the given color map. This only changes the colors of the faces of self. @@ -1062,7 +1064,7 @@ def repaint(self, cmap='Set1'): elif isinstance(cmap, str): # matplotlib color maps global cm - if not cm: + if cm is None: from matplotlib import cm if cmap not in cm.datad: @@ -1075,7 +1077,7 @@ def repaint(self, cmap='Set1'): else: raise TypeError("Type of cmap (=%s) must be dict, list or str" % cmap) - def plot(self, projmat=None, opacity=0.75): + def plot(self, projmat=None, opacity=0.75) -> Graphics: r""" Return a 2D graphic object depicting the patch. @@ -1182,7 +1184,7 @@ def plot3d(self): return G def plot_tikz(self, projmat=None, print_tikz_env=True, edgecolor='black', - scale=0.25, drawzero=False, extra_code_before='', extra_code_after=''): + scale=0.25, drawzero=False, extra_code_before='', extra_code_after='') -> str: r""" Return a string containing some TikZ code to be included into a LaTeX document, depicting the patch. @@ -1308,7 +1310,7 @@ def plot_tikz(self, projmat=None, print_tikz_env=True, edgecolor='black', e2 = projmat * vector([0, 1, 0]) e3 = projmat * vector([0, 0, 1]) face_contour = self._face_contour - color = () + color = None # string s contains the TiKZ code of the patch s = '' @@ -1323,7 +1325,7 @@ def plot_tikz(self, projmat=None, print_tikz_env=True, edgecolor='black', t = f.type() x, y, z = f.vector() - if tuple(color) != tuple(f.color()): # tuple is needed, comparison for RGB fails + if color is None or color != f.color(): color = f.color() s += '\\definecolor{facecolor}{rgb}{%.3f,%.3f,%.3f}\n' % (color[0], color[1], color[2]) @@ -1437,7 +1439,7 @@ def __init__(self, sigma, method='suffix'): X[letter].append((v, k)) self._base_iter = X - def __eq__(self, other): + def __eq__(self, other) -> bool: r""" Equality test for E1Star morphisms. @@ -1460,7 +1462,7 @@ def __eq__(self, other): """ return (isinstance(other, E1Star) and self._base_iter == other._base_iter) - def __call__(self, patch, iterations=1): + def __call__(self, patch, iterations=1) -> Patch: r""" Applies a generalized substitution to a Patch; this returns a new object. @@ -1473,7 +1475,7 @@ def __call__(self, patch, iterations=1): OUTPUT: - a patch + a patch EXAMPLES:: @@ -1509,7 +1511,7 @@ def __call__(self, patch, iterations=1): old_faces = new_faces return Patch(new_faces) - def __mul__(self, other): + def __mul__(self, other) -> E1Star: r""" Return the product of ``self`` and ``other``. @@ -1538,7 +1540,7 @@ def __mul__(self, other): raise TypeError("other (=%s) must be an instance of E1Star" % other) return E1Star(other.sigma() * self.sigma()) - def __repr__(self): + def __repr__(self) -> str: r""" String representation of a patch. @@ -1564,7 +1566,7 @@ def _call_on_face(self, face, color=None): OUTPUT: - iterator of faces + iterator of faces EXAMPLES:: @@ -1616,9 +1618,9 @@ def inverse_matrix(self): """ return self.matrix().inverse() - def sigma(self): + def sigma(self) -> WordMorphism: r""" - Return the ``WordMorphism`` associated with self. + Return the ``WordMorphism`` associated with ``self``. EXAMPLES:: diff --git a/src/sage/combinat/non_decreasing_parking_function.py b/src/sage/combinat/non_decreasing_parking_function.py index b3d51b9fd3b..cea50974f0c 100644 --- a/src/sage/combinat/non_decreasing_parking_function.py +++ b/src/sage/combinat/non_decreasing_parking_function.py @@ -30,6 +30,7 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +from __future__ import annotations from copy import copy @@ -120,7 +121,7 @@ def NonDecreasingParkingFunctions(n=None): return NonDecreasingParkingFunctions_n(n) -def is_a(x, n=None): +def is_a(x, n=None) -> bool: """ Check whether a list is a non-decreasing parking function. @@ -144,9 +145,7 @@ def is_a(x, n=None): if prev > elt or elt > i + 1: return False prev = elt - if n is not None and n != len(x): - return False - return True + return n is None or n == len(x) class NonDecreasingParkingFunction(Element): @@ -220,7 +219,7 @@ def __call__(self, n): """ return self._list[n - 1] - def _mul_(self, lp): + def _mul_(self, lp) -> NonDecreasingParkingFunction: """ The composition of non-decreasing parking functions. @@ -280,7 +279,7 @@ def to_dyck_word(self): from sage.combinat.dyck_word import CompleteDyckWords_all return CompleteDyckWords_all().from_non_decreasing_parking_function(self) - def __len__(self): + def __len__(self) -> int: """ Return the length of ``self``. @@ -294,7 +293,7 @@ def __len__(self): grade = __len__ # for the category SetsWithGrading - def _repr_(self): + def _repr_(self) -> str: """ Return the string representation of ``self``. @@ -305,7 +304,7 @@ def _repr_(self): """ return str(self._list) - def _richcmp_(self, other, op): + def _richcmp_(self, other, op) -> bool: """ Compare ``self`` with ``other``. @@ -318,7 +317,7 @@ def _richcmp_(self, other, op): """ return richcmp(self._list, other._list, op) - def __hash__(self): + def __hash__(self) -> int: """ Return the hash of ``self``. @@ -332,7 +331,7 @@ def __hash__(self): return hash(tuple(self._list)) @classmethod - def from_dyck_word(cls, dw): + def from_dyck_word(cls, dw) -> NonDecreasingParkingFunction: """ Bijection from :class:`Dyck words`. It is the inverse of the @@ -380,7 +379,7 @@ def __init__(self): cat = InfiniteEnumeratedSets() & SetsWithGrading() Parent.__init__(self, category=cat) - def __repr__(self): + def __repr__(self) -> str: """ TESTS:: @@ -389,7 +388,7 @@ def __repr__(self): """ return "Non-decreasing parking functions" - def __contains__(self, x): + def __contains__(self, x) -> bool: """ TESTS:: @@ -482,7 +481,7 @@ def __init__(self, n): self.n = n Parent.__init__(self, category=Monoids().Enumerated().Finite()) - def __repr__(self): + def __repr__(self) -> str: """ TESTS:: @@ -491,7 +490,7 @@ def __repr__(self): """ return "Non-decreasing parking functions of size %s" % self.n - def __contains__(self, x): + def __contains__(self, x) -> bool: """ TESTS:: @@ -514,7 +513,7 @@ def __contains__(self, x): return True return is_a(x, self.n) - def cardinality(self): + def cardinality(self) -> Integer: """ Return the number of non-decreasing parking functions of size `n`. @@ -539,7 +538,7 @@ def cardinality(self): """ return catalan_number(self.n) - def random_element(self): + def random_element(self) -> NonDecreasingParkingFunction: """ Return a random parking function of the given size. @@ -556,7 +555,7 @@ def random_element(self): dw = DyckWords(n).random_element() return NonDecreasingParkingFunction.from_dyck_word(dw) - def one(self): + def one(self) -> NonDecreasingParkingFunction: """ Return the unit of this monoid. diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 7d37336578d..b0b8bd96253 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -46,11 +46,12 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +from __future__ import annotations from sage.combinat.posets.lattices import LatticePoset, MeetSemilattice from sage.arith.all import gcd -def paths_in_triangle(i, j, a, b): +def paths_in_triangle(i, j, a, b) -> list: r""" Return all Dyck paths from `(0,0)` to `(i,j)` in the `(a \times b)`-rectangle. @@ -100,7 +101,7 @@ def paths_in_triangle(i, j, a, b): return [u + tuple([0]) for u in paths_in_triangle(i - 1, j, a, b)] -def swap(p, i, m=1): +def swap(p, i, m=1) -> tuple: r""" Perform a covering move in the `(a,b)`-Tamari lattice of parameter `m`. @@ -159,7 +160,7 @@ def swap(p, i, m=1): return tuple(q) -def GeneralizedTamariLattice(a, b, m=1, check=True): +def GeneralizedTamariLattice(a, b, m=1, check=True) -> LatticePoset: r""" Return the `(a,b)`-Tamari lattice of parameter `m`. @@ -226,7 +227,7 @@ def covers(p): for p in paths_in_triangle(a, b, a, b)}, check=check) -def TamariLattice(n, m=1): +def TamariLattice(n, m=1) -> LatticePoset: r""" Return the `n`-th Tamari lattice. @@ -268,7 +269,7 @@ def TamariLattice(n, m=1): # a variation : the Dexter meet-semilattices -def swap_dexter(p, i): +def swap_dexter(p, i) -> list: r""" Perform covering moves in the `(a,b)`-Dexter posets. @@ -339,7 +340,7 @@ def swap_dexter(p, i): return resu -def DexterSemilattice(n): +def DexterSemilattice(n) -> MeetSemilattice: r""" Return the `n`-th Dexter meet-semilattice. From 44d9a30172da9b1df014a48c18935bd61bea9826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Jul 2021 18:22:33 +0200 Subject: [PATCH 250/336] fix for removal of embedded --- src/sage/misc/sagedoc.py | 2 ++ src/sage/misc/sageinspect.py | 70 +++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 49926223de7..7239651bb2b 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -187,6 +187,7 @@ def _rmcmd(s, cmd, left='', right=''): itempattern = re.compile(r"\\item\[?([^]]*)\]? *(.*)") itemreplace = r"* \1 \2" + def detex(s, embedded=False): r"""nodetex This strips LaTeX commands from a string; it is used by the @@ -745,6 +746,7 @@ def format(s, embedded=False): s = detex(s, embedded=embedded) return s + def format_src(s): """ Format Sage source code ``s`` for viewing with IPython. diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index cbb4483b006..cd1e190dcac 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -188,6 +188,7 @@ def isclassinstance(obj): # Parse Python identifiers __identifier_re = re.compile(r"^[^\d\W]\w*") + def _extract_embedded_position(docstring): r""" If docstring has a Cython embedded position, return a tuple @@ -263,6 +264,7 @@ def _extract_embedded_position(docstring): original = res.group('ORIGINAL') return (original, filename, lineno) + def _extract_embedded_signature(docstring, name): r""" If docstring starts with the embedded of a method called ``name``, return @@ -300,14 +302,15 @@ def _extract_embedded_signature(docstring, name): return docstring, None signature = firstline.split(name, 1)[-1] if signature.startswith("(") and signature.endswith(")"): - docstring = L[1] if len(L)>1 else '' # Remove first line, keep the rest - def_string = "def "+name+signature+": pass" + docstring = L[1] if len(L) > 1 else '' # Remove first line, keep the rest + def_string = "def " + name + signature + ": pass" try: return docstring, inspect.ArgSpec(*_sage_getargspec_cython(def_string)) except SyntaxError: docstring = os.linesep.join(L) return docstring, None + class BlockFinder: """ Provide a tokeneater() method to detect the end of a code block. @@ -365,7 +368,9 @@ def _getblock(lines): blockfinder = BlockFinder() iter_lines = iter(lines) tokenizer = tokenize.tokenize - readline = lambda: next(iter_lines).encode('utf-8') + + def readline(): + return next(iter_lines).encode('utf-8') try: for tok in tokenizer(readline): blockfinder.tokeneater(*tok) @@ -397,7 +402,7 @@ def _extract_source(lines, lineno): lineno -= 1 if isinstance(lines, str): - lines = lines.splitlines(True) # true keeps the '\n' + lines = lines.splitlines(True) # true keeps the '\n' if len(lines): # Fixes an issue with getblock lines[-1] += '\n' @@ -711,27 +716,27 @@ def visit_Compare(self, node): """ left = self.visit(node.left) ops = list(node.ops) - comparators = list(node.comparators) # the things to be compared with. + comparators = list(node.comparators) # the things to be compared with. while ops: op = ops.pop(0).__class__.__name__ right = self.visit(comparators.pop(0)) - if op=='Lt': - if not leftright: + elif op == 'Gt': + if not left > right: return False - elif op=='GtE': - if not left>=right: + elif op == 'GtE': + if not left >= right: return False - elif op=='Eq': - if not left==right: + elif op == 'Eq': + if not left == right: return False - elif op=='NotEq': - if not left!=right: + elif op == 'NotEq': + if not left != right: return False left = right return True @@ -841,6 +846,7 @@ def visit_UnaryOp(self, node): if op == 'USub': return -self.visit(node.operand) + def _grep_first_pair_of_parentheses(s): """ Return the first matching pair of parentheses in a code string. @@ -870,7 +876,6 @@ def _grep_first_pair_of_parentheses(s): Traceback (most recent call last): ... SyntaxError: The given string does not contain balanced parentheses - """ out = [] single_quote = False @@ -878,17 +883,17 @@ def _grep_first_pair_of_parentheses(s): escaped = False level = 0 for c in s: - if level>0: + if level > 0: out.append(c) - if c=='(' and not single_quote and not double_quote and not escaped: + if c == '(' and not single_quote and not double_quote and not escaped: level += 1 - elif c=='"' and not single_quote and not escaped: + elif c == '"' and not single_quote and not escaped: double_quote = not double_quote - elif c=="'" and not double_quote and not escaped: + elif c == "'" and not double_quote and not escaped: single_quote = not single_quote - elif c==')' and not single_quote and not double_quote and not escaped: + elif c == ')' and not single_quote and not double_quote and not escaped: if level == 1: - return '('+''.join(out) + return '(' + ''.join(out) level -= 1 elif c=="\\" and (single_quote or double_quote): escaped = not escaped @@ -896,6 +901,7 @@ def _grep_first_pair_of_parentheses(s): escaped = False raise SyntaxError("The given string does not contain balanced parentheses") + def _split_syntactical_unit(s): """ Split off a sub-expression from the start of a given string. @@ -958,6 +964,7 @@ def _split_syntactical_unit(s): s = s.strip() if not s: return s + # Split a given string at the next unescaped quotation mark def split_string(s, quot): escaped = False @@ -972,13 +979,13 @@ def split_string(s, quot): raise SyntaxError("EOF while scanning string literal") # 1. s is a triple-quoted string if s.startswith('"""'): - a,b = split_string(s[3:], '"""') + a, b = split_string(s[3:], '"""') return '"""'+a+'"""', b.strip() if s.startswith('r"""'): - a,b = split_string(s[4:], '"""') + a, b = split_string(s[4:], '"""') return 'r"""'+a+'"""', b.strip() if s.startswith("'''"): - a,b = split_string(s[3:], "'''") + a, b = split_string(s[3:], "'''") return "'''"+a+"'''", b.strip() if s.startswith("r'''"): a,b = split_string(s[4:], "'''") @@ -1036,6 +1043,7 @@ def split_string(s, quot): return ''.join(out), s[1:].strip() raise SyntaxError("Syntactical group starting with %s did not end with %s"%(repr(start),repr(stop))) + def _sage_getargspec_from_ast(source): r""" Return an argspec for a Python function or method by compiling its @@ -2005,17 +2013,18 @@ def sage_getdoc(obj, obj_name='', embeddeded=False): if obj is None: return '' r = sage_getdoc_original(obj) - s = sage.misc.sagedoc.format(r, embedded=embedded) + s = sage.misc.sagedoc.format(r) # Fix object naming if obj_name != '': i = obj_name.find('.') if i != -1: obj_name = obj_name[:i] - s = s.replace('self.','%s.'%obj_name) + s = s.replace('self.','%s.' % obj_name) return s + def sage_getsource(obj): r""" Return the source code associated to obj as a string, or None. @@ -2416,6 +2425,7 @@ class Element(object): break return _extract_source(source_lines, lineno), lineno + def sage_getvariablename(self, omit_underscore_names=True): """ Attempt to get the name of a Sage object. @@ -2463,6 +2473,7 @@ def sage_getvariablename(self, omit_underscore_names=True): else: return sorted(result) + __internal_teststring = ''' import os # 1 # preceding comment not include # 2 @@ -2479,6 +2490,7 @@ def test3(b, # 12 a=2): # 13 pass # EOF # 14''' + def __internal_tests(): r""" Test internals of the sageinspect module. From 0b000e153cd5507f5b5eff7c20486979949ca6c0 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 8 Jul 2021 12:47:05 -0500 Subject: [PATCH 251/336] Add exposition to examples of invariant.py --- src/sage/modules/with_basis/invariant.py | 45 +++++++++++++++--------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index eef88233cc9..75ebda6fae9 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -28,12 +28,12 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): M^S = \{m \in M : s\cdot m = m,\, \forall s \in S \} - INPUTS: + INPUT: - ``R`` -- an instance of a ``Representation`` of a semigroup `S` acting on the module `M`. - OUTPUTS: + OUTPUT: - ``MS`` -- the invariant algebra of the semigroup action of `S` on `M`, or equivalently, the isotypic component of the representation of @@ -41,43 +41,62 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): EXAMPLES:: + First, we create the invariant defined by the cyclic group action on the + free module with basis `\{1,2,3\}`. + sage: G = CyclicPermutationGroup(3) sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') - sage: from sage.modules.with_basis.representation import Representation sage: action = lambda g, m: M.term(g(m)) #cyclically permute coordinates + + In order to pass the action along, we need to make it an instance of a ``Representation``. + + sage: from sage.modules.with_basis.representation import Representation sage: R = Representation(G, M, action) sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule sage: I = FiniteDimensionalInvariantModule(R) + + Then we can lift the basis from the invariant to the original module. + sage: [I.lift(b) for b in I.basis()] [M[1] + M[2] + M[3]] + + The we could also have the action be a right-action, instead of the default left-action. - sage: G = CyclicPermutationGroup(3) - sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') - sage: from sage.modules.with_basis.representation import Representation sage: action = lambda g, m: M.term(g(m)) #cyclically permute coordinates sage: R = Representation(G, M, action, side='right') #same as last but on right - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule sage: g = G.an_element(); g (1,2,3) sage: r = R.an_element(); r 2*M[1] + 2*M[2] + 3*M[3] sage: R.side() 'right' + + So now we can see that multiplication with ``g`` on the right sends ``M[1]`` to ``M[2]`` + and so on. + sage: r*g 3*M[1] + 2*M[2] + 2*M[3] sage: I = FiniteDimensionalInvariantModule(R) sage: [I.lift(b) for b in I.basis()] [M[1] + M[2] + M[3]] + Now we'll take the regular representation of the symmetric group on three elements to + be the module, and compute its invariant. + sage: G = SymmetricGroup(3) sage: R = G.regular_representation(QQ) sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule sage: I = FiniteDimensionalInvariantModule(R) sage: [I.lift(b).to_vector() for b in I.basis()] [(1, 1, 1, 1, 1, 1)] + + We can also check the scalar multiplication by elements of the base ring (for this + example, the rational field). + sage: [I.lift(3*b).to_vector() for b in I.basis()] [(3, 3, 3, 3, 3, 3)] + sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) sage: from sage.modules.with_basis.representation import Representation @@ -112,7 +131,6 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): Extend when `M` does not have a basis and `S` is a permutation group using: - https://arxiv.org/abs/0812.3082 - https://www.dmtcs.org/pdfpapers/dmAA0123.pdf - """ def __init__(self, R, *args, **kwargs): @@ -129,7 +147,6 @@ def __init__(self, R, *args, **kwargs): Traceback (most recent call last): ... ValueError: Multiplicative form of Rational Field is not finitely generated - """ self._semigroup_representation = R @@ -143,8 +160,8 @@ def __init__(self, R, *args, **kwargs): # The left/right multiplication is taken care of # by self._semigroup_representation, so here # we can just pass the left multiplication. - # This means that the side argument of annihilator_basis - # (see below) will always be side = 'left' + # This means that the ``side`` argument of ``annihilator_basis`` + # (see below) will always be ``side = 'left'`` if self._semigroup_representation.side() == 'left': def _invariant_map(g, x): return g*x - x @@ -182,9 +199,7 @@ def _repr_(self): sage: FiniteDimensionalInvariantModule(R) (Cyclic group of order 3 as a permutation group)-invariant submodule of Free module generated by {1, 2, 3} over Rational Field - """ - return f"({self._semigroup})-invariant submodule of {self._semigroup_representation._module}" @@ -205,7 +220,6 @@ def semigroup(self): Symmetric group of order 3! as a permutation group """ - return self._semigroup def semigroup_representation(self): @@ -226,10 +240,9 @@ def semigroup_representation(self): Representation of Symmetric group of order 3! as a permutation group indexed by {1, 2, 3} over Rational Field """ - return self._semigroup_representation - def _test_invariant(self,**options): + def _test_invariant(self,**options): ## Lift to representation and check that the element is invariant """ Check (on some elements) that ``self`` is invariant. From f3b1c46239e44d3bbadd58980056fe8797684e8a Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Thu, 8 Jul 2021 20:15:16 +0200 Subject: [PATCH 252/336] Revert TermWithCoefficient._repr_ to be more descriptive. --- src/sage/rings/asymptotic/term_monoid.py | 37 ++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 047da7db3c2..1d0e5968b04 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -1771,7 +1771,7 @@ def _element_constructor_(self, data, coefficient=None): sage: G = GrowthGroup('x^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: t1 = T(x^2, 5); t1 # indirect doctest - Term with coefficient 5*x^2 + Term with coefficient 5 and growth x^2 TESTS:: @@ -1782,23 +1782,23 @@ def _element_constructor_(self, data, coefficient=None): :: sage: T(G.gen()^10) - Term with coefficient x^10 + Term with coefficient 1 and growth x^10 sage: T(G.gen()^10, coefficient=10) - Term with coefficient 10*x^10 + Term with coefficient 10 and growth x^10 sage: T(x^123) - Term with coefficient x^123 + Term with coefficient 1 and growth x^123 :: sage: T(x) - Term with coefficient x + Term with coefficient 1 and growth x :: sage: G_log = GrowthGroup('log(x)^ZZ') sage: T_log = TermWithCoefficientMonoid(TermMonoid, G_log, ZZ) sage: T_log(log(x)) - Term with coefficient log(x) + Term with coefficient 1 and growth log(x) """ if isinstance(data, self.element_class) and data.parent() == self: @@ -2818,9 +2818,9 @@ class TermWithCoefficient(GenericTerm): sage: CT_ZZ = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: CT_QQ = TermWithCoefficientMonoid(TermMonoid, G, QQ) sage: CT_ZZ(x^2, 5) - Term with coefficient 5*x^2 + Term with coefficient 5 and growth x^2 sage: CT_QQ(x^3, 3/8) - Term with coefficient 3/8*x^3 + Term with coefficient 3/8 and growth x^3 """ def __init__(self, parent, growth, coefficient): @@ -2848,7 +2848,7 @@ def __init__(self, parent, growth, coefficient): ValueError: 1/2 is not a coefficient in Generic Term Monoid x^ZZ with (implicit) coefficients in Integer Ring. sage: t = CT_QQ(x, 1/2); t - Term with coefficient 1/2*x + Term with coefficient 1/2 and growth x For technical reasons, the coefficient 0 is not allowed:: @@ -2864,7 +2864,7 @@ def __init__(self, parent, growth, coefficient): sage: x = SR('x'); x.parent() Symbolic Ring sage: CT_ZZ(x^42, 42) - Term with coefficient 42*x^42 + Term with coefficient 42 and growth x^42 """ try: coefficient = parent.coefficient_ring(coefficient) @@ -2901,9 +2901,10 @@ def _repr_(self): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: T(x^2, 5)._repr_() - 'Term with coefficient 5*x^2' + 'Term with coefficient 5 and growth x^2' """ - return f'Term with coefficient {self._product_repr_()}' + return 'Term with coefficient %s and growth %s' % \ + (self.coefficient, self.growth) def _product_repr_(self, latex=False): if latex: @@ -2969,7 +2970,7 @@ def _mul_(self, other): sage: t1 = CT(x^2, 2); t2 = CT(x^3, 3) sage: t1 * t2 - Term with coefficient 6*x^5 + Term with coefficient 6 and growth x^5 And now, an example for exact terms:: @@ -3003,11 +3004,11 @@ def _calculate_pow_(self, exponent): sage: G = GrowthGroup('z^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: t = T('2*z'); t - Term with coefficient 2*z + Term with coefficient 2 and growth z sage: t._calculate_pow_(3) - Term with coefficient 8*z^3 + Term with coefficient 8 and growth z^3 sage: t._calculate_pow_(-2) - Term with coefficient 1/4*z^(-2) + Term with coefficient 1/4 and growth z^(-2) :: @@ -3116,7 +3117,7 @@ def _eq_(self, other): sage: T = TermWithCoefficientMonoid(TermMonoid, GrowthGroup('x^ZZ'), ZZ) sage: t = T.an_element(); t - Term with coefficient x + Term with coefficient 1 and growth x sage: t == T(x, 1) True sage: t == T(x, 2) @@ -3216,7 +3217,7 @@ def _an_element_(self): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G = GrowthGroup('x^ZZ') sage: TermWithCoefficientMonoid(TermMonoid, G, ZZ).an_element() # indirect doctest - Term with coefficient x + Term with coefficient 1 and growth x sage: TermMonoid('exact', G, ZZ).an_element() # indirect doctest x sage: TermMonoid('exact', G, QQ).an_element() # indirect doctest From 4ce377607fbe91413a7f28f1efb1eb14c4b92c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Jul 2021 21:13:12 +0200 Subject: [PATCH 253/336] fix for removal of EMBEDDED stuff --- src/sage/misc/sagedoc.py | 3 ++- src/sage/misc/sageinspect.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 7239651bb2b..01e4ec4bcc1 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -571,6 +571,7 @@ def process_mathtt(s): s = s[:start] + s[start+8:end] + s[end+1:] return s + def format(s, embedded=False): r"""noreplace Format Sage documentation ``s`` for viewing with IPython. @@ -1441,7 +1442,7 @@ def __call__(self, obj, output='html', view=True): s += newline s += '**Docstring:**' s += newline - s += sageinspect.sage_getdoc(obj, obj_name, embedded_override=True) + s += sageinspect.sage_getdoc(obj, obj_name, embedded=True) # now s should be the reST version of the docstring if output == 'html': diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index cd1e190dcac..f4729d30bbf 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -1974,14 +1974,14 @@ def sage_getdoc_original(obj): return s -def sage_getdoc(obj, obj_name='', embeddeded=False): +def sage_getdoc(obj, obj_name='', embedded=False): r""" Return the docstring associated to ``obj`` as a string. If ``obj`` is a Cython object with an embedded position in its docstring, the embedded position is stripped. - The optional boolean argument ``embeddeded`` controls the + The optional boolean argument ``embedded`` controls the string formatting. It is False by default. INPUT: @@ -2013,14 +2013,14 @@ def sage_getdoc(obj, obj_name='', embeddeded=False): if obj is None: return '' r = sage_getdoc_original(obj) - s = sage.misc.sagedoc.format(r) + s = sage.misc.sagedoc.format(r, embedded=embedded) # Fix object naming if obj_name != '': i = obj_name.find('.') if i != -1: obj_name = obj_name[:i] - s = s.replace('self.','%s.' % obj_name) + s = s.replace('self.', '%s.' % obj_name) return s From f6f76c51c698fac0e149bffcc9417e5a2fd13e09 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 8 Jul 2021 14:58:09 -0500 Subject: [PATCH 254/336] Fix invariant_module method inside the FiniteDimensionalModuleWithBasis.ParentMethods class --- .../finite_dimensional_modules_with_basis.py | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 75021802f13..1550fe030a8 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -352,41 +352,50 @@ def echelon_form(self, elements, row_reduced=False, order=None): ret = [self.from_vector(vec, order=order) for vec in mat if vec] return ret - def invariant_module(self, A, G, action_on_basis = lambda x,g: x, + def invariant_module(self, S, action_on_basis = None, side = "left", **kwargs): r""" - Given a group acting on a module, return the module - `A^G = \{a \in A : ga = a \forall g \in G\}` + Given a semigroup acting on a module, return the module + `A^S = \{a \in A : s*a = a \forall s \in S\}` - INPUT:: + INPUT: - - ``M`` -- a finite-dimensional module with a basis + - ``self`` -- a finite-dimensional module with a basis - - ``G`` -- a finitely-generated group + - ``S`` -- a finitely-generated semigroup - ``action_on_basis(x,g)`` -- a function of taking positional arguments ``x`` and ``g`` which gives the action of ``g`` defined on a basis element ``x`` of ``M``. Default is the trivial action. - OUTPUT:: + OUTPUT: - - ``invariant`` -- an instance of ``FiniteDimensionalInvariantModule`` - representing `M^G` + - ``I`` -- an instance of ``FiniteDimensionalInvariantModule`` + which is `M^S` EXAMPLES:: - sage: action = lambda g, m: m.parent()(g._act_on_list_on_position(list(m))) - sage: G = SymmetricGroup(3) - sage: M = FreeModule(ZZ,[0,1,2]) - sage: R = Representation(G, M, action) + + sage: G = SymmetricGroup(3); G.rename('S3') + sage: M = FreeModule(ZZ, [1,2,3], prefix='M'); M.rename('M') + sage: action = lambda g,x: M.term(g(x)) + sage: I = M.invariant_module(G, action); I + (S3)-invariant submodule of M + sage: I.basis() + Finite family {0: B[0]} + sage: [I.lift(b) for b in I.basis()] + [M[1] + M[2] + M[3]] """ - from sage.modules.invariants import FiniteDimensionalInvariantModule + if not action_on_basis: + action_on_basis = lambda g,x: M.term(x) + + from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule from sage.modules.with_basis.representation import Representation - R = Representation(G, M, action_on_basis, side) + R = Representation(S, self, action_on_basis, side=side) - return FiniteDimensionalInvariantModule(M, G, R) + return FiniteDimensionalInvariantModule(R) class ElementMethods: def dense_coefficient_list(self, order=None): From ac750a472d6805fd6b587cfaae2605e49a166ea3 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 8 Jul 2021 15:28:58 -0500 Subject: [PATCH 255/336] Add example of symmetric functions with monomial basis --- src/sage/modules/with_basis/invariant.py | 85 +++++++++++++++++++++--- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 75ebda6fae9..ce7b4be4688 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -96,24 +96,47 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: [I.lift(3*b).to_vector() for b in I.basis()] [(3, 3, 3, 3, 3, 3)] - + A more subtle example is the invariant submodule of a skew-commutative module, for + example the exterior algebra `E[x_0,x_1,x_2]` generated by three elements. + sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) - sage: from sage.modules.with_basis.representation import Representation sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators + + If you care about being able to exploit the algebra structure of the exterior algebra (i.e. + if you want to multiply elements together), you should explicitly pass the category of + finite dimensional algebras with a basis to the ``Representation`` to the ``category`` + keyword argument. + sage: from sage.categories.algebras import Algebras sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) sage: I = FiniteDimensionalInvariantModule(R) + + We can express the basis in the ambient algebra (`E[x_0,x_1,x_2]`). + sage: [I.lift(b) for b in I.basis()] [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] + + Or we can express the basis intrinsicallly to the invariant ``I``. + sage: B = I.basis() sage: m = 3*B[0] + 2*B[1] + 7*B[3] + + This lifts to the exterior algebra. + sage: I.lift(m) 3 + 2*x0 + 7*x0*x1*x2 + 2*x1 + 2*x2 + + And we can check using the invariant element ``m`` that arithmetic works. + sage: m^2 9*B[0] + 12*B[1] + 42*B[3] sage: m+m 6*B[0] + 4*B[1] + 14*B[3] + + To see the actual elements expressed in the exterior algebra, we lift them + again. + sage: I.lift(m+m) 6 + 4*x0 + 14*x0*x1*x2 + 4*x1 + 4*x2 sage: 7*m @@ -121,6 +144,50 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: I.lift(7*m) 21 + 14*x0 + 49*x0*x1*x2 + 14*x1 + 14*x2 + The classic example of an invariant module is the module of symmetric functions, + which is the invariant module of polynomials whose variables are acted upon by + permutation. We can create a module isomorphic to the polynomials in `n` variables + of a fixed degree `d` by looking at the weak compositions of `d` of length `n`, + which we can associate to the exponent vector. For example, `x^2yz` in `\Bold{Q}[x,y,z]` + would have the exponent vector `(2,1,1)`. It is a weak composition of `4`, with length + `3`, and so we can think of it as being in the degree-`4` homogeneous component of a + polynomial ring in three variables. + + sage: C = Compositions(4, length=3, min_part=0) #representing degree-4 monomials + doctest:warning + ... + RuntimeWarning: Currently, setting min_part=0 produces Composition + objects which violate internal assumptions. Calling methods on these + objects may produce errors or WRONG results! + sage: M = CombinatorialFreeModule(QQ,C) #isomorphic to deg-4 homog. polynomials + sage: G = SymmetricGroup(3) + sage: action = lambda g,x: M.term(Composition(g(x._list))) + sage: action(G((1,2,3)),Composition([4,3,2])) + B[[3, 2, 4]] + sage: R = Representation(G, M, action) + sage: I = FiniteDimensionalInvariantModule(R) + sage: [I.lift(b) for b in I.basis()] + [B[[0, 0, 4]] + B[[0, 4, 0]] + B[[4, 0, 0]], + B[[0, 1, 3]] + B[[0, 3, 1]] + B[[1, 0, 3]] + B[[1, 3, 0]] + B[[3, 0, 1]] + B[[3, 1, 0]], + B[[0, 2, 2]] + B[[2, 0, 2]] + B[[2, 2, 0]], + B[[1, 1, 2]] + B[[1, 2, 1]] + B[[2, 1, 1]]] + + These are the monomial symmetric functions, which are a well-known basis for + the symmetric functions. + + sage: Sym = SymmetricFunctions(QQ) + sage: m = Sym.monomial() + sage: [m[mu].expand(3) for mu in Partitions(4)] + [x0^4 + x1^4 + x2^4, + x0^3*x1 + x0*x1^3 + x0^3*x2 + x1^3*x2 + x0*x2^3 + x1*x2^3, + x0^2*x1^2 + x0^2*x2^2 + x1^2*x2^2, + x0^2*x1*x2 + x0*x1^2*x2 + x0*x1*x2^2, + 0] + + .. SEEALSO:: + + :mod:`sage.combinat.sf` + .. NOTE:: The current implementation works when `S` is a finitely-generated semigroup, @@ -183,7 +250,7 @@ def _invariant_map(g, x): super().__init__(Family(basis), support_order = self._semigroup_representation._compute_support_order(basis), ambient = self._semigroup_representation, - unitriangular = False,#is this right? + unitriangular = False, category = category, *args, **kwargs) @@ -262,23 +329,25 @@ def _test_invariant(self,**options): ## Lift to representation and check that th sage: M = CombinatorialFreeModule(QQ, list(range(1,11)), prefix='M') sage: action = lambda g,x: M.term(g(x)) sage: R = Representation(G, M, action) - sage: I._test_invariant(max_runs=20) + sage: I._test_invariant(max_runs=10) """ tester = self._tester(**options) S = tester.some_elements() L = [] - max_len = int(tester._max_runs) + 1 + max_len = int(tester._max_runs) + for i,x in enumerate(self._semigroup): L.append(x) if i >= max_len: break + for x in L: for elt in S: if self._semigroup_representation.side() == 'left': - tester.assertEqual(x*elt, elt) + tester.assertEqual(x*self.lift(elt), self.lift(elt)) else: - tester.assertEqual(elt*x, elt) + tester.assertEqual(self.lift(elt)*x, self.lift(elt)) class Element(SubmoduleWithBasis.Element): @@ -328,7 +397,7 @@ def _mul_(self, other): #FIX THIS TO ASSUME OTHER IS IN SAME PARENT B[2] + 6*B[3] sage: (1/2)*v 1/2*B[0] + B[1] - sage:remote w*(1/2) + sage: w*(1/2) 1/2*B[2] sage: g = G((1,3,2)) sage: v*g From daeb91e7313d4240002e573078fb5547cc3a8a78 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 8 Jul 2021 14:39:24 -0700 Subject: [PATCH 256/336] src/sage/sets/set.py: Fix docstring markup --- src/sage/sets/set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 4c11f89781d..1055b8e1973 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -344,7 +344,7 @@ def _test_as_set_object(self, tester=None, **options): class Set_boolean_operators: r""" - Mix-in class providing the Boolean operators `__or__`, `__and__`, `__xor__`. + Mix-in class providing the Boolean operators ``__or__``, ``__and__``, ``__xor__``. The operators delegate to the methods ``union``, ``intersection``, and ``symmetric_difference``, which need to be implemented by the class. @@ -394,7 +394,7 @@ def __xor__(self, X): class Set_add_sub_operators: r""" - Mix-in class providing the operators `__add__` and `__sub__`. + Mix-in class providing the operators ``__add__`` and ``__sub__``. The operators delegate to the methods ``union`` and ``intersection``, which need to be implemented by the class. From 42d5e3446c840aae41fa45f16b42be1067426f49 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 8 Jul 2021 17:30:31 -0500 Subject: [PATCH 257/336] Fix _mul_ to assume that both are in same parent --- src/sage/modules/with_basis/invariant.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index ce7b4be4688..891354c84ae 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -352,7 +352,7 @@ def _test_invariant(self,**options): ## Lift to representation and check that th class Element(SubmoduleWithBasis.Element): - def _mul_(self, other): #FIX THIS TO ASSUME OTHER IS IN SAME PARENT + def _mul_(self, other): """ EXAMPLES:: @@ -440,7 +440,6 @@ def _mul_(self, other): #FIX THIS TO ASSUME OTHER IS IN SAME PARENT P = self.parent() return P.retract(P.lift(self) * P.lift(other)) - def _lmul_(self, right): """ Give the product of ``self*right`` From c038ef539cabfe7468ff72c319ddd2a1866e5172 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 8 Jul 2021 17:33:42 -0500 Subject: [PATCH 258/336] Edit exposition in symmetric function example --- src/sage/modules/with_basis/invariant.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 891354c84ae..9a117031632 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -146,12 +146,13 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): The classic example of an invariant module is the module of symmetric functions, which is the invariant module of polynomials whose variables are acted upon by - permutation. We can create a module isomorphic to the polynomials in `n` variables - of a fixed degree `d` by looking at the weak compositions of `d` of length `n`, - which we can associate to the exponent vector. For example, `x^2yz` in `\Bold{Q}[x,y,z]` - would have the exponent vector `(2,1,1)`. It is a weak composition of `4`, with length - `3`, and so we can think of it as being in the degree-`4` homogeneous component of a - polynomial ring in three variables. + permutation. We can create a module isomorphic to the homogeneous component of a + a polynomial ring in `n` variable of a fixed degree `d` by looking at weak + compositions of `d` of length `n`, which we can consider as the exponent vector. + For example, `x^2yz` in `\Bold{Q}[x,y,z]` would have the exponent vector `(2,1,1)`. + The vector `(2,1,1)` is a weak composition of `4`, with length `3`, and so we can + think of it as being in the degree-`4` homogeneous component of a polynomial ring + in three variables. sage: C = Compositions(4, length=3, min_part=0) #representing degree-4 monomials doctest:warning @@ -173,7 +174,7 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): B[[1, 1, 2]] + B[[1, 2, 1]] + B[[2, 1, 1]]] These are the monomial symmetric functions, which are a well-known basis for - the symmetric functions. + the symmetric functions. For comparison: sage: Sym = SymmetricFunctions(QQ) sage: m = Sym.monomial() From a75293a228dd7e5db70a86be6ceecf5ba66615bb Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 8 Jul 2021 17:49:47 -0500 Subject: [PATCH 259/336] Add exposition in examples --- src/sage/modules/with_basis/invariant.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 9a117031632..a0c1e5dc143 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -354,9 +354,12 @@ def _test_invariant(self,**options): ## Lift to representation and check that th class Element(SubmoduleWithBasis.Element): def _mul_(self, other): - """ + r""" EXAMPLES:: + In general, there is not a well defined multiplication between two elements + of a given module, but there is a multiplication with scalars. + sage: M = CombinatorialFreeModule(QQ,[1,2,3],prefix='M'); sage: G = CyclicPermutationGroup(3); G.rename('G') sage: g = G.an_element(); g @@ -378,9 +381,14 @@ def _mul_(self, other): sage: v*(1/2) 1/2*B[0] + Sometimes, the module is also a ring. To ensure the multiplication + works as desired, we should be sure to pass the correct category to + the ``Representation``. In the following example, we use the exterior + algebra over `\Bold{Q}` with three generators, which is in the category + of finite dimensional `\Bold{Q}`-algebras with a basis. + sage: G = CyclicPermutationGroup(3); G.rename('G') sage: M = algebras.Exterior(QQ, 'x', 3) - sage: from sage.modules.with_basis.representation import Representation sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators sage: from sage.categories.algebras import Algebras sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional(), side = 'right') From 553dc56e758b7dbac708a9ea2560a4cae1353ce7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 9 Jul 2021 11:29:56 +1000 Subject: [PATCH 260/336] Reviewer changes for invariant modules. - Rewriting the invariant module to not use Representation. - Extending semigroup actions to work with coercion. - Adding invariant_module() method to Representation_abstract so we don't need to pass the group. - Documentation improvements. - More flexibility for construction methods. - Adding invariant.py to documentation. --- src/doc/en/reference/modules/index.rst | 1 + .../finite_dimensional_modules_with_basis.py | 72 ++- src/sage/modules/with_basis/invariant.py | 539 ++++++++---------- src/sage/modules/with_basis/representation.py | 157 ++++- 4 files changed, 435 insertions(+), 334 deletions(-) diff --git a/src/doc/en/reference/modules/index.rst b/src/doc/en/reference/modules/index.rst index 0975f276ffb..d5ff3a70986 100644 --- a/src/doc/en/reference/modules/index.rst +++ b/src/doc/en/reference/modules/index.rst @@ -33,6 +33,7 @@ Modules sage/modules/with_basis/__init__ sage/modules/with_basis/cell_module + sage/modules/with_basis/invariant sage/modules/with_basis/morphism sage/modules/with_basis/subquotient sage/modules/with_basis/representation diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 1550fe030a8..1bcdf117f50 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -352,50 +352,76 @@ def echelon_form(self, elements, row_reduced=False, order=None): ret = [self.from_vector(vec, order=order) for vec in mat if vec] return ret - def invariant_module(self, S, action_on_basis = None, - side = "left", **kwargs): + def invariant_module(self, S, action=operator.mul, action_on_basis=None, + side="left", **kwargs): r""" - Given a semigroup acting on a module, return the module - `A^S = \{a \in A : s*a = a \forall s \in S\}` + Return the submodule of ``self`` invariant under the action + of ``S``. - INPUT: + For a semigroup `S` acting on a module `M`, the invariant + submodule is given by - - ``self`` -- a finite-dimensional module with a basis + .. MATH:: - - ``S`` -- a finitely-generated semigroup + M^S = \{m \in M : s \cdot m = m,\, \forall s \in S\}. + + INPUT: - - ``action_on_basis(x,g)`` -- a function of taking positional arguments - ``x`` and ``g`` which gives the action of ``g`` defined on a basis - element ``x`` of ``M``. Default is the trivial action. + - ``S`` -- a finitely-generated semigroup + - ``action`` -- a function (default: :obj:`operator.mul`) + - ``side`` -- ``'left'`` or ``'right'`` (default: ``'right'``); + which side of ``self`` the elements of ``S`` acts + - ``action_on_basis`` -- (optional) define the action of ``S`` + on the basis of ``self`` OUTPUT: - - ``I`` -- an instance of ``FiniteDimensionalInvariantModule`` - which is `M^S` + - :class:`~sage.modules.with_basis.invariant.FiniteDimensionalInvariantModule` - EXAMPLES:: + EXAMPLES: + + We build the invariant module of the permutation representation + of the symmetric group:: sage: G = SymmetricGroup(3); G.rename('S3') sage: M = FreeModule(ZZ, [1,2,3], prefix='M'); M.rename('M') - sage: action = lambda g,x: M.term(g(x)) - sage: I = M.invariant_module(G, action); I + sage: action = lambda g, x: M.term(g(x)) + sage: I = M.invariant_module(G, action_on_basis=action); I (S3)-invariant submodule of M sage: I.basis() Finite family {0: B[0]} sage: [I.lift(b) for b in I.basis()] [M[1] + M[2] + M[3]] + sage: G.rename(); M.rename() # reset the names + + We can construct the invariant module of any module that has + an action of ``S``. In this example, we consider the dihedral + group `G = D_4` and the subgroup `H < G` of all rotations. We + construct the `H`-invariant module of the group algebra `\QQ[G]`:: + + sage: G = groups.permutation.Dihedral(4) + sage: H = G.subgroup(G.gen(0)) + sage: H + Subgroup generated by [(1,2,3,4)] of (Dihedral group of order 8 as a permutation group) + sage: H.cardinality() + 4 + sage: A = G.algebra(QQ) + sage: I = A.invariant_module(H) + sage: [I.lift(b) for b in I.basis()] + [() + (1,2,3,4) + (1,3)(2,4) + (1,4,3,2), + (2,4) + (1,2)(3,4) + (1,3) + (1,4)(2,3)] + sage: all(h * I.lift(b) == I.lift(b) for b in I.basis() for h in H) + True """ - - if not action_on_basis: - action_on_basis = lambda g,x: M.term(x) + if action_on_basis is not None: + from sage.modules.with_basis.representation import Representation + M = Representation(S, self, action_on_basis, side=side) + else: + M = self from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - from sage.modules.with_basis.representation import Representation - - R = Representation(S, self, action_on_basis, side=side) - - return FiniteDimensionalInvariantModule(R) + return FiniteDimensionalInvariantModule(M, S, action=action, side=side, **kwargs) class ElementMethods: def dense_coefficient_list(self, order=None): diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index a0c1e5dc143..338ea6b3e50 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -12,6 +12,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +import operator from sage.modules.with_basis.subquotient import SubmoduleWithBasis from sage.categories.finitely_generated_semigroups import FinitelyGeneratedSemigroups from sage.categories.finite_dimensional_modules_with_basis import FiniteDimensionalModulesWithBasis @@ -20,50 +21,46 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): r""" - Construct the `S`-invariant submodule of `M`. When a semigroup `S` acts on a module - `M`, the invariant module is the collection of elements `m` in `M` such that - `s \cdot m = m` for all `s \in S. + The invariant submodule under a semigroup action. + + When a semigroup `S` acts on a module `M`, the invariant module is the + set of elements `m \in M` such that `s \cdot m = m` for all `s \in S`: .. MATH:: - M^S = \{m \in M : s\cdot m = m,\, \forall s \in S \} + M^S := \{m \in M : s \cdot m = m,\, \forall s \in S \}. INPUT: - ``R`` -- an instance of a ``Representation`` of a semigroup `S` - acting on the module `M`. - - OUTPUT: + acting on the module `M` - - ``MS`` -- the invariant algebra of the semigroup action of `S` on `M`, or - equivalently, the isotypic component of the representation of - `S` carried by `M` corresponding to the trivial character. - - EXAMPLES:: + EXAMPLES: First, we create the invariant defined by the cyclic group action on the - free module with basis `\{1,2,3\}`. + free module with basis `\{1,2,3\}`:: sage: G = CyclicPermutationGroup(3) sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') - sage: action = lambda g, m: M.term(g(m)) #cyclically permute coordinates + sage: action = lambda g, m: M.monomial(g(m)) # cyclically permute coordinates - In order to pass the action along, we need to make it an instance of a ``Representation``. + In order to give the module an action of ``G``, we create a + :class:`~sage.modules.with_basis.representation.Representation`:: sage: from sage.modules.with_basis.representation import Representation sage: R = Representation(G, M, action) - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) + sage: I = R.invariant_module() - Then we can lift the basis from the invariant to the original module. + Then we can lift the basis from the invariant to the original module:: sage: [I.lift(b) for b in I.basis()] [M[1] + M[2] + M[3]] - The we could also have the action be a right-action, instead of the default left-action. + The we could also have the action be a right-action, instead of the + default left-action:: - sage: action = lambda g, m: M.term(g(m)) #cyclically permute coordinates - sage: R = Representation(G, M, action, side='right') #same as last but on right + sage: def rt_action(g, m): return M.monomial(g(m)) # cyclically permute coordinates + sage: R = Representation(G, M, rt_action, side='right') # same as last but on right sage: g = G.an_element(); g (1,2,3) sage: r = R.an_element(); r @@ -71,63 +68,65 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: R.side() 'right' - So now we can see that multiplication with ``g`` on the right sends ``M[1]`` to ``M[2]`` - and so on. + So now we can see that multiplication with ``g`` on the right sends + ``M[1]`` to ``M[2]`` and so on:: - sage: r*g + sage: r * g 3*M[1] + 2*M[2] + 2*M[3] - sage: I = FiniteDimensionalInvariantModule(R) + sage: I = R.invariant_module() sage: [I.lift(b) for b in I.basis()] [M[1] + M[2] + M[3]] - Now we'll take the regular representation of the symmetric group on three elements to - be the module, and compute its invariant. + Now we will take the regular representation of the symmetric group on + three elements to be the module, and compute its invariant submodule:: sage: G = SymmetricGroup(3) sage: R = G.regular_representation(QQ) - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) + sage: I = R.invariant_module() sage: [I.lift(b).to_vector() for b in I.basis()] [(1, 1, 1, 1, 1, 1)] - We can also check the scalar multiplication by elements of the base ring (for this - example, the rational field). + We can also check the scalar multiplication by elements of the base ring + (for this example, the rational field):: sage: [I.lift(3*b).to_vector() for b in I.basis()] [(3, 3, 3, 3, 3, 3)] - A more subtle example is the invariant submodule of a skew-commutative module, for - example the exterior algebra `E[x_0,x_1,x_2]` generated by three elements. + A more subtle example is the invariant submodule of a skew-commutative + module, for example the exterior algebra `E[x_0,x_1,x_2]` generated + by three elements:: sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) - sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators + sage: def cyclic_ext_action(g, m): + ....: # cyclically permute generators + ....: return M.prod([M.monomial((g(j+1)-1,)) for j in m]) - If you care about being able to exploit the algebra structure of the exterior algebra (i.e. - if you want to multiply elements together), you should explicitly pass the category of - finite dimensional algebras with a basis to the ``Representation`` to the ``category`` - keyword argument. + If you care about being able to exploit the algebra structure of the + exterior algebra (i.e. if you want to multiply elements together), you + should make sure the representation knows it is also an algebra with + the semigroup action being by algebra endomorphisms:: - sage: from sage.categories.algebras import Algebras - sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) - sage: I = FiniteDimensionalInvariantModule(R) + sage: cat = Algebras(QQ).WithBasis().FiniteDimensional() + sage: R = Representation(G, M, cyclic_ext_action, category=cat) + sage: I = R.invariant_module() - We can express the basis in the ambient algebra (`E[x_0,x_1,x_2]`). + We can express the basis in the ambient algebra (`E[x_0,x_1,x_2]`):: sage: [I.lift(b) for b in I.basis()] [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] - Or we can express the basis intrinsicallly to the invariant ``I``. + or we can express the basis intrinsicallly to the invariant ``I``:: sage: B = I.basis() sage: m = 3*B[0] + 2*B[1] + 7*B[3] - This lifts to the exterior algebra. + This lifts to the exterior algebra:: sage: I.lift(m) 3 + 2*x0 + 7*x0*x1*x2 + 2*x1 + 2*x2 - And we can check using the invariant element ``m`` that arithmetic works. + We can also check using the invariant element ``m`` that arithmetic works:: sage: m^2 9*B[0] + 12*B[1] + 42*B[3] @@ -135,7 +134,7 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): 6*B[0] + 4*B[1] + 14*B[3] To see the actual elements expressed in the exterior algebra, we lift them - again. + again:: sage: I.lift(m+m) 6 + 4*x0 + 14*x0*x1*x2 + 4*x1 + 4*x2 @@ -144,37 +143,34 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): sage: I.lift(7*m) 21 + 14*x0 + 49*x0*x1*x2 + 14*x1 + 14*x2 - The classic example of an invariant module is the module of symmetric functions, - which is the invariant module of polynomials whose variables are acted upon by - permutation. We can create a module isomorphic to the homogeneous component of a - a polynomial ring in `n` variable of a fixed degree `d` by looking at weak - compositions of `d` of length `n`, which we can consider as the exponent vector. - For example, `x^2yz` in `\Bold{Q}[x,y,z]` would have the exponent vector `(2,1,1)`. - The vector `(2,1,1)` is a weak composition of `4`, with length `3`, and so we can - think of it as being in the degree-`4` homogeneous component of a polynomial ring - in three variables. - - sage: C = Compositions(4, length=3, min_part=0) #representing degree-4 monomials - doctest:warning - ... - RuntimeWarning: Currently, setting min_part=0 produces Composition - objects which violate internal assumptions. Calling methods on these - objects may produce errors or WRONG results! - sage: M = CombinatorialFreeModule(QQ,C) #isomorphic to deg-4 homog. polynomials + The classic example of an invariant module is the module of symmetric + functions, which is the invariant module of polynomials whose variables + are acted upon by permutation. We can create a module isomorphic to the + homogeneous component of a a polynomial ring in `n` variable of a fixed + degree `d` by looking at weak compositions of `d` of length `n`, which + we consider as the exponent vector. For example, `x^2yz \in \QQ[x,y,z]` + would have the exponent vector `(2,1,1)`. The vector `(2,1,1)` is a + weak composition of `4`, with length `3`, and so we can think of it as + being in the degree-`4` homogeneous component of a polynomial ring + in three variables:: + + sage: C = IntegerVectors(4, length=3, min_part=0) # representing degree-4 monomials + sage: M = CombinatorialFreeModule(QQ, C) # isomorphic to deg-4 homog. polynomials sage: G = SymmetricGroup(3) - sage: action = lambda g,x: M.term(Composition(g(x._list))) - sage: action(G((1,2,3)),Composition([4,3,2])) + sage: def perm_action(g,x): return M.monomial(C(g(list(x)))) + sage: perm_action(G((1,2,3)), C([4,3,2])) B[[3, 2, 4]] - sage: R = Representation(G, M, action) - sage: I = FiniteDimensionalInvariantModule(R) + sage: R = Representation(G, M, perm_action) + sage: I = R.invariant_module() sage: [I.lift(b) for b in I.basis()] [B[[0, 0, 4]] + B[[0, 4, 0]] + B[[4, 0, 0]], - B[[0, 1, 3]] + B[[0, 3, 1]] + B[[1, 0, 3]] + B[[1, 3, 0]] + B[[3, 0, 1]] + B[[3, 1, 0]], + B[[0, 1, 3]] + B[[0, 3, 1]] + B[[1, 0, 3]] + + B[[1, 3, 0]] + B[[3, 0, 1]] + B[[3, 1, 0]], B[[0, 2, 2]] + B[[2, 0, 2]] + B[[2, 2, 0]], B[[1, 1, 2]] + B[[1, 2, 1]] + B[[2, 1, 1]]] - These are the monomial symmetric functions, which are a well-known basis for - the symmetric functions. For comparison: + These are the monomial symmetric functions, which are a well-known + basis for the symmetric functions. For comparison:: sage: Sym = SymmetricFunctions(QQ) sage: m = Sym.monomial() @@ -185,132 +181,122 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): x0^2*x1*x2 + x0*x1^2*x2 + x0*x1*x2^2, 0] - .. SEEALSO:: + .. NOTE:: - :mod:`sage.combinat.sf` + The current implementation works when `S` is a finitely-generated + semigroup, and when `M` is a finite-dimensional free module with + a distinguished basis. - .. NOTE:: + .. TODO:: - The current implementation works when `S` is a finitely-generated semigroup, - and when `M` is a finite-dimensional free module with a distinguished basis. + Extend this to have multiple actions, including actions on both sides. .. TODO:: - Extend when `M` does not have a basis and `S` is a permutation group using: + Extend when `M` does not have a basis and `S` is a permutation + group using: + - https://arxiv.org/abs/0812.3082 - https://www.dmtcs.org/pdfpapers/dmAA0123.pdf """ - - def __init__(self, R, *args, **kwargs): + def __init__(self, M, S, action=operator.mul, side='left', *args, **kwargs): """ + Initialize ``self``. + + EXAMPLES:: + + sage: G = CyclicPermutationGroup(3) + sage: R = G.regular_representation() + sage: I = R.invariant_module() + sage: TestSuite(I).run() + TESTS:: sage: G = GroupExp()(QQ) # a group that is not finitely generated sage: M = CombinatorialFreeModule(QQ, [1,2,3]) - sage: on_basis = lambda g,m: M.term(m) # trivial rep'n + sage: def on_basis(g,m): return M.monomial(m) # trivial rep'n sage: from sage.modules.with_basis.representation import Representation sage: R = Representation(G, M, on_basis) - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) + sage: R.invariant_module() Traceback (most recent call last): ... ValueError: Multiplicative form of Rational Field is not finitely generated """ + if S not in FinitelyGeneratedSemigroups(): + raise ValueError(f"{S} is not finitely generated") + if M not in FiniteDimensionalModulesWithBasis: + raise ValueError(f"{M} is not a finite dimensional module with a distinguished basis") - self._semigroup_representation = R - self._semigroup = R.semigroup() - - # A check for self._semigroup_representation._module not in FiniteDimensionalModulesWithBasis - # is not required, because a ``Representation`` cannot be built without a basis - if self._semigroup not in FinitelyGeneratedSemigroups: - raise ValueError(f'{self._semigroup} is not finitely generated') - - # The left/right multiplication is taken care of - # by self._semigroup_representation, so here - # we can just pass the left multiplication. - # This means that the ``side`` argument of ``annihilator_basis`` - # (see below) will always be ``side = 'left'`` - if self._semigroup_representation.side() == 'left': + if side == "left": def _invariant_map(g, x): - return g*x - x - elif self._semigroup_representation.side() == 'right': + return action(g, x) - x + elif side == "right": def _invariant_map(g, x): - return x*g - x + return action(x, g) - x + else: + raise ValueError("side must either be 'left' or 'right'") - self._invariant_map = _invariant_map + self._side = side + self._action = action + self._semigroup = S - category = kwargs.pop('category', R.category().Subobjects()) + category = kwargs.pop("category", M.category().Subobjects()) # Give the intersection of kernels of the map `s*x-x` to determine when # `s*x = x` for all generators `s` of `S` - basis = self._semigroup_representation.annihilator_basis( - self._semigroup.gens(), - action = self._invariant_map, - side = 'left') + basis = M.annihilator_basis(S.gens(), action=_invariant_map, side="left") super().__init__(Family(basis), - support_order = self._semigroup_representation._compute_support_order(basis), - ambient = self._semigroup_representation, - unitriangular = False, - category = category, - *args, **kwargs) + support_order=M._compute_support_order(basis), + ambient=M, + unitriangular=False, + category=category, + *args, **kwargs) def _repr_(self): - """ + r""" + Return a string representaion of ``self``. + EXAMPLES:: - sage: M = CombinatorialFreeModule(QQ,[1,2,3]) sage: G = CyclicPermutationGroup(3) - sage: from sage.modules.with_basis.representation import Representation - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: R = Representation(G,M,lambda g,x: M.monomial(g(x))) - sage: FiniteDimensionalInvariantModule(R) + sage: R = G.trivial_representation() + sage: R.invariant_module() (Cyclic group of order 3 as a permutation group)-invariant submodule of - Free module generated by {1, 2, 3} over Rational Field - """ - return f"({self._semigroup})-invariant submodule of {self._semigroup_representation._module}" - - - def semigroup(self): - """ - Return the semigroup `S` whose action ``self`` is invariant under. - - EXAMPLES:: + Trivial representation of Cyclic group of order 3 as a permutation group over Integer Ring - sage: G = SymmetricGroup(3) + sage: G = CyclicPermutationGroup(3) sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') - sage: action = lambda g,x: M.term(g(x)) - sage: from sage.modules.with_basis.representation import Representation - sage: R = Representation(G, M, action) - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) - sage: I.semigroup() - Symmetric group of order 3! as a permutation group - + sage: action = lambda g, m: M.monomial(g(m)) # cyclically permute coordinates + sage: M.invariant_module(G, action_on_basis=action) + (Cyclic group of order 3 as a permutation group)-invariant submodule of + Free module generated by {1, 2, 3} over Rational Field """ - return self._semigroup + M = self._ambient + from sage.modules.with_basis.representation import Representation + if isinstance(self._ambient, Representation): + M = M._module + return f"({self._semigroup})-invariant submodule of {M}" - def semigroup_representation(self): - """ - Return the underlying representation of the invariant module. + def _latex_(self): + r""" + Return a latex representaion of ``self``. EXAMPLES:: - sage: G = SymmetricGroup(3) - sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') - sage: action = lambda g,x: M.term(g(x)) - sage: from sage.modules.with_basis.representation import Representation - sage: R = Representation(G, M, action); R - Representation of Symmetric group of order 3! as a permutation group indexed by {1, 2, 3} over Rational Field - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) - sage: I.semigroup_representation() - Representation of Symmetric group of order 3! as a permutation group indexed by {1, 2, 3} over Rational Field - + sage: G = CyclicPermutationGroup(3) + sage: R = G.algebra(QQ) + sage: latex(R.invariant_module(G)) + \left( \Bold{Q}[\langle (1,2,3) \rangle] \right)^{\langle (1,2,3) \rangle} """ - return self._semigroup_representation - - def _test_invariant(self,**options): ## Lift to representation and check that the element is invariant + M = self._ambient + from sage.modules.with_basis.representation import Representation + if isinstance(self._ambient, Representation): + M = M._module + from sage.misc.latex import latex + return "\\left( {} \\right)^{{{}}}".format(latex(M), latex(self._semigroup)) + + def _test_invariant(self, **options): ## Lift to representation and check that the element is invariant """ Check (on some elements) that ``self`` is invariant. @@ -318,56 +304,70 @@ def _test_invariant(self,**options): ## Lift to representation and check that th sage: G = SymmetricGroup(3) sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') - sage: action = lambda g,x: M.term(g(x)) - sage: from sage.modules.with_basis.representation import Representation - sage: R = Representation(G, M, action); R - Representation of Symmetric group of order 3! as a permutation group indexed by {1, 2, 3} over Rational Field - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) + sage: def action(g, x): return M.monomial(g(x)) + sage: I = M.invariant_module(G, action_on_basis=action) sage: I._test_invariant() sage: G = SymmetricGroup(10) sage: M = CombinatorialFreeModule(QQ, list(range(1,11)), prefix='M') - sage: action = lambda g,x: M.term(g(x)) - sage: R = Representation(G, M, action) + sage: def action(g, x): return M.monomial(g(x)) + sage: I = M.invariant_module(G, action_on_basis=action) sage: I._test_invariant(max_runs=10) - """ tester = self._tester(**options) - S = tester.some_elements() + X = tester.some_elements() L = [] - max_len = int(tester._max_runs) + max_len = tester._max_runs - for i,x in enumerate(self._semigroup): + # FIXME: This is max_len * dim number of runs!!! + for i, x in enumerate(self._semigroup): L.append(x) if i >= max_len: break for x in L: - for elt in S: - if self._semigroup_representation.side() == 'left': - tester.assertEqual(x*self.lift(elt), self.lift(elt)) + for elt in X: + lifted = self.lift(elt) + if self._side == 'left': + tester.assertEqual(self._action(x, lifted), lifted) else: - tester.assertEqual(self.lift(elt)*x, self.lift(elt)) + tester.assertEqual(self._action(lifted, x), lifted) + + def semigroup(self): + r""" + Return the semigroup `S` whose action ``self`` is invariant under. + EXAMPLES:: - class Element(SubmoduleWithBasis.Element): + sage: G = SymmetricGroup(3) + sage: M = CombinatorialFreeModule(QQ, [1,2,3], prefix='M') + sage: def action(g,x): return M.monomial(g(x)) + sage: I = M.invariant_module(G, action_on_basis=action) + sage: I.semigroup() + Symmetric group of order 3! as a permutation group + """ + return self._semigroup + + semigroup_representation = SubmoduleWithBasis.ambient + class Element(SubmoduleWithBasis.Element): def _mul_(self, other): r""" - EXAMPLES:: + Multiply ``self`` and ``other``. - In general, there is not a well defined multiplication between two elements - of a given module, but there is a multiplication with scalars. + EXAMPLES: + + In general, there is not a well defined multiplication between + two elements of a given module, but there is a multiplication + with scalars:: sage: M = CombinatorialFreeModule(QQ,[1,2,3],prefix='M'); sage: G = CyclicPermutationGroup(3); G.rename('G') sage: g = G.an_element(); g (1,2,3) sage: from sage.modules.with_basis.representation import Representation - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule sage: R = Representation(G,M,lambda g,x:M.monomial(g(x))); R.rename('R') - sage: I = FiniteDimensionalInvariantModule(R) + sage: I = R.invariant_module() sage: B = I.basis() sage: [I.lift(b) for b in B] [M[1] + M[2] + M[3]] @@ -376,152 +376,106 @@ def _mul_(self, other): Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for *: 'R' and 'R' - sage: (1/2)*v + sage: (1/2) * v 1/2*B[0] - sage: v*(1/2) + sage: v * (1/2) 1/2*B[0] + sage: R.rename() # reset name Sometimes, the module is also a ring. To ensure the multiplication works as desired, we should be sure to pass the correct category to - the ``Representation``. In the following example, we use the exterior - algebra over `\Bold{Q}` with three generators, which is in the category - of finite dimensional `\Bold{Q}`-algebras with a basis. + the :class:`~sage.modules.with_basis.representation.Representation`. + In the following example, we use the exterior algebra over `\QQ` + with three generators, which is in the category of finite + dimensional `\QQ`-algebras with a basis:: sage: G = CyclicPermutationGroup(3); G.rename('G') sage: M = algebras.Exterior(QQ, 'x', 3) - sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators - sage: from sage.categories.algebras import Algebras - sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional(), side = 'right') - sage: I = FiniteDimensionalInvariantModule(R); I.rename('I') + sage: def on_basis(g,m): return M.prod([M.monomial((g(j+1)-1,)) for j in m]) # cyclically permute generators + sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional(), side='right') + sage: I = R.invariant_module(); I.rename('I') sage: B = I.basis() sage: v = B[0] + 2*B[1]; I.lift(v) 1 + 2*x0 + 2*x1 + 2*x2 sage: w = B[2]; I.lift(w) x0*x1 - x0*x2 + x1*x2 - sage: v*w + sage: v * w B[2] + 6*B[3] sage: I.lift(v*w) x0*x1 + 6*x0*x1*x2 - x0*x2 + x1*x2 - sage: w*v + sage: w * v B[2] + 6*B[3] - sage: (1/2)*v + sage: (1/2) * v 1/2*B[0] + B[1] - sage: w*(1/2) + sage: w * (1/2) 1/2*B[2] sage: g = G((1,3,2)) - sage: v*g + sage: v * g B[0] + 2*B[1] - sage: w*g + sage: w * g B[2] - sage: g*v + sage: g * v Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for *: 'G' and 'I' + sage: I.rename() # reset name sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) - sage: I = FiniteDimensionalInvariantModule(R); I.rename('I') + sage: I = R.invariant_module(); I.rename('I') sage: B = I.basis() sage: v = B[0] + 2*B[1]; I.lift(v) 1 + 2*x0 + 2*x1 + 2*x2 sage: w = B[2]; I.lift(w) x0*x1 - x0*x2 + x1*x2 - sage: v*w + sage: v * w B[2] + 6*B[3] sage: I.lift(v*w) x0*x1 + 6*x0*x1*x2 - x0*x2 + x1*x2 - sage: w*v + sage: w * v B[2] + 6*B[3] - sage: (1/2)*v + sage: (1/2) * v 1/2*B[0] + B[1] - sage: w*(1/2) + sage: w * (1/2) 1/2*B[2] sage: g = G((1,3,2)) - sage: v*v + sage: v * v B[0] + 4*B[1] - sage: g*w + sage: g * w B[2] - sage: v*g + sage: v * g Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for *: 'I' and 'G' - + sage: G.rename(); I.rename() # reset names """ P = self.parent() return P.retract(P.lift(self) * P.lift(other)) - def _lmul_(self, right): + def _acted_upon_(self, scalar, self_on_left=False): """ - Give the product of ``self*right`` - EXAMPLES:: - sage: M = CombinatorialFreeModule(QQ,[1,2,3]) sage: G = CyclicPermutationGroup(3) sage: g = G.an_element(); g (1,2,3) + sage: M = CombinatorialFreeModule(QQ, [1,2,3]) + sage: E = algebras.Exterior(QQ, 'x', 3) sage: from sage.modules.with_basis.representation import Representation - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: R = Representation(G,M,lambda g,x: M.monomial(g(x)), side = 'right') - sage: I = FiniteDimensionalInvariantModule(R) + sage: R = Representation(G, M, lambda g,x: M.monomial(g(x))) + sage: I = R.invariant_module() + sage: [b._acted_upon_(G((1,3,2))) for b in I.basis()] + [B[0]] sage: v = I.an_element(); v 2*B[0] - sage: v*g + sage: g * v 2*B[0] - sage: [v*g for g in G.list()] + sage: [g * v for g in G.list()] [2*B[0], 2*B[0], 2*B[0]] - sage: G = CyclicPermutationGroup(3) - sage: M = algebras.Exterior(QQ, 'x', 3) - sage: from sage.modules.with_basis.representation import Representation - sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators - sage: from sage.categories.algebras import Algebras - sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional(), side = 'right') - sage: I = FiniteDimensionalInvariantModule(R) - sage: B = I.basis() - sage: [I.lift(b) for b in B] - [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] - sage: [[b*g for g in G] for b in B] - [[B[0], B[0], B[0]], - [B[1], B[1], B[1]], - [B[2], B[2], B[2]], - [B[3], B[3], B[3]]] - sage: 3*I.basis()[0] - 3*B[0] - sage: 3*B[0] + B[1]*2 - 3*B[0] + 2*B[1] - - """ - if right in self.parent()._semigroup and self.parent()._semigroup_representation.side() == 'right': - return self - return super()._lmul_(right) - - def _rmul_(self, left): - """ - Give the product of ``left * self`` - - EXAMPLES:: - - sage: M = CombinatorialFreeModule(QQ,[1,2,3]) - sage: G = CyclicPermutationGroup(3) - sage: g = G.an_element(); g - (1,2,3) - sage: from sage.modules.with_basis.representation import Representation - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: R = Representation(G,M,lambda g,x: M.monomial(g(x))) - sage: I = FiniteDimensionalInvariantModule(R) - sage: v = I.an_element(); v - 2*B[0] - sage: g*v - 2*B[0] - sage: [g*v for g in G.list()] - [2*B[0], 2*B[0], 2*B[0]] - sage: G = CyclicPermutationGroup(3) - sage: M = algebras.Exterior(QQ, 'x', 3) - sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators - sage: from sage.categories.algebras import Algebras - sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) - sage: I = FiniteDimensionalInvariantModule(R) + sage: def on_basis(g,m): return E.prod([E.monomial((g(j+1)-1,)) for j in m]) # cyclically permute generators + sage: R = Representation(G, E, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) + sage: I = R.invariant_module() sage: B = I.basis() sage: [I.lift(b) for b in B] [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] @@ -530,57 +484,58 @@ def _rmul_(self, left): [B[1], B[1], B[1]], [B[2], B[2], B[2]], [B[3], B[3], B[3]]] - sage: 3*I.basis()[0] + sage: 3 * I.basis()[0] 3*B[0] sage: 3*B[0] + B[1]*2 3*B[0] + 2*B[1] - """ - if left in self.parent()._semigroup and self.parent()._semigroup_representation.side() == 'left': - return self - return super()._rmul_(left) - - def _acted_upon_(self, scalar, self_on_left = False): - """ - EXAMPLES:: - - sage: G = CyclicPermutationGroup(3) - sage: M = CombinatorialFreeModule(QQ,[1,2,3]) - sage: from sage.modules.with_basis.representation import Representation - sage: R = Representation(G, M, lambda g,x: M.monomial(g(x))) - sage: from sage.modules.with_basis.invariant import FiniteDimensionalInvariantModule - sage: I = FiniteDimensionalInvariantModule(R) - sage: B = I.basis() - sage: [b._acted_upon_(G((1,3,2))) for b in B] - [B[0]] - - sage: R = Representation(G, M, lambda g,x: M.monomial(g(x)), side = 'right') - sage: I = FiniteDimensionalInvariantModule(R) - sage: B = I.basis() - sage: [b._acted_upon_(G((1,3,2)), self_on_left = True) for b in B] - [B[0]] sage: R = G.regular_representation(QQ) - sage: I = FiniteDimensionalInvariantModule(R) + sage: I = R.invariant_module() sage: B = I.basis() sage: [I.lift(b) for b in B] [() + (1,2,3) + (1,3,2)] sage: B[0]._acted_upon_(G((1,3,2))) B[0] - sage: B[0]._acted_upon_(G((1,3,2)), self_on_left=True) == None + sage: B[0]._acted_upon_(G((1,3,2)), self_on_left=True) is None True - sage: R = G.regular_representation(QQ, side = 'right') - sage: I = FiniteDimensionalInvariantModule(R) + sage: R = G.regular_representation(QQ, side='right') + sage: I = R.invariant_module() sage: B = I.basis() sage: [I.lift(b) for b in B] [() + (1,2,3) + (1,3,2)] sage: g = G((1,3,2)) - sage: B[0]._acted_upon_(g, self_on_left = True) + sage: B[0]._acted_upon_(g, self_on_left=True) B[0] - sage: B[0]._acted_upon_(g, self_on_left = False) == None + sage: B[0]._acted_upon_(g, self_on_left=False) is None True + sage: R = Representation(G, M, lambda g,x: M.monomial(g(x)), side='right') + sage: I = R.invariant_module() + sage: v = I.an_element(); v + 2*B[0] + sage: v * g + 2*B[0] + sage: [v * g for g in G.list()] + [2*B[0], 2*B[0], 2*B[0]] + sage: [b._acted_upon_(G((1,3,2)), self_on_left=True) for b in I.basis()] + [B[0]] + + sage: def on_basis(g,m): return E.prod([E.monomial((g(j+1)-1,)) for j in m]) # cyclically permute generators + sage: R = Representation(G, E, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional(), side='right') + sage: I = R.invariant_module() + sage: B = I.basis() + sage: [I.lift(b) for b in B] + [1, x0 + x1 + x2, x0*x1 - x0*x2 + x1*x2, x0*x1*x2] + sage: [[b * g for g in G] for b in B] + [[B[0], B[0], B[0]], + [B[1], B[1], B[1]], + [B[2], B[2], B[2]], + [B[3], B[3], B[3]]] + sage: 3 * B[0] + B[1] * 2 + 3*B[0] + 2*B[1] """ - if scalar in self.parent()._semigroup and self_on_left == (self.parent()._semigroup_representation.side() == 'right'): + if scalar in self.parent()._semigroup and self_on_left == (self.parent()._side == 'right'): return self - return super()._acted_upon_(scalar, self_on_left) \ No newline at end of file + return super()._acted_upon_(scalar, self_on_left) + diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 27fffed163d..317a401f1fc 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -93,6 +93,62 @@ def side(self): 'left' """ + def invariant_module(self, S=None, **kwargs): + r""" + Return the submodule of ``self`` invariant under the action of ``S``. + + For a semigroup `S` acting on a module `M`, the invariant + submodule is given by + + .. MATH:: + + M^S = \{m \in M : s \cdot m = m \forall s \in S\}. + + INPUT: + + - ``S`` -- a finitely-generated semigroup (default: the semigroup + this is a representation of) + - ``action`` -- a function (default: :obj:`operator.mul`) + - ``side`` -- ``'left'`` or ``'right'`` (default: :meth:`side()`); + which side of ``self`` the elements of ``S`` acts + + .. NOTE:: + + Two sided actions are considered as left actions for the + invariant module. + + OUTPUT: + + - :class:`~sage.modules.with_basis.invariant.FiniteDimensionalInvariantModule` + + EXAMPLES:: + + sage: S3 = SymmetricGroup(3) + sage: M = S3.regular_representation() + sage: I = M.invariant_module() + sage: [I.lift(b) for b in I.basis()] + [() + (2,3) + (1,2) + (1,2,3) + (1,3,2) + (1,3)] + + We build the `D_4`-invariant representation inside of the regular + representation of `S_4`:: + + sage: D4 = groups.permutation.Dihedral(4) + sage: S4 = SymmetricGroup(4) + sage: R = S4.regular_representation() + sage: I = R.invariant_module(D4) + sage: [I.lift(b) for b in I.basis()] + [() + (2,4) + (1,2)(3,4) + (1,2,3,4) + (1,3) + (1,3)(2,4) + (1,4,3,2) + (1,4)(2,3), + (3,4) + (2,3,4) + (1,2) + (1,2,4) + (1,3,2) + (1,3,2,4) + (1,4,3) + (1,4,2,3), + (2,3) + (2,4,3) + (1,2,3) + (1,2,4,3) + (1,3,4,2) + (1,3,4) + (1,4,2) + (1,4)] + """ + if S is None: + S = self.semigroup() + side = kwargs.pop('side', self.side()) + if side == "twosided": + side = "left" + + return super().invariant_module(S, side=side, **kwargs) + class Representation(Representation_abstract): """ Representation of a semigroup. @@ -442,10 +498,22 @@ def _acted_upon_(self, scalar, self_on_left=False): TypeError: unsupported operand parent(s) for *: 'Algebra of Weyl Group of type ['B', 2] ... over Rational Field' and 'Left Regular Representation of Weyl Group of type ['B', 2] ... over Integer Ring' + + Check that things that coerce into the group (algebra) also have + an action:: + + sage: D4 = groups.permutation.Dihedral(4) + sage: S4 = SymmetricGroup(4) + sage: S4.has_coerce_map_from(D4) + True + sage: R = S4.regular_representation() + sage: D4.an_element() * R.an_element() + 2*(2,4) + 3*(1,2,3,4) + (1,3) + (1,4,2,3) """ if isinstance(scalar, Element): P = self.parent() - if scalar.parent() is P._semigroup: + sP = scalar.parent() + if sP is P._semigroup: if not self: return self if self_on_left == P._left_repr: @@ -453,7 +521,7 @@ def _acted_upon_(self, scalar, self_on_left=False): return P.linear_combination(((P._on_basis(scalar, m), c) for m,c in self), not self_on_left) - if scalar.parent() is P._semigroup_algebra: + if sP is P._semigroup_algebra: if not self: return self ret = P.zero() @@ -464,9 +532,23 @@ def _acted_upon_(self, scalar, self_on_left=False): for m,c in self), not self_on_left) return ret - return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) + if P._semigroup.has_coerce_map_from(sP): + scalar = P._semigroup(scalar) + return self._acted_upon_(scalar, self_on_left) + + # Check for scalars first before general coercion to the semigroup algebra. + # This will result in a faster action for the scalars. + ret = CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) + if ret is not None: + return ret + + if P._semigroup_algebra.has_coerce_map_from(sP): + scalar = P._semigroup_algebra(scalar) + return self._acted_upon_(scalar, self_on_left) + + return None - _rmul_ = _lmul_ = _acted_upon_ + return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) class RegularRepresentation(Representation): r""" @@ -631,20 +713,38 @@ def _acted_upon_(self, scalar, self_on_left=False): sage: z = V.zero() sage: all(b * z == z for b in SGA.basis()) True + + sage: H = groups.permutation.Dihedral(5) + sage: G = SymmetricGroup(5) + sage: G.has_coerce_map_from(H) + True + sage: R = G.trivial_representation(QQ) + sage: H.an_element() * R.an_element() + 2*B['v'] + + sage: AG = G.algebra(QQ) + sage: AG.an_element() * R.an_element() + 14*B['v'] + + sage: AH = H.algebra(ZZ) + sage: AG.has_coerce_map_from(AH) + True + sage: AH.an_element() * R.an_element() + 14*B['v'] """ if isinstance(scalar, Element): - if scalar.parent() is self.parent()._semigroup: + P = self.parent() + if P._semigroup.has_coerce_map_from(scalar.parent()): return self - if scalar.parent() is self.parent()._semigroup_algebra: + if P._semigroup_algebra.has_coerce_map_from(scalar.parent()): if not self: return self + scalar = P._semigroup_algebra(scalar) d = self.monomial_coefficients(copy=True) d['v'] *= sum(scalar.coefficients()) - return self.parent()._from_dict(d) + return P._from_dict(d) return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) - _rmul_ = _lmul_ = _acted_upon_ - class SignRepresentation_abstract(Representation_abstract): """ @@ -757,26 +857,48 @@ def _acted_upon_(self, scalar, self_on_left=False): 0 sage: (c-s)*x 4*B['v'] + + sage: H = groups.permutation.Dihedral(4) + sage: G = SymmetricGroup(4) + sage: G.has_coerce_map_from(H) + True + sage: R = G.sign_representation() + sage: H.an_element() * R.an_element() + -2*B['v'] + + sage: AG = G.algebra(ZZ) + sage: AH = H.algebra(ZZ) + sage: AG.has_coerce_map_from(AH) + True + sage: AH.an_element() * R.an_element() + -2*B['v'] """ if isinstance(scalar, Element): P = self.parent() - if not self: - return self - if scalar.parent() is P._semigroup: + if P._semigroup.has_coerce_map_from(scalar.parent()): + scalar = P._semigroup(scalar) return self if P.sign_function(scalar) > 0 else -self - if scalar.parent() is P._semigroup_algebra: + # We need to check for scalars first + ret = CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) + if ret is not None: + return ret + + if P._semigroup_algebra.has_coerce_map_from(scalar.parent()): + if not self: + return self sum_scalar_coeff = 0 + scalar = P._semigroup_algebra(scalar) for ms, cs in scalar: sum_scalar_coeff += P.sign_function(ms) * cs return sum_scalar_coeff * self + return None + return CombinatorialFreeModule.Element._acted_upon_( self, scalar, self_on_left ) - _rmul_ = _lmul_ = _acted_upon_ - class SignRepresentationPermgroup(SignRepresentation_abstract): """ @@ -788,7 +910,6 @@ class SignRepresentationPermgroup(SignRepresentation_abstract): sage: V = G.sign_representation() sage: TestSuite(V).run() """ - def _default_sign(self, elem): """ Return the sign of the element @@ -807,7 +928,6 @@ def _default_sign(self, elem): sage: V._default_sign(elem) -1 """ - return elem.sign() @@ -821,7 +941,6 @@ class SignRepresentationMatrixGroup(SignRepresentation_abstract): sage: V = G.sign_representation() sage: TestSuite(V).run() """ - def _default_sign(self, elem): """ Return the sign of the element @@ -854,7 +973,6 @@ class SignRepresentationCoxeterGroup(SignRepresentation_abstract): sage: V = G.sign_representation() sage: TestSuite(V).run() """ - def _default_sign(self, elem): """ Return the sign of the element @@ -872,3 +990,4 @@ def _default_sign(self, elem): 1 """ return -1 if elem.length() % 2 == 1 else 1 + From 59cc054aed61866b2fbdfd72b7d5c9d1405956d3 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Thu, 8 Jul 2021 22:49:24 -0600 Subject: [PATCH 261/336] do not deprecate __len__ --- src/sage/logic/boolformula.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/logic/boolformula.py b/src/sage/logic/boolformula.py index 8af231ca111..bfaa37739dc 100644 --- a/src/sage/logic/boolformula.py +++ b/src/sage/logic/boolformula.py @@ -1553,8 +1553,9 @@ def length(self): """ return len(flatten(self.full_tree())) - from sage.misc.superseded import deprecated_function_alias - __len__ = deprecated_function_alias(32148, length) + # For backward compatibility, we allow `self.length()` to be called as + # `len(self)`, but this may be deprecated in the future (see :trac:`32148`): + __len__ = length # allow is_consequence to be called as a function (not only as a method of BooleanFormula) is_consequence = BooleanFormula.is_consequence From 8e5d552739fc8d461ffaa4fb408f8ed2ec9d1bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Jul 2021 09:05:41 +0200 Subject: [PATCH 262/336] fixing misc/misc --- src/sage/misc/misc.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index 16c829b64c6..8c4f620752f 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -43,9 +43,11 @@ import resource import pdb import warnings + import sage.misc.prandom as random from .lazy_string import lazy_string - +from sage.interfaces.quit import expect_objects +from sage.env import DOT_SAGE, HOSTNAME from sage.misc.lazy_import import lazy_import lazy_import("sage.misc.call", ["AttrCallObject", "attrcall", "call_method"], @@ -58,8 +60,6 @@ lazy_import("sage.misc.repr", ["coeff_repr", "repr_lincomb"], deprecation=29892) -from sage.env import DOT_SAGE, HOSTNAME - LOCAL_IDENTIFIER = '%s.%s' % (HOSTNAME, os.getpid()) ################################################################# @@ -95,7 +95,7 @@ def sage_makedirs(dirname, mode=0o777): raise -# We create the DOT_SAGE directory (if it doesn't exist yet; note in particular +# We create the DOT_SAGE directory (if it does not exist yet; note in particular # that it may already have been created by the bin/sage script) with # restrictive permissions, since otherwise possibly just anybody can easily see # every command you type. @@ -344,7 +344,7 @@ def cputime(t=0, subprocesses=False): else: if t == 0: ret = GlobalCputime(cputime()) - for s in sage.interfaces.quit.expect_objects: + for s in expect_objects: S = s() if S and S.is_running(): try: @@ -358,7 +358,7 @@ def cputime(t=0, subprocesses=False): if not isinstance(t, GlobalCputime): t = GlobalCputime(t) ret = GlobalCputime(cputime() - t.local) - for s in sage.interfaces.quit.expect_objects: + for s in expect_objects: S = s() if S and S.is_running(): try: @@ -832,7 +832,7 @@ def __mul__(self, right): ################################################################# # is_iterator function ################################################################# -def is_iterator(it): +def is_iterator(it) -> bool: """ Tests if it is an iterator. @@ -846,13 +846,7 @@ def is_iterator(it): sage: is_iterator(it) True - sage: class wrong(): # py2 - ....: def __init__(self): self.n = 5 - ....: def next(self): - ....: self.n -= 1 - ....: if self.n == 0: raise StopIteration - ....: return self.n - sage: class wrong(): # py3 + sage: class wrong(): ....: def __init__(self): self.n = 5 ....: def __next__(self): ....: self.n -= 1 @@ -861,11 +855,7 @@ def is_iterator(it): sage: x = wrong() sage: is_iterator(x) False - sage: list(x) # py2 - Traceback (most recent call last): - ... - TypeError: iteration over non-sequence - sage: list(x) # py3 + sage: list(x) Traceback (most recent call last): ... TypeError: 'wrong' object is not iterable @@ -926,6 +916,7 @@ def random_sublist(X, s): """ return [a for a in X if random.random() <= s] + def is_sublist(X, Y): """ Test whether ``X`` is a sublist of ``Y``. @@ -951,6 +942,7 @@ def is_sublist(X, Y): X_i += 1 return X_i == len(X) + def some_tuples(elements, repeat, bound, max_samples=None): r""" Return an iterator over at most ``bound`` number of ``repeat``-tuples of From 23f404faf194c06ae1756340f1284e5b5a75ff22 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 9 Jul 2021 11:35:55 +0100 Subject: [PATCH 263/336] #32165 fix one doctest --- src/sage/rings/number_field/number_field_ideal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index da9c90f3b2f..54d7ebb1535 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -2196,8 +2196,8 @@ def invertible_residues(self, reduce=True): Check that the integrality is not lost, cf. :trac:`30801`:: sage: K. = NumberField(x^2 + x + 1) - sage: list(K.ideal(8).invertible_residues())[:5] - [1, a - 1, -3*a, -2*a + 3, -a - 1] + sage: all(x.is_integral() for x in K.ideal(8).invertible_residues()) + True AUTHOR: John Cremona """ From d996d40eb07069b2d17f812ab383242e2a6df36a Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Fri, 9 Jul 2021 11:06:27 -0400 Subject: [PATCH 264/336] fix conversion --- src/sage/modular/modform/space.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index d4d2f78ecd3..4d2b09d0594 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1057,6 +1057,16 @@ def _element_constructor_(self, x, check=True): sage: g.is_old() True + Test that :trac:`32168` is fixed:: + + sage: M0 = ModularForms(Gamma0(8), 10) + sage: M1 = ModularForms(Gamma1(8), 10) + sage: f = M0.0; g = M1.0 + sage: f + g + 2*q + O(q^6) + sage: M1(f) + q + O(q^6) + :: sage: M = ModularForms(22,2) ; S = CuspForms(22,2) @@ -1081,7 +1091,7 @@ def _element_constructor_(self, x, check=True): sage: M(M([1, 2, 3, 4, 5]), check=True) 4 + 6*q + 47*q^2 + 143*q^3 + 358*q^4 + 630*q^5 + O(q^6) """ - if isinstance(x, self.element_class): + if isinstance(x, (self.element_class, ModularFormElement)): if x.parent() is self: return x @@ -1117,9 +1127,6 @@ def _element_constructor_(self, x, check=True): else: raise TypeError("q-expansion needed to at least precision %s" % W.degree()) - if isinstance(x, ModularFormElement): - x = x.element() - return self.element_class(self, self.free_module()(x, check)) def __richcmp__(self, x, op): From 2a64c198c5fb49085ea4e353fc7c73525800b9c4 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 9 Jul 2021 10:32:12 -0500 Subject: [PATCH 265/336] Update input documentation --- src/sage/modules/with_basis/invariant.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index 338ea6b3e50..bde1cbd640a 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -4,6 +4,7 @@ # **************************************************************************** # Copyright (C) 2021 Trevor K. Karn +# Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -32,8 +33,15 @@ class FiniteDimensionalInvariantModule(SubmoduleWithBasis): INPUT: - - ``R`` -- an instance of a ``Representation`` of a semigroup `S` - acting on the module `M` + - ``M`` - a module in the category of + :class:`~sage.categories.finite_dimensional_modules_with_basis.FiniteDimensionalModulesWithBasis` + + - ``S`` - a semigroup in the category of + :class:`~sage.categories.finitely_generated_semigroups.FinitelyGeneratedSemigroups` + + - ``action`` - (default: ``operator.mul``) the action of ``S`` on ``M``. + + - ``side`` - (default: ``'left'``) the side on which ``S`` acts. EXAMPLES: @@ -296,7 +304,7 @@ def _latex_(self): from sage.misc.latex import latex return "\\left( {} \\right)^{{{}}}".format(latex(M), latex(self._semigroup)) - def _test_invariant(self, **options): ## Lift to representation and check that the element is invariant + def _test_invariant(self, **options): """ Check (on some elements) that ``self`` is invariant. From d54ac2a52fad8b4627afd5693f80456022ab0c8c Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Fri, 9 Jul 2021 16:58:24 -0400 Subject: [PATCH 266/336] fix some redundancy --- src/sage/modular/modform/space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index 4d2b09d0594..f46dbfcaec7 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1091,7 +1091,7 @@ def _element_constructor_(self, x, check=True): sage: M(M([1, 2, 3, 4, 5]), check=True) 4 + 6*q + 47*q^2 + 143*q^3 + 358*q^4 + 630*q^5 + O(q^6) """ - if isinstance(x, (self.element_class, ModularFormElement)): + if isinstance(x, ModularFormElement): if x.parent() is self: return x From 26824697667f2eec0977b532f442e8e2ed4a8058 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 9 Jul 2021 16:32:46 -0700 Subject: [PATCH 267/336] src/sage/interfaces/sympy_wrapper.py: Use Family, not Set, in doctests to make sure that the SageSet wrapper is actually used --- src/sage/interfaces/sympy_wrapper.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py index 00f7b65dd27..77450912643 100644 --- a/src/sage/interfaces/sympy_wrapper.py +++ b/src/sage/interfaces/sympy_wrapper.py @@ -65,11 +65,11 @@ def _sage_(self): EXAMPLES:: - sage: F = Set([1, 2]) - sage: F is Set([1, 2]) + sage: F = Family([1, 2]) + sage: F is Family([1, 2]) False sage: sF = F._sympy_(); sF - SageSet({1, 2}) + SageSet(Family (1, 2)) sage: sF._sage_() is F True """ @@ -82,7 +82,7 @@ def is_empty(self): EXAMPLES:: - sage: Empty = Set([]) + sage: Empty = Family([]) sage: sEmpty = Empty._sympy_() sage: sEmpty.is_empty True From 753babb46c7bbe70faa1e0da4d7887b4fa5867e0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 9 Jul 2021 16:38:25 -0700 Subject: [PATCH 268/336] Set_object_enumerated._sympy_: Translate empty sets to EmptySet --- src/sage/sets/set.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 1055b8e1973..c4a32bf0efc 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -1206,12 +1206,23 @@ def _sympy_(self): sage: X = Set({1, 2, 3}); X {1, 2, 3} - sage: X._sympy_() + sage: sX = X._sympy_(); sX Set(1, 2, 3) + sage: sX.is_empty is None + True + + sage: Empty = Set([]); Empty + {} + sage: sEmpty = Empty._sympy_(); sEmpty + EmptySet + sage: sEmpty.is_empty + True """ - from sympy import Set + from sympy import Set, EmptySet from sage.interfaces.sympy import sympy_init sympy_init() + if self.is_empty(): + return EmptySet return Set(*[x._sympy_() for x in self]) From 1eb270a5e34e0ae634e73054d644499f10c16779 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 9 Jul 2021 20:24:36 -0700 Subject: [PATCH 269/336] src/sage/docs/conf.py: Add more \ensuremath to \DeclareUnicodeCharacter --- src/sage/docs/conf.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index 0a7bb2ca257..37c04e47d53 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -392,40 +392,40 @@ def set_intersphinx_mappings(app, config): \DeclareUnicodeCharacter{03A5}{\ensuremath{\Upsilon}} \DeclareUnicodeCharacter{2113}{\ell} - \DeclareUnicodeCharacter{2148}{\id} - \DeclareUnicodeCharacter{2202}{\partial} + \DeclareUnicodeCharacter{2148}{\ensuremath{\id}} + \DeclareUnicodeCharacter{2202}{\ensuremath{\partial}} \DeclareUnicodeCharacter{2205}{\ensuremath{\emptyset}} - \DeclareUnicodeCharacter{2208}{\in} - \DeclareUnicodeCharacter{2209}{\notin} - \DeclareUnicodeCharacter{2211}{\sum} + \DeclareUnicodeCharacter{2208}{\ensuremath{\in}} + \DeclareUnicodeCharacter{2209}{\ensuremath{\notin}} + \DeclareUnicodeCharacter{2211}{\ensuremath{\sum}} \DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}} - \DeclareUnicodeCharacter{221E}{\infty} + \DeclareUnicodeCharacter{221E}{\ensuremath{\infty}} \DeclareUnicodeCharacter{2227}{\ensuremath{\wedge}} \DeclareUnicodeCharacter{2228}{\ensuremath{\vee}} \DeclareUnicodeCharacter{2229}{\ensuremath{\cap}} \DeclareUnicodeCharacter{222A}{\ensuremath{\cup}} \DeclareUnicodeCharacter{222B}{\ensuremath{\int}} - \DeclareUnicodeCharacter{2248}{\approx} - \DeclareUnicodeCharacter{2260}{\neq} - \DeclareUnicodeCharacter{2264}{\leq} - \DeclareUnicodeCharacter{2265}{\geq} + \DeclareUnicodeCharacter{2248}{\ensuremath{\approx}} + \DeclareUnicodeCharacter{2260}{\ensuremath{\neq}} + \DeclareUnicodeCharacter{2264}{\ensuremath{\leq}} + \DeclareUnicodeCharacter{2265}{\ensuremath{\geq}} \DeclareUnicodeCharacter{2293}{\ensuremath{\sqcap}} \DeclareUnicodeCharacter{2294}{\ensuremath{\sqcup}} \DeclareUnicodeCharacter{22C0}{\ensuremath{\bigwedge}} \DeclareUnicodeCharacter{22C1}{\ensuremath{\bigvee}} \DeclareUnicodeCharacter{22C2}{\ensuremath{\bigcap}} \DeclareUnicodeCharacter{22C3}{\ensuremath{\bigcup}} - \DeclareUnicodeCharacter{00B1}{\pm} + \DeclareUnicodeCharacter{00B1}{\ensuremath{\pm}} \DeclareUnicodeCharacter{2A02}{\ensuremath{\bigotimes}} \DeclareUnicodeCharacter{2297}{\ensuremath{\otimes}} - \DeclareUnicodeCharacter{2A01}{\oplus} - \DeclareUnicodeCharacter{00BD}{\nicefrac{1}{2}} - \DeclareUnicodeCharacter{00D7}{\times} - \DeclareUnicodeCharacter{00B7}{\cdot} - \DeclareUnicodeCharacter{230A}{\lfloor} - \DeclareUnicodeCharacter{230B}{\rfloor} - \DeclareUnicodeCharacter{2308}{\lceil} - \DeclareUnicodeCharacter{2309}{\rceil} + \DeclareUnicodeCharacter{2A01}{\ensuremath{\oplus}} + \DeclareUnicodeCharacter{00BD}{\ensuremath{\nicefrac{1}{2}}} + \DeclareUnicodeCharacter{00D7}{\ensuremath{\times}} + \DeclareUnicodeCharacter{00B7}{\ensuremath{\cdot}} + \DeclareUnicodeCharacter{230A}{\ensuremath{\lfloor}} + \DeclareUnicodeCharacter{230B}{\ensuremath{\rfloor}} + \DeclareUnicodeCharacter{2308}{\ensuremath{\lceil}} + \DeclareUnicodeCharacter{2309}{\ensuremath{\rceil}} \DeclareUnicodeCharacter{22C5}{\ensuremath{\cdot}} \DeclareUnicodeCharacter{2227}{\ensuremath{\wedge}} \DeclareUnicodeCharacter{22C0}{\ensuremath{\bigwedge}} From dc6eb1e6c9cdf4159592d1016d5a33c353b68dc4 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 28 Jun 2021 15:01:53 +0200 Subject: [PATCH 270/336] #32078 catch more exceptions in (x in P) Likely since #31716, P = Frac(ZZ['x,y']) a = P(1/2) ZZ(a) raises an ArithmeticError (instead of a TypeError before), breaking (a in P). It is a reasonable thing to do for such conversions, so we fix the issue by catching all ArithmeticErrors instead of just ZeroDivisionErrors in Parent.__contains__(). --- src/sage/structure/parent.pyx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 5a46c73c26a..f56fc05b154 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1140,6 +1140,14 @@ cdef class Parent(sage.structure.category_object.CategoryObject): False sage: 15/36 in Integers(6) False + + Check that :trac:`32078` is fixed:: + + sage: P = Frac(ZZ['x,y']) + sage: P(1) in ZZ + True + sage: P(1/2) in ZZ + False """ P = parent(x) if P is self or P == self: @@ -1163,7 +1171,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject): # SR is ultra-permissive about letting other rings # coerce in, but ultra-strict about doing # comparisons. - except (TypeError, ValueError, ZeroDivisionError): + except (TypeError, ValueError, ArithmeticError): return False cpdef coerce(self, x): From 58a89c121b8c3a5ded5b4827f1b93341815a7b03 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sat, 10 Jul 2021 08:47:20 +0200 Subject: [PATCH 271/336] #32078 Revert doctest change from #21746 This reverts commit d1c54b1679d9f60b6612a98d6b1386d5ddb1b347. --- src/sage/coding/encoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 0e00a5170de..304e68cce02 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -156,7 +156,7 @@ def encode(self, word): sage: E.encode(word) Traceback (most recent call last): ... - ArithmeticError: reduction modulo 2 not defined + ValueError: The value to encode must be in Vector space of dimension 4 over Finite Field of size 2 """ M = self.message_space() if word not in M: From 997528b3c3bbc674ea8f286cf2b3dbed96b9b681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 10 Jul 2021 18:23:26 +0200 Subject: [PATCH 272/336] one more use of libgap in abelian groups --- src/sage/groups/abelian_gps/abelian_group.py | 48 +++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 6567ac6c18b..21af1fa601e 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -191,7 +191,7 @@ immutables. Rename invariants to gens_orders. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein # Copyright (C) 2006 David Joyner # Copyright (C) 2012 Volker Braun @@ -200,8 +200,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ @@ -1415,22 +1415,22 @@ def subgroups(self, check=False): INPUT: - - check: if True, performs the same computation in GAP and checks that - the number of subgroups generated is the same. (I don't know how to - convert GAP's output back into Sage, so we don't actually compare the - subgroups). + - check: if ``True``, performs the same computation in GAP and + checks that the number of subgroups generated is the + same. (I don't know how to convert GAP's output back into + Sage, so we don't actually compare the subgroups). ALGORITHM: - If the group is cyclic, the problem is easy. Otherwise, write it as - a direct product A x B, where B is cyclic. Compute the subgroups of - A (by recursion). + If the group is cyclic, the problem is easy. Otherwise, write it as + a direct product A x B, where B is cyclic. Compute the subgroups of + A (by recursion). - Now, for every subgroup C of A x B, let G be its *projection onto* - A and H its *intersection with* B. Then there is a well-defined - homomorphism f: G -> B/H that sends a in G to the class mod H of b, - where (a,b) is any element of C lifting a; and every subgroup C - arises from a unique triple (G, H, f). + Now, for every subgroup C of A x B, let G be its *projection onto* + A and H its *intersection with* B. Then there is a well-defined + homomorphism f: G -> B/H that sends a in G to the class mod H of b, + where (a,b) is any element of C lifting a; and every subgroup C + arises from a unique triple (G, H, f). .. TODO:: @@ -1460,6 +1460,9 @@ def subgroups(self, check=False): sage: B.subgroups() [Multiplicative Abelian subgroup isomorphic to C2 generated by {f1}, Trivial Abelian subgroup] + sage: B.subgroups(check=True) + [Multiplicative Abelian subgroup isomorphic to C2 generated by {f1}, + Trivial Abelian subgroup] """ if not self.is_finite(): raise ValueError("group must be finite") @@ -1469,7 +1472,7 @@ def subgroups(self, check=False): return [self] if self.ngens() == 1: n = self.gen(0).order() - return [ self.subgroup([self.gen(0)**i]) for i in divisors(n) ] + return [self.subgroup([self.gen(0)**i]) for i in divisors(n)] v = self.gens_orders() A = AbelianGroup(v[:-1]) @@ -1484,28 +1487,29 @@ def subgroups(self, check=False): verbose("invariants are: %s" % [t.order() for t in G.gens()]) for H in divisors(x): # H = the subgroup of *index* H. - its = [range(0, H, H/gcd(H, G.gen(i).order())) for i in range(ngens)] + its = [range(0, H, H // gcd(H, G.gen(i).order())) + for i in range(ngens)] for f in cartesian_product_iterator(its): - verbose("using hom from G to C_%s sending gens to %s" % (H,f)) + verbose("using hom from G to C_%s sending gens to %s" % (H, f)) new_sub = [] for a in range(ngens): val = G.gen(a).list() + [f[a]] if any(l != 0 for l in val): new_sub.append(val) if H != x: - new_sub.append([0]*A.ngens() + [H]) + new_sub.append([0] * A.ngens() + [H]) subgps.append(self.subgroup_reduced(new_sub)) if check: - from sage.interfaces.all import gap verbose("Running Gap cross-check") - t = ZZ(gap.eval("Size(SubgroupsSolvableGroup(AbelianGroup(%s)))" % v)) + from sage.libs.gap.libgap import libgap + t = libgap(v).AbelianGroup().SubgroupsSolvableGroup().Size().sage() if t != len(subgps): raise ArithmeticError("For %s Gap finds %s subgroups, I found %s" % (v, t, len(subgps))) verbose("Gap check OK for %s: %s" % (v, t)) return subgps - def subgroup_reduced(self,elts, verbose=False): + def subgroup_reduced(self, elts, verbose=False): r""" Given a list of lists of integers (corresponding to elements of self), find a set of independent generators for the subgroup generated by From 3200b93082cb001d364db4bfd6d86c774b6293da Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Jul 2021 10:51:42 -0700 Subject: [PATCH 273/336] build/pkgs/llvm: New --- build/pkgs/llvm/SPKG.rst | 22 ++++++++++++++++++++++ build/pkgs/llvm/distros/alpine.txt | 1 + build/pkgs/llvm/distros/arch.txt | 1 + build/pkgs/llvm/distros/cygwin.txt | 1 + build/pkgs/llvm/distros/debian.txt | 1 + build/pkgs/llvm/distros/fedora.txt | 1 + build/pkgs/llvm/distros/freebsd.txt | 1 + build/pkgs/llvm/distros/gentoo.txt | 1 + build/pkgs/llvm/distros/homebrew.txt | 1 + build/pkgs/llvm/distros/macports.txt | 1 + build/pkgs/llvm/distros/nix.txt | 1 + build/pkgs/llvm/distros/openbsd.txt | 1 + build/pkgs/llvm/distros/opensuse.txt | 1 + build/pkgs/llvm/distros/slackware.txt | 1 + build/pkgs/llvm/distros/void.txt | 1 + build/pkgs/llvm/spkg-configure.m4 | 3 +++ build/pkgs/llvm/spkg-install | 4 ++++ build/pkgs/llvm/type | 1 + 18 files changed, 44 insertions(+) create mode 100644 build/pkgs/llvm/SPKG.rst create mode 100644 build/pkgs/llvm/distros/alpine.txt create mode 100644 build/pkgs/llvm/distros/arch.txt create mode 100644 build/pkgs/llvm/distros/cygwin.txt create mode 100644 build/pkgs/llvm/distros/debian.txt create mode 100644 build/pkgs/llvm/distros/fedora.txt create mode 100644 build/pkgs/llvm/distros/freebsd.txt create mode 100644 build/pkgs/llvm/distros/gentoo.txt create mode 100644 build/pkgs/llvm/distros/homebrew.txt create mode 100644 build/pkgs/llvm/distros/macports.txt create mode 100644 build/pkgs/llvm/distros/nix.txt create mode 100644 build/pkgs/llvm/distros/openbsd.txt create mode 100644 build/pkgs/llvm/distros/opensuse.txt create mode 100644 build/pkgs/llvm/distros/slackware.txt create mode 100644 build/pkgs/llvm/distros/void.txt create mode 100644 build/pkgs/llvm/spkg-configure.m4 create mode 100755 build/pkgs/llvm/spkg-install create mode 100644 build/pkgs/llvm/type diff --git a/build/pkgs/llvm/SPKG.rst b/build/pkgs/llvm/SPKG.rst new file mode 100644 index 00000000000..03cf6f1a7c6 --- /dev/null +++ b/build/pkgs/llvm/SPKG.rst @@ -0,0 +1,22 @@ +llvm: The LLVM Compiler Infrastructure, including the Clang C/C++/Objective-C compiler +====================================================================================== + +Description +----------- + +The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. + +Clang is an "LLVM native" C/C++/Objective-C compiler. + +The libc++ and libc++ ABI projects provide a standard conformant and high-performance +implementation of the C++ Standard Library, including full support for C++11 and C++14. + +License +------- + +Apache 2.0 License with LLVM exceptions + +Upstream Contact +---------------- + +https://llvm.org/ diff --git a/build/pkgs/llvm/distros/alpine.txt b/build/pkgs/llvm/distros/alpine.txt new file mode 100644 index 00000000000..e671fa21003 --- /dev/null +++ b/build/pkgs/llvm/distros/alpine.txt @@ -0,0 +1 @@ +clang diff --git a/build/pkgs/llvm/distros/arch.txt b/build/pkgs/llvm/distros/arch.txt new file mode 100644 index 00000000000..e671fa21003 --- /dev/null +++ b/build/pkgs/llvm/distros/arch.txt @@ -0,0 +1 @@ +clang diff --git a/build/pkgs/llvm/distros/cygwin.txt b/build/pkgs/llvm/distros/cygwin.txt new file mode 100644 index 00000000000..e671fa21003 --- /dev/null +++ b/build/pkgs/llvm/distros/cygwin.txt @@ -0,0 +1 @@ +clang diff --git a/build/pkgs/llvm/distros/debian.txt b/build/pkgs/llvm/distros/debian.txt new file mode 100644 index 00000000000..d9c90515cfc --- /dev/null +++ b/build/pkgs/llvm/distros/debian.txt @@ -0,0 +1 @@ +llvm-toolchain diff --git a/build/pkgs/llvm/distros/fedora.txt b/build/pkgs/llvm/distros/fedora.txt new file mode 100644 index 00000000000..e671fa21003 --- /dev/null +++ b/build/pkgs/llvm/distros/fedora.txt @@ -0,0 +1 @@ +clang diff --git a/build/pkgs/llvm/distros/freebsd.txt b/build/pkgs/llvm/distros/freebsd.txt new file mode 100644 index 00000000000..0c56db703cc --- /dev/null +++ b/build/pkgs/llvm/distros/freebsd.txt @@ -0,0 +1 @@ +devel/llvm diff --git a/build/pkgs/llvm/distros/gentoo.txt b/build/pkgs/llvm/distros/gentoo.txt new file mode 100644 index 00000000000..fdca4430686 --- /dev/null +++ b/build/pkgs/llvm/distros/gentoo.txt @@ -0,0 +1 @@ +sys-devel/clang diff --git a/build/pkgs/llvm/distros/homebrew.txt b/build/pkgs/llvm/distros/homebrew.txt new file mode 100644 index 00000000000..453889a6bc6 --- /dev/null +++ b/build/pkgs/llvm/distros/homebrew.txt @@ -0,0 +1 @@ +llvm diff --git a/build/pkgs/llvm/distros/macports.txt b/build/pkgs/llvm/distros/macports.txt new file mode 100644 index 00000000000..e671fa21003 --- /dev/null +++ b/build/pkgs/llvm/distros/macports.txt @@ -0,0 +1 @@ +clang diff --git a/build/pkgs/llvm/distros/nix.txt b/build/pkgs/llvm/distros/nix.txt new file mode 100644 index 00000000000..e671fa21003 --- /dev/null +++ b/build/pkgs/llvm/distros/nix.txt @@ -0,0 +1 @@ +clang diff --git a/build/pkgs/llvm/distros/openbsd.txt b/build/pkgs/llvm/distros/openbsd.txt new file mode 100644 index 00000000000..0c56db703cc --- /dev/null +++ b/build/pkgs/llvm/distros/openbsd.txt @@ -0,0 +1 @@ +devel/llvm diff --git a/build/pkgs/llvm/distros/opensuse.txt b/build/pkgs/llvm/distros/opensuse.txt new file mode 100644 index 00000000000..453889a6bc6 --- /dev/null +++ b/build/pkgs/llvm/distros/opensuse.txt @@ -0,0 +1 @@ +llvm diff --git a/build/pkgs/llvm/distros/slackware.txt b/build/pkgs/llvm/distros/slackware.txt new file mode 100644 index 00000000000..453889a6bc6 --- /dev/null +++ b/build/pkgs/llvm/distros/slackware.txt @@ -0,0 +1 @@ +llvm diff --git a/build/pkgs/llvm/distros/void.txt b/build/pkgs/llvm/distros/void.txt new file mode 100644 index 00000000000..e671fa21003 --- /dev/null +++ b/build/pkgs/llvm/distros/void.txt @@ -0,0 +1 @@ +clang diff --git a/build/pkgs/llvm/spkg-configure.m4 b/build/pkgs/llvm/spkg-configure.m4 new file mode 100644 index 00000000000..26aceec5276 --- /dev/null +++ b/build/pkgs/llvm/spkg-configure.m4 @@ -0,0 +1,3 @@ +SAGE_SPKG_CONFIGURE([llvm], [ + sage_require_llvm=no +]) diff --git a/build/pkgs/llvm/spkg-install b/build/pkgs/llvm/spkg-install new file mode 100755 index 00000000000..4233e976c04 --- /dev/null +++ b/build/pkgs/llvm/spkg-install @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +echo Error: llvm is not installed. +echo Please install it, for example using the system packages recommended by ./configure +exit 1 diff --git a/build/pkgs/llvm/type b/build/pkgs/llvm/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/llvm/type @@ -0,0 +1 @@ +optional From 8b16cf8d7a4509f4da6029e829a71c8d8c128ad6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Jul 2021 11:17:33 -0700 Subject: [PATCH 274/336] tox.ini, build/bin/write-dockerfile.sh: Add configuration factor 'llvm' --- build/bin/write-dockerfile.sh | 3 ++- tox.ini | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 1389858fc09..94418e38cf7 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -8,13 +8,14 @@ shopt -s extglob SAGE_PACKAGE_LIST_ARGS="${2:- --has-file=spkg-configure.m4 :standard:}" WITH_SYSTEM_SPKG="${3:-yes}" IGNORE_MISSING_SYSTEM_PACKAGES="${4:-no}" +EXTRA_SAGE_PACKAGES="${5:-_bootstrap}" # STRIP_COMMENTS="sed s/#.*//;" SAGE_ROOT=. export PATH="$SAGE_ROOT"/build/bin:$PATH SYSTEM_PACKAGES= CONFIGURE_ARGS="--enable-option-checking " -for PKG_BASE in $($SAGE_ROOT/sage -package list --has-file=distros/$SYSTEM.txt $SAGE_PACKAGE_LIST_ARGS) _bootstrap; do +for PKG_BASE in $($SAGE_ROOT/sage -package list --has-file=distros/$SYSTEM.txt $SAGE_PACKAGE_LIST_ARGS) $EXTRA_SAGE_PACKAGES; do PKG_SCRIPTS="$SAGE_ROOT"/build/pkgs/$PKG_BASE if [ -d $PKG_SCRIPTS ]; then SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/distros/$SYSTEM.txt diff --git a/tox.ini b/tox.ini index 21e342350d5..78063bcdc8c 100644 --- a/tox.ini +++ b/tox.ini @@ -141,8 +141,8 @@ setenv = maximal: SAGE_PACKAGE_LIST_ARGS=:standard: :optional: conda-environment: SAGE_PACKAGE_LIST_ARGS=_prereq # Whether to add the system packages needed for bootstrapping - EXTRA_SAGE_PACKAGES=_bootstrap - nobootstrap: EXTRA_SAGE_PACKAGES= + EXTRA_SAGE_PACKAGES_0=_bootstrap + nobootstrap: EXTRA_SAGE_PACKAGES_0= # local envs need HOME set, also Docker 19.03 needs HOME {local,docker}: HOME={envdir} # for local envs we can guess the package system if it is not provided as a factor @@ -447,6 +447,8 @@ setenv = gcc_9: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-9 CXX=g++-9 FC=gfortran-9 gcc_10: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-10 CXX=g++-10 FC=gfortran-10 gcc_11: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-11 CXX=g++-11 FC=gfortran-11 + llvm: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=clang CXX=clang++ + llvm: EXTRA_SAGE_PACKAGES_2=llvm macos-nohomebrew: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC="$CONFIGURED_CC" CXX="$CONFIGURED_CXX" --with-mp=gmp --without-system-mpfr --without-system-readline --without-system-boost --without-system-boost_cropped macos-nohomebrew: CONFIGURED_CXX=g++ -isysroot {env:MACOS_SDK} macos-nohomebrew: CONFIGURED_CC=gcc -isysroot {env:MACOS_SDK} @@ -468,6 +470,10 @@ setenv = # Resulting full configuration args, including EXTRA_CONFIGURE_ARGS from the user environment # CONFIGURE_ARGS=--enable-experimental-packages --enable-download-from-upstream-url {env:CONFIG_CONFIGURE_ARGS_ROOT:} {env:CONFIG_CONFIGURE_ARGS_1:} {env:CONFIG_CONFIGURE_ARGS_2:} {env:EXTRA_CONFIGURE_ARGS:} + # + # Resulting EXTRA_SAGE_PACKAGES + # + EXTRA_SAGE_PACKAGES={env:EXTRA_SAGE_PACKAGES_0:} {env:EXTRA_SAGE_PACKAGES_1:} {env:EXTRA_SAGE_PACKAGES_2:} # environment will be skipped if regular expression does not match against the sys.platform string platform = @@ -523,7 +529,7 @@ commands = local: bash -c 'if [ ! -d prefix -o -L prefix ]; then rm -f prefix; ln -sf {env:PREFIX:{envdir}/local} prefix; fi' ##commands = - docker: bash -c 'build/bin/write-dockerfile.sh {env:SYSTEM} "{env:SAGE_PACKAGE_LIST_ARGS:}" {env:WITH_SYSTEM_SPKG} {env:IGNORE_MISSING_SYSTEM_PACKAGES} > {envdir}/Dockerfile' + docker: bash -c 'build/bin/write-dockerfile.sh {env:SYSTEM} "{env:SAGE_PACKAGE_LIST_ARGS:}" {env:WITH_SYSTEM_SPKG} {env:IGNORE_MISSING_SYSTEM_PACKAGES} "{env:EXTRA_SAGE_PACKAGES}" > {envdir}/Dockerfile' # From https://hub.docker.com/r/multiarch/ubuntu-core/ # configure binfmt-support on the Docker host (works locally or remotely, i.e: using boot2docker) docker-{arm64,armhf}: docker run --rm --privileged multiarch/qemu-user-static:register --reset From 38e3d3ee6d36986dfa24af2333a1287c7924c8ca Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Jul 2021 12:11:12 -0700 Subject: [PATCH 275/336] tox.ini [homebrew-llvm]: Set CC, CXX to full path --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 78063bcdc8c..3e5ed6c9040 100644 --- a/tox.ini +++ b/tox.ini @@ -449,6 +449,8 @@ setenv = gcc_11: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-11 CXX=g++-11 FC=gfortran-11 llvm: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=clang CXX=clang++ llvm: EXTRA_SAGE_PACKAGES_2=llvm + # LLVM is keg-only + homebrew-llvm: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC={env:HOMEBREW}/opt/llvm/bin/clang CXX={env:HOMEBREW}/opt/llvm/bin/clang++ macos-nohomebrew: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC="$CONFIGURED_CC" CXX="$CONFIGURED_CXX" --with-mp=gmp --without-system-mpfr --without-system-readline --without-system-boost --without-system-boost_cropped macos-nohomebrew: CONFIGURED_CXX=g++ -isysroot {env:MACOS_SDK} macos-nohomebrew: CONFIGURED_CC=gcc -isysroot {env:MACOS_SDK} From a2ab555e29407ae4dfb52d345fd0443ed1cc4867 Mon Sep 17 00:00:00 2001 From: Steven Trogdon Date: Sat, 10 Jul 2021 14:26:12 -0600 Subject: [PATCH 276/336] update: also set NTL_INCDIR using pkgconfig when system ntl.pc is available --- build/pkgs/ntl/spkg-configure.m4 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build/pkgs/ntl/spkg-configure.m4 b/build/pkgs/ntl/spkg-configure.m4 index 438e5d408b8..ba019d712de 100644 --- a/build/pkgs/ntl/spkg-configure.m4 +++ b/build/pkgs/ntl/spkg-configure.m4 @@ -47,11 +47,14 @@ SAGE_SPKG_CONFIGURE([ntl], [ ntl_inc_ntl_dir=`AS_DIRNAME(["$gl_cv_absolute_NTL_ZZ_h"])` ntl_inc_dir=`AS_DIRNAME(["$ntl_inc_ntl_dir"])` ntl_prefix=`AS_DIRNAME(["$ntl_inc_dir"])` - AC_SUBST(NTL_INCDIR, [$ntl_inc_dir]) + PKG_CHECK_VAR([ntl_includedir], [ntl], [includedir]) + AS_IF([test "x$ntl_includedir" = x], [AC_SUBST(NTL_INCDIR, [$ntl_inc_dir])], + [AC_SUBST(NTL_INCDIR, [$ntl_includedir])] + ) PKG_CHECK_VAR([ntl_libdir], [ntl], [libdir]) AS_IF([test "x$ntl_libdir" = x], [AC_SUBST(NTL_LIBDIR, [$ntl_prefix/lib])], - [test "x$ntl_libdir" != x], [AC_SUBST(NTL_LIBDIR, [$ntl_libdir])] - ) + [AC_SUBST(NTL_LIBDIR, [$ntl_libdir])] + ) fi ]) From 79b205373300669adfa8b9e891d1080391878108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 11 Jul 2021 08:32:21 +0200 Subject: [PATCH 277/336] annotations details in e_one_star --- src/sage/combinat/e_one_star.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/e_one_star.py b/src/sage/combinat/e_one_star.py index f37b4f59074..2cca168d20f 100644 --- a/src/sage/combinat/e_one_star.py +++ b/src/sage/combinat/e_one_star.py @@ -209,7 +209,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Union from sage.misc.functional import det from sage.structure.sage_object import SageObject @@ -436,7 +435,7 @@ def type(self): """ return self._type - def color(self, color=None) -> Color: + def color(self, color=None): r""" Return or change the color of the face. @@ -448,7 +447,7 @@ def color(self, color=None) -> Color: OUTPUT: - color + color or None EXAMPLES:: @@ -457,11 +456,13 @@ def color(self, color=None) -> Color: sage: f.color() RGB color (0.0, 0.0, 1.0) sage: f.color('red') + sage: f.color() RGB color (1.0, 0.0, 0.0) """ if color is not None: self._color = Color(color) - return self._color + else: + return self._color def _plot(self, projmat, face_contour, opacity) -> Graphics: r""" @@ -599,7 +600,6 @@ def __init__(self, faces, face_contour=None): sage: next(iter(P)).color() RGB color (0.0, 1.0, 0.0) sage: next(iter(Q)).color('yellow') - RGB color (1.0, 1.0, 0.0) sage: next(iter(P)).color() RGB color (0.0, 1.0, 0.0) """ @@ -850,7 +850,7 @@ def difference(self, other) -> Patch: else: return Patch(self._faces.difference(other)) - def dimension(self) -> Union[None, int]: + def dimension(self) -> None | int: r""" Return the dimension of the vectors of the faces of self @@ -1556,7 +1556,7 @@ def __repr__(self) -> str: def _call_on_face(self, face, color=None): r""" - Return an iterator of faces obtained by applying self on the face. + Return an iterator of faces obtained by applying ``self`` on the face. INPUT: @@ -1586,7 +1586,7 @@ def _call_on_face(self, face, color=None): @cached_method def matrix(self): r""" - Return the matrix associated with self. + Return the matrix associated with ``self``. EXAMPLES:: @@ -1603,7 +1603,7 @@ def matrix(self): @cached_method def inverse_matrix(self): r""" - Return the inverse of the matrix associated with self. + Return the inverse of the matrix associated with ``self``. EXAMPLES:: From 7fb17cef5a32e71fa908e85c660be7a220b30e5a Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Sun, 11 Jul 2021 09:38:52 +0200 Subject: [PATCH 278/336] rename _product_repr_ to _repr_product_ --- src/sage/rings/asymptotic/term_monoid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 1d0e5968b04..067b4a0e54b 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2906,7 +2906,7 @@ def _repr_(self): return 'Term with coefficient %s and growth %s' % \ (self.coefficient, self.growth) - def _product_repr_(self, latex=False): + def _repr_product_(self, latex=False): if latex: from sage.misc.latex import latex as latex_repr f = latex_repr @@ -3366,7 +3366,7 @@ def _repr_(self, latex=False): sage: (1+a)/n (a + 1)*n^(-1) """ - return self._product_repr_(latex) + return self._repr_product_(latex) def _latex_(self): r""" From 7af743f3cdab60639801f81a5b376a1bc59ecce2 Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Sun, 11 Jul 2021 09:44:18 +0200 Subject: [PATCH 279/336] added docstring to TermWithCoefficient._repr_product_ --- src/sage/rings/asymptotic/term_monoid.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 067b4a0e54b..f380d663b91 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2907,6 +2907,30 @@ def _repr_(self): (self.coefficient, self.growth) def _repr_product_(self, latex=False): + r""" + A representation string for this term with coefficient as a product. + + INPUT: + + - ``latex`` -- (default: ``False``) a boolean. If set, then + LaTeX-output is returned + + OUTPUT: + + A string + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import TermWithCoefficientMonoid + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + + sage: G = GrowthGroup('x^ZZ'); x = G.gen() + sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) + sage: T(x^2, 5)._repr_product_() + '5*x^2' + """ if latex: from sage.misc.latex import latex as latex_repr f = latex_repr From 1db8a42d0288607f913c1ea279e295034e00c3d0 Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Sun, 11 Jul 2021 09:46:41 +0200 Subject: [PATCH 280/336] Changed the call to the product representation in ExactTerm._repr_ to self._repr_product_(latex=latex) --- src/sage/rings/asymptotic/term_monoid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index f380d663b91..435478f6444 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -3390,7 +3390,7 @@ def _repr_(self, latex=False): sage: (1+a)/n (a + 1)*n^(-1) """ - return self._repr_product_(latex) + return self._repr_product_(latex=latex) def _latex_(self): r""" From 70e49b6312d9ea8d6288237b46b3d7f9c722d12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 11 Jul 2021 11:07:03 +0200 Subject: [PATCH 281/336] get rid of some ParentWithBase, specially in modular/ --- src/sage/modular/abvar/abvar.py | 7 +-- src/sage/modular/local_comp/smoothchar.py | 12 ++--- .../modular/overconvergent/weightspace.py | 54 +++++++++---------- .../multi_polynomial_libsingular.pyx | 2 - src/sage/structure/all.py | 3 -- 5 files changed, 36 insertions(+), 42 deletions(-) diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index 9483e6fb8d6..9367c4d3d37 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -38,7 +38,7 @@ from sage.structure.sequence import Sequence, Sequence_generic from sage.structure.richcmp import (richcmp_method, richcmp_not_equal, rich_to_bool) -from sage.structure.parent_base import ParentWithBase +from sage.structure.parent import Parent from .morphism import HeckeOperator, Morphism, DegeneracyMap from .torsion_subgroup import RationalTorsionSubgroup, QQbarTorsionSubgroup from .finite_subgroup import (FiniteSubgroup_lattice, FiniteSubgroup, @@ -96,7 +96,7 @@ def is_ModularAbelianVariety(x) -> bool: @richcmp_method -class ModularAbelianVariety_abstract(ParentWithBase): +class ModularAbelianVariety_abstract(Parent): def __init__(self, groups, base_field, is_simple=None, newform_level=None, isogeny_number=None, number=None, check=True): """ @@ -165,7 +165,8 @@ def __init__(self, groups, base_field, is_simple=None, newform_level=None, self.__isogeny_number = isogeny_number if check and not is_Ring(base_field) and base_field.is_field(): raise TypeError("base_field must be a field") - ParentWithBase.__init__(self, base_field, category=ModularAbelianVarieties(base_field)) + Parent.__init__(self, base=base_field, + category=ModularAbelianVarieties(base_field)) def groups(self): r""" diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index 92135f7f71d..7e2a776e700 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -42,7 +42,7 @@ """ import operator from sage.structure.element import MultiplicativeGroupElement, parent -from sage.structure.parent_base import ParentWithBase +from sage.structure.parent import Parent from sage.structure.sequence import Sequence from sage.structure.richcmp import richcmp_not_equal, richcmp from sage.rings.all import QQ, ZZ, Zmod, NumberField @@ -54,7 +54,7 @@ from sage.categories.rings import Rings from sage.misc.mrange import xmrange from sage.misc.verbose import verbose -from sage.modular.dirichlet import DirichletGroup +from sage.modular.dirichlet import DirichletGroup class SmoothCharacterGeneric(MultiplicativeGroupElement): @@ -337,7 +337,7 @@ def galois_conjugate(self): return self.parent().character(self.level(), [self(sig(x)) for x in self.parent().unit_gens(self.level())]) -class SmoothCharacterGroupGeneric(ParentWithBase): +class SmoothCharacterGroupGeneric(Parent): r""" The group of smooth (i.e. locally constant) characters of a `p`-adic field, with values in some ring `R`. This is an abstract base class and should not @@ -359,10 +359,10 @@ def __init__(self, p, base_ring): """ if base_ring not in Rings(): raise TypeError("base ring (=%s) must be a ring" % base_ring) - ParentWithBase.__init__(self, base=base_ring, - category=Groups().Commutative()) + Parent.__init__(self, base=base_ring, + category=Groups().Commutative()) if not (p in ZZ and ZZ(p).is_prime()): - raise ValueError( "p (=%s) must be a prime integer" % p ) + raise ValueError("p (=%s) must be a prime integer" % p) self._p = ZZ.coerce(p) def _element_constructor_(self, x): diff --git a/src/sage/modular/overconvergent/weightspace.py b/src/sage/modular/overconvergent/weightspace.py index 66297610aa8..78a64804f71 100644 --- a/src/sage/modular/overconvergent/weightspace.py +++ b/src/sage/modular/overconvergent/weightspace.py @@ -17,11 +17,11 @@ sage: W = pAdicWeightSpace(17) sage: W - Space of 17-adic weight-characters defined over '17-adic Field with capped relative precision 20' + Space of 17-adic weight-characters defined over 17-adic Field with capped relative precision 20 sage: R. = QQ[] sage: L = Qp(17).extension(x^2 - 17, names='a'); L.rename('L') sage: W.base_extend(L) - Space of 17-adic weight-characters defined over 'L' + Space of 17-adic weight-characters defined over L We create a simple element of `\mathcal{W}`: the algebraic character, `x \mapsto x^6`:: @@ -61,10 +61,10 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** -from sage.structure.parent_base import ParentWithBase +from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import richcmp from sage.modular.dirichlet import DirichletGroup, trivial_character @@ -94,9 +94,9 @@ def WeightSpace_constructor(p, base_ring=None): EXAMPLES:: sage: pAdicWeightSpace(3) # indirect doctest - Space of 3-adic weight-characters defined over '3-adic Field with capped relative precision 20' + Space of 3-adic weight-characters defined over 3-adic Field with capped relative precision 20 sage: pAdicWeightSpace(3, QQ) - Space of 3-adic weight-characters defined over 'Rational Field' + Space of 3-adic weight-characters defined over Rational Field sage: pAdicWeightSpace(10) Traceback (most recent call last): ... @@ -113,7 +113,7 @@ def WeightSpace_constructor(p, base_ring=None): return m -class WeightSpace_class(ParentWithBase): +class WeightSpace_class(Parent): r""" The space of `p`-adic weight-characters `\mathcal{W} = {\rm Hom}(\ZZ_p^\times, \CC_p^\times)`. @@ -137,25 +137,25 @@ def __init__(self, p, base_ring): EXAMPLES:: sage: pAdicWeightSpace(17) - Space of 17-adic weight-characters defined over '17-adic Field with capped relative precision 20' + Space of 17-adic weight-characters defined over 17-adic Field with capped relative precision 20 """ - ParentWithBase.__init__(self, base=base_ring) + Parent.__init__(self, base=base_ring) p = ZZ(p) if not p.is_prime(): raise ValueError("p must be prime") self._p = p self._param = Qp(p)((p == 2 and 5) or (p + 1)) - def _repr_(self): + def _repr_(self) -> str: r""" String representation of ``self``. EXAMPLES:: sage: pAdicWeightSpace(17)._repr_() - "Space of 17-adic weight-characters defined over '17-adic Field with capped relative precision 20'" + 'Space of 17-adic weight-characters defined over 17-adic Field with capped relative precision 20' """ - return "Space of %s-adic weight-characters defined over '%s'" % (self.prime(), self.base_ring()) + return "Space of %s-adic weight-characters defined over %s" % (self.prime(), self.base_ring()) def __reduce__(self): r""" @@ -168,7 +168,7 @@ def __reduce__(self): """ return (WeightSpace_constructor, (self.prime(), self.base_ring())) - def __call__(self, arg1, arg2 = None, algebraic=True): + def _element_constructor_(self, arg1, arg2=None, algebraic=True): r""" Create an element of this space. @@ -241,7 +241,7 @@ def base_extend(self, R): sage: W = pAdicWeightSpace(3, QQ) sage: W.base_extend(Qp(3)) - Space of 3-adic weight-characters defined over '3-adic Field with capped relative precision 20' + Space of 3-adic weight-characters defined over 3-adic Field with capped relative precision 20 sage: W.base_extend(IntegerModRing(12)) Traceback (most recent call last): ... @@ -252,9 +252,9 @@ def base_extend(self, R): else: raise TypeError("No coercion map from '%s' to '%s' is defined" % (self.base_ring(), R)) - def _coerce_impl(self, x): + def _coerce_map_from_(self, other): r""" - Canonical coercion of x into self. + Canonical coercion of ``other`` into ``self``. TESTS:: @@ -264,11 +264,9 @@ def _coerce_impl(self, x): sage: W2.coerce(w) # indirect doctest 3 """ - if isinstance(x, WeightCharacter) \ - and x.parent().prime() == self.prime() \ - and self.base_ring().has_coerce_map_from(x.base_ring()): - return self._coerce_in_wtchar(x) - raise TypeError + return (isinstance(other, WeightSpace_class) + and other.prime() == self.prime() + and self.base_ring().has_coerce_map_from(other.base_ring())) def _coerce_in_wtchar(self, x): r""" @@ -308,14 +306,14 @@ def __init__(self, parent): sage: pAdicWeightSpace(17)(0) 0 """ - Element.__init__(self, parent) self._p = self.parent().prime() def base_extend(self, R): r""" - Extend scalars to the base ring R (which must have a canonical map from - the current base ring) + Extend scalars to the base ring R. + + The ring R must have a canonical map from the current base ring. EXAMPLES:: @@ -325,7 +323,7 @@ def base_extend(self, R): """ return self.parent().base_extend(R).coerce(self) - def is_even(self): + def is_even(self) -> bool: r""" Return True if this weight-character sends -1 to +1. @@ -380,7 +378,7 @@ def values_on_gens(self): return ( self(self.parent()._param), self.teichmuller_type()) - def is_trivial(self): + def is_trivial(self) -> bool: r""" Return True if and only if this is the trivial character. @@ -395,7 +393,7 @@ def is_trivial(self): """ return self.values_on_gens() == (1, 0) - def _richcmp_(self, other, op): + def _richcmp_(self, other, op) -> bool: r""" Compare ``self`` to ``other``. diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 831113a5c94..cddfd4e35a3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -235,8 +235,6 @@ from sage.rings.number_field.number_field_base cimport NumberField from sage.structure.element import coerce_binop from sage.structure.parent cimport Parent -from sage.structure.parent_base cimport ParentWithBase -from sage.structure.parent_gens cimport ParentWithGens from sage.structure.category_object cimport CategoryObject from sage.structure.coerce cimport coercion_model diff --git a/src/sage/structure/all.py b/src/sage/structure/all.py index 459ece332a3..324efd515c2 100644 --- a/src/sage/structure/all.py +++ b/src/sage/structure/all.py @@ -1,4 +1,3 @@ - from .factorization import Factorization from .sequence import Sequence, seq @@ -17,8 +16,6 @@ from .parent import Parent -from .parent_base import ParentWithBase - from .parent_gens import ParentWithGens, localvars from .proof import all as proof From 888dda9b36bf322d437dc8c1c4bd7f9cf0163e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 11 Jul 2021 13:14:58 +0200 Subject: [PATCH 282/336] fix for trac 32179 --- src/sage/interfaces/interface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 3a0e4e9ff70..7ace8e6ba03 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -633,6 +633,7 @@ def __getattr__(self, attrname): """ TESTS:: + sage: from sage.structure.parent_base import ParentWithBase sage: ParentWithBase.__getattribute__(singular, '_coerce_map_from_') """ @@ -647,7 +648,7 @@ def console(self): raise NotImplementedError def help(self, s): - return AsciiArtString('No help on %s available'%s) + return AsciiArtString('No help on %s available' % s) @instancedoc @@ -660,7 +661,7 @@ def __init__(self, parent, name): self._name = name def _repr_(self): - return "%s"%self._name + return "%s" % self._name def __call__(self, *args, **kwds): return self._parent.function_call(self._name, list(args), kwds) From f99b446ac3f0a2c17736a082f0cb26561cc4aeb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 11 Jul 2021 13:39:25 +0200 Subject: [PATCH 283/336] details in overconvergent weightspace --- src/sage/modular/overconvergent/weightspace.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/overconvergent/weightspace.py b/src/sage/modular/overconvergent/weightspace.py index 78a64804f71..08aacc88588 100644 --- a/src/sage/modular/overconvergent/weightspace.py +++ b/src/sage/modular/overconvergent/weightspace.py @@ -63,6 +63,7 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** +import weakref from sage.structure.parent import Parent from sage.structure.element import Element @@ -73,7 +74,7 @@ from sage.rings.padics.padic_generic_element import pAdicGenericElement from sage.misc.cachefunc import cached_method from sage.rings.padics.precision_error import PrecisionError -import weakref +from sage.categories.sets_cat import Sets _wscache = {} @@ -139,7 +140,7 @@ def __init__(self, p, base_ring): sage: pAdicWeightSpace(17) Space of 17-adic weight-characters defined over 17-adic Field with capped relative precision 20 """ - Parent.__init__(self, base=base_ring) + Parent.__init__(self, base=base_ring, category=Sets()) p = ZZ(p) if not p.is_prime(): raise ValueError("p must be prime") From f6c54ff5fcc6a3db5d99f6fc76f264401485214e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 11 Jul 2021 14:36:34 +0200 Subject: [PATCH 284/336] refactor imports in pbori --- src/sage/rings/polynomial/pbori/PyPolyBoRi.py | 5 ++++- src/sage/rings/polynomial/pbori/__init__.py | 3 ++- src/sage/rings/polynomial/pbori/blocks.py | 6 +++--- .../rings/polynomial/pbori/easy_polynomials.py | 2 +- src/sage/rings/polynomial/pbori/fglm.py | 4 ++-- src/sage/rings/polynomial/pbori/gbcore.py | 3 ++- src/sage/rings/polynomial/pbori/interpolate.py | 5 ++--- src/sage/rings/polynomial/pbori/interred.py | 3 ++- src/sage/rings/polynomial/pbori/ll.py | 11 +++++------ src/sage/rings/polynomial/pbori/nf.py | 7 ++++--- src/sage/rings/polynomial/pbori/parallel.py | 14 +++++--------- src/sage/rings/polynomial/pbori/randompoly.py | 9 ++++----- src/sage/rings/polynomial/pbori/specialsets.py | 7 +++---- src/sage/rings/polynomial/pbori/statistics.py | 4 ++-- 14 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py index ea1b5e9c47a..e3f15d58ad6 100644 --- a/src/sage/rings/polynomial/pbori/PyPolyBoRi.py +++ b/src/sage/rings/polynomial/pbori/PyPolyBoRi.py @@ -65,7 +65,10 @@ [a, b, c, d, e, y2] """ -from sage.rings.polynomial.pbori.pbori import * +from .pbori import (order_dict, TermOrder_from_pb_order, BooleanPolynomialRing, + BooleanPolynomialVector, MonomialFactory, + PolynomialFactory, VariableFactory, add_up_polynomials, + gauss_on_polys) import weakref diff --git a/src/sage/rings/polynomial/pbori/__init__.py b/src/sage/rings/polynomial/pbori/__init__.py index b4dc00794bd..34f33571cca 100644 --- a/src/sage/rings/polynomial/pbori/__init__.py +++ b/src/sage/rings/polynomial/pbori/__init__.py @@ -10,7 +10,8 @@ PolyBoRi features a powerful reference implementation for Groebner basis computation. AUTHOR: - The PolyBoRi Team, 2007-2011 + +The PolyBoRi Team, 2007-2011 REFERENCES: M. Brickenstein, A. Dreyer, G. Greuel, M. Wedler, O. Wienand, diff --git a/src/sage/rings/polynomial/pbori/blocks.py b/src/sage/rings/polynomial/pbori/blocks.py index 0f37364d134..9e189de5ce5 100644 --- a/src/sage/rings/polynomial/pbori/blocks.py +++ b/src/sage/rings/polynomial/pbori/blocks.py @@ -1,9 +1,9 @@ import sys - -from .PyPolyBoRi import Ring, VariableBlock, Polynomial -from .PyPolyBoRi import VariableFactory, Variable from itertools import chain, islice +from .pbori import VariableBlock +from .PyPolyBoRi import (Ring, Polynomial, VariableFactory, Variable) + class Block(object): r""" diff --git a/src/sage/rings/polynomial/pbori/easy_polynomials.py b/src/sage/rings/polynomial/pbori/easy_polynomials.py index 6b2372c1dac..8641e93afa3 100644 --- a/src/sage/rings/polynomial/pbori/easy_polynomials.py +++ b/src/sage/rings/polynomial/pbori/easy_polynomials.py @@ -1,5 +1,5 @@ from .interpolate import variety_lex_leading_terms, nf_lex_points -from .PyPolyBoRi import easy_linear_factors +from .pbori import easy_linear_factors def easy_linear_polynomials(p): diff --git a/src/sage/rings/polynomial/pbori/fglm.py b/src/sage/rings/polynomial/pbori/fglm.py index 1afa8792d4e..bbcfd33fe35 100644 --- a/src/sage/rings/polynomial/pbori/fglm.py +++ b/src/sage/rings/polynomial/pbori/fglm.py @@ -1,5 +1,5 @@ -from .PyPolyBoRi import (BooleSet, Polynomial, BoolePolynomialVector, - FGLMStrategy) +from .PyPolyBoRi import Polynomial, BoolePolynomialVector +from .pbori import FGLMStrategy, BooleSet def _fglm(I, from_ring, to_ring): diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py index ad2e784a433..6acc44db81e 100644 --- a/src/sage/rings/polynomial/pbori/gbcore.py +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -3,8 +3,9 @@ from inspect import getfullargspec as getargspec from .nf import GeneratorLimitExceeded, symmGB_F2_C, symmGB_F2_python +from .pbori import GroebnerStrategy, ll_red_nf_redsb from .PyPolyBoRi import (Monomial, Polynomial, - GroebnerStrategy, OrderCode, ll_red_nf_redsb) + OrderCode) from .ll import eliminate, ll_encode from .statistics import used_vars_set from .heuristics import dense_system, gauss_on_linear diff --git a/src/sage/rings/polynomial/pbori/interpolate.py b/src/sage/rings/polynomial/pbori/interpolate.py index 79edb097636..68f25d57bb2 100644 --- a/src/sage/rings/polynomial/pbori/interpolate.py +++ b/src/sage/rings/polynomial/pbori/interpolate.py @@ -61,9 +61,9 @@ def gen_random_o_z(points, points_p): def variety_lex_leading_terms(points, variables): + assert isinstance(points, BooleSet), "Points needs to be a BooleSet" ring = variables.ring() standards = BooleSet(ring.zero()) - assert type(points) == BooleSet, "Points needs to be a BooleSet" points_tuple = tuple(points) myvars_div = variables.divisors() if points != myvars_div: @@ -78,8 +78,7 @@ def variety_lex_leading_terms(points, variables): len_standards = len(standards) standards_old = standards - leading_terms = BooleSet(myvars_div.diff(standards)).minimal_elements() - return leading_terms + return BooleSet(myvars_div.diff(standards)).minimal_elements() def lex_groebner_basis_points(points, variables): diff --git a/src/sage/rings/polynomial/pbori/interred.py b/src/sage/rings/polynomial/pbori/interred.py index 724b9e5cd4b..4cc8a67f8bf 100644 --- a/src/sage/rings/polynomial/pbori/interred.py +++ b/src/sage/rings/polynomial/pbori/interred.py @@ -1,4 +1,5 @@ -from .PyPolyBoRi import Polynomial, ReductionStrategy +from .pbori import ReductionStrategy +from .PyPolyBoRi import Polynomial def interred(l, completely=False): diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py index 48c43220a89..e7d7e5e6347 100644 --- a/src/sage/rings/polynomial/pbori/ll.py +++ b/src/sage/rings/polynomial/pbori/ll.py @@ -1,9 +1,8 @@ -from sage.rings.polynomial.pbori.pbori import (top_index, if_then_else, - substitute_variables) -from .PyPolyBoRi import (BooleSet, Polynomial, Monomial, Ring, - BoolePolynomialVector, - ll_red_nf_redsb, ll_red_nf_noredsb, - ll_red_nf_noredsb_single_recursive_call) +from .pbori import (top_index, if_then_else, + substitute_variables, BooleSet, + ll_red_nf_redsb, ll_red_nf_noredsb, + ll_red_nf_noredsb_single_recursive_call) +from .PyPolyBoRi import (Polynomial, Monomial, Ring, BoolePolynomialVector) from .statistics import used_vars_set from .rank import rank diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py index 57697c0dbe8..55f71b0686d 100644 --- a/src/sage/rings/polynomial/pbori/nf.py +++ b/src/sage/rings/polynomial/pbori/nf.py @@ -1,7 +1,8 @@ from sage.rings.polynomial.pbori.pbori import mod_mon_set -from .PyPolyBoRi import (BooleSet, Monomial, Polynomial, Variable, - GroebnerStrategy, ReductionStrategy, parallel_reduce, - easy_linear_factors, BoolePolynomialVector) +from .pbori import (BooleSet, GroebnerStrategy, ReductionStrategy, + parallel_reduce, easy_linear_factors) +from .PyPolyBoRi import (Monomial, Polynomial, Variable, + BoolePolynomialVector) from .easy_polynomials import (easy_linear_polynomials as easy_linear_polynomials_func) from .statistics import used_vars_set diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py index 67d9322983e..bd3c4730b05 100644 --- a/src/sage/rings/polynomial/pbori/parallel.py +++ b/src/sage/rings/polynomial/pbori/parallel.py @@ -6,13 +6,13 @@ Created by Michael Brickenstein on 2008-10-31. Copyright 2008 The PolyBoRi Team """ -from sage.rings.polynomial.pbori.pbori import if_then_else -from .PyPolyBoRi import CCuddNavigator, BooleSet -from .PyPolyBoRi import (Polynomial, Ring, WeakRingRef, Monomial, Variable) -from .gbcore import groebner_basis from zlib import compress, decompress import copyreg +from .pbori import if_then_else, BooleSet, CCuddNavigator +from .PyPolyBoRi import (Polynomial, Ring, WeakRingRef, Monomial, Variable) +from .gbcore import groebner_basis + def to_fast_pickable(l): r""" @@ -157,11 +157,7 @@ def from_fast_pickable(l, r): def _calculate_gb_with_keywords(args): (I, kwds_as_single_arg) = args - import traceback - try: - return groebner_basis(I, **kwds_as_single_arg) - except: - raise ValueError(traceback.format_exc()) + return groebner_basis(I, **kwds_as_single_arg) def _decode_polynomial(code): diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py index 2efc2105eab..b0327a50062 100644 --- a/src/sage/rings/polynomial/pbori/randompoly.py +++ b/src/sage/rings/polynomial/pbori/randompoly.py @@ -1,8 +1,9 @@ -from .PyPolyBoRi import (Monomial, random_set, Polynomial, - set_random_seed, ll_red_nf_redsb, Variable) -from .ll import ll_encode from random import Random from pprint import pformat + +from .PyPolyBoRi import (Monomial, Polynomial, Variable) +from .pbori import random_set, set_random_seed, ll_red_nf_redsb +from .ll import ll_encode from .blocks import declare_ring @@ -87,8 +88,6 @@ def sparse_random_system(ring, number_of_polynomials, variables_per_polynomial, return [p + ll_red_nf_redsb(p, solutions) for p in res] - - def sparse_random_system_data_file_content(number_of_variables, **kwds): r""" TESTS:: diff --git a/src/sage/rings/polynomial/pbori/specialsets.py b/src/sage/rings/polynomial/pbori/specialsets.py index 5a988da4a81..74f56c2faf5 100644 --- a/src/sage/rings/polynomial/pbori/specialsets.py +++ b/src/sage/rings/polynomial/pbori/specialsets.py @@ -1,7 +1,6 @@ -from sage.rings.polynomial.pbori.pbori import (top_index, if_then_else, - mod_mon_set) -from .PyPolyBoRi import (BooleSet, Polynomial, - Monomial, BooleConstant, Variable) +from .pbori import (top_index, if_then_else, + mod_mon_set, BooleSet, BooleConstant) +from .PyPolyBoRi import (Polynomial, Monomial, Variable) def all_monomials_of_degree_d_old(d, variables): diff --git a/src/sage/rings/polynomial/pbori/statistics.py b/src/sage/rings/polynomial/pbori/statistics.py index f4608fc50d6..93cced307a7 100644 --- a/src/sage/rings/polynomial/pbori/statistics.py +++ b/src/sage/rings/polynomial/pbori/statistics.py @@ -1,5 +1,5 @@ -from sage.rings.polynomial.pbori.pbori import top_index -from .PyPolyBoRi import Monomial, Polynomial, BooleConstant +from .pbori import top_index, BooleConstant +from .PyPolyBoRi import Monomial, Polynomial def used_vars(l, bound=None): From c11b2f46051c9438d90bf45c69d7437c421b3b48 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 11 Jul 2021 19:32:43 +0200 Subject: [PATCH 285/336] Trac #32153: fix missing full stop --- src/sage/rings/asymptotic/term_monoid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 435478f6444..51ddd16dd1d 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2913,7 +2913,7 @@ def _repr_product_(self, latex=False): INPUT: - ``latex`` -- (default: ``False``) a boolean. If set, then - LaTeX-output is returned + LaTeX-output is returned. OUTPUT: From 88e7d95ddd4e7a640f8c9077977478fbce51d98b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 11 Jul 2021 10:45:35 -0700 Subject: [PATCH 286/336] build/pkgs/suitesparse/spkg-install.in: Stop suitesparse from using -std=c99 with C++ compilers --- build/pkgs/suitesparse/spkg-install.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build/pkgs/suitesparse/spkg-install.in b/build/pkgs/suitesparse/spkg-install.in index 85acff88db1..78701c659f0 100644 --- a/build/pkgs/suitesparse/spkg-install.in +++ b/build/pkgs/suitesparse/spkg-install.in @@ -1,5 +1,12 @@ +# -*- shell-script -*- cd src +# Trac #30478: suitesparse 5.10.1 uses -std=c99 even with g++ +# by the following setting in SuiteSparse_config/SuiteSparse_config.mk +# CF ?= -std=c99 $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $(OPTIMIZATION) -fexceptions -fPIC +# Override this: +export CF="${CFLAGS} ${CPPFLAGS} -fexceptions -fPIC" + echo "print configuration" $MAKE MY_METIS_LIB=none CHOLMOD_CONFIG=-DNPARTITION \ BLAS="$(pkg-config --libs blas)" LAPACK="$(pkg-config --libs lapack)" \ From 9e7e1fd7e2dec550f016a228c14e5c11a67bff20 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 11 Jul 2021 14:04:37 -0400 Subject: [PATCH 287/336] move PolyhedralComplex from sage.homology to sage.geometry --- src/doc/en/reference/discrete_geometry/index.rst | 8 ++++++++ src/sage/geometry/all.py | 1 + .../{homology => geometry}/polyhedral_complex.py | 14 +++++++------- 3 files changed, 16 insertions(+), 7 deletions(-) rename src/sage/{homology => geometry}/polyhedral_complex.py (99%) diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 6975fa144a7..a5d9663c5d7 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -47,6 +47,14 @@ Lattice polyhedra sage/geometry/polyhedron/ppl_lattice_polygon sage/geometry/polyhedron/ppl_lattice_polytope +Polyhedral complexes +~~~~~~~~~~~~~~~~~~~~ + +.. toctree:: + :maxdepth: 1 + + sage/geometry/polyhedral_complex + Toric geometry ~~~~~~~~~~~~~~ diff --git a/src/sage/geometry/all.py b/src/sage/geometry/all.py index f143f171600..3b7dcf756d8 100644 --- a/src/sage/geometry/all.py +++ b/src/sage/geometry/all.py @@ -1,5 +1,6 @@ from .polyhedron.all import * from .hyperbolic_space.all import * +from .polyhedral_complex import PolyhedralComplex from sage.misc.lazy_import import lazy_import lazy_import('sage.geometry.cone', ['Cone', 'random_cone']) diff --git a/src/sage/homology/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py similarity index 99% rename from src/sage/homology/polyhedral_complex.py rename to src/sage/geometry/polyhedral_complex.py index 8f5006af093..34965a27225 100644 --- a/src/sage/homology/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -13,16 +13,16 @@ In this context, a "polyhedron" means the geometric realization of a polyhedron. This is in contrast to :mod:`simplicial complex -`, whose cells are abstract simplices. +`, whose cells are abstract simplices. The concept of a polyhedral complex generalizes that of a **geometric** simplicial complex. .. note:: This class derives from - :class:`~sage.homology.cell_complex.GenericCellComplex`, and so + :class:`~sage.topology.cell_complex.GenericCellComplex`, and so inherits its methods. Some of those methods are not listed here; - see the :mod:`Generic Cell Complex ` + see the :mod:`Generic Cell Complex ` page instead. AUTHORS: @@ -48,7 +48,7 @@ :meth:`~PolyhedralComplex.cells` | Return the dictionary of the cells in this polyhedral complex. :meth:`~PolyhedralComplex.cell_iterator` | Return an iterator over cells in this polyhedral complex. :meth:`~PolyhedralComplex.cells_sorted` | Return the sorted list of all cells in this polyhedral complex. - :meth:`~sage.homology.cell_complex.GenericCellComplex.n_cells` | List the cells of dimension `n` in this polyhedral complex. + :meth:`~sage.topology.cell_complex.GenericCellComplex.n_cells` | List the cells of dimension `n` in this polyhedral complex. :meth:`~PolyhedralComplex._n_cells_sorted` | Return the sorted list of `n`-cells in this polyhedral complex. :meth:`~PolyhedralComplex.has_cell` | Return ``True`` if the given cell is in this polyhedral complex. :meth:`~PolyhedralComplex.face_poset` | Return the poset of nonempty cells in the polyhedral complex. @@ -130,7 +130,7 @@ # **************************************************************************** from copy import copy -from sage.homology.cell_complex import GenericCellComplex +from sage.topology.cell_complex import GenericCellComplex from sage.geometry.polyhedron.constructor import Polyhedron from sage.geometry.polyhedron.base import is_Polyhedron from sage.modules.free_module_element import vector @@ -2063,7 +2063,7 @@ def remove_cell(self, cell, check=False): ValueError: this polyhedral complex is not mutable Check that this function is coherent with - :meth:`~sage.homology.simplicial_complex.SimplicialComplex.remove_face`:: + :meth:`~sage.topology.simplicial_complex.SimplicialComplex.remove_face`:: sage: v1 = (1, 0, 0, 0); v2 = (0, 1, 0, 0); v3 = (0, 0, 1, 0); v4 = (0, 0, 0, 1) sage: Z = PolyhedralComplex([Polyhedron(vertices=[v1, v2, v3, v4])]); Z @@ -2388,7 +2388,7 @@ def cells_list_to_cells_dict(cells_list): sage: p2 = Polyhedron(vertices=[(1, 1), (0, 0)]) sage: p3 = Polyhedron(vertices=[(0, 0)]) sage: p4 = Polyhedron(vertices=[(1, 1)]) - sage: sage.homology.polyhedral_complex.cells_list_to_cells_dict([p1, p2, p3, p4]) + sage: sage.geometry.polyhedral_complex.cells_list_to_cells_dict([p1, p2, p3, p4]) {0: {A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex, A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex}, 1: {A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices}, From 3188e747c31d7a5f933290f1c663510893087cfc Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 11 Jul 2021 16:18:32 -0400 Subject: [PATCH 288/336] revise according to ticket 31748 comment 43 --- src/sage/geometry/polyhedral_complex.py | 58 ++++++++++++++++++------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index 34965a27225..3e5cff7a2b5 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -44,13 +44,13 @@ :meth:`~PolyhedralComplex.maximal_cells_sorted` | Return the sorted list of all maximal cells in this polyhedral complex. :meth:`~PolyhedralComplex.n_maximal_cells` | List the maximal cells of dimension `n` in this polyhedral complex. :meth:`~PolyhedralComplex._n_maximal_cells_sorted` | Return the sorted list of maximal cells of dim `n` in this complex. - :meth:`~PolyhedralComplex.has_maximal_cell` | Return ``True`` if the given cell is a maximal cell in this complex. + :meth:`~PolyhedralComplex.is_maximal_cell` | Return ``True`` if the given cell is a maximal cell in this complex. :meth:`~PolyhedralComplex.cells` | Return the dictionary of the cells in this polyhedral complex. :meth:`~PolyhedralComplex.cell_iterator` | Return an iterator over cells in this polyhedral complex. :meth:`~PolyhedralComplex.cells_sorted` | Return the sorted list of all cells in this polyhedral complex. :meth:`~sage.topology.cell_complex.GenericCellComplex.n_cells` | List the cells of dimension `n` in this polyhedral complex. :meth:`~PolyhedralComplex._n_cells_sorted` | Return the sorted list of `n`-cells in this polyhedral complex. - :meth:`~PolyhedralComplex.has_cell` | Return ``True`` if the given cell is in this polyhedral complex. + :meth:`~PolyhedralComplex.is_cell` | Return ``True`` if the given cell is in this polyhedral complex. :meth:`~PolyhedralComplex.face_poset` | Return the poset of nonempty cells in the polyhedral complex. :meth:`~PolyhedralComplex.relative_boundary_cells` | List the maximal cells on the boundary of the polyhedral complex. @@ -204,7 +204,8 @@ class PolyhedralComplex(GenericCellComplex): (A vertex at (0, 1/4),), (A vertex at (1/7, 2/7),), (A vertex at (1/3, 1/3),)] - sage: pc.plot() # not tested + sage: pc.plot() + Graphics object consisting of 10 graphics primitives sage: pc.is_pure() True sage: pc.is_full_dimensional() @@ -275,8 +276,10 @@ def __init__(self, maximal_cells=None, backend=None, maximality_check=True, EXAMPLES:: - sage: PolyhedralComplex([Polyhedron(vertices=[(1, 1), (0, 0)])]) + sage: pc = PolyhedralComplex([Polyhedron(vertices=[(1, 1), (0, 0)])]) + sage: pc Polyhedral complex with 1 maximal cell + sage: TestSuite(pc).run() """ self._backend = backend if maximal_cells is None: @@ -642,7 +645,7 @@ def maximal_cells_sorted(self): self._maximal_cells_sorted = maximal_cells return self._maximal_cells_sorted - def has_maximal_cell(self, c): + def is_maximal_cell(self, c): """ Return whether the given cell ``c`` is a maximal cell of ``self``. @@ -657,23 +660,23 @@ def has_maximal_cell(self, c): sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) sage: pc = PolyhedralComplex([p1, p2, p3]) - sage: pc.has_maximal_cell(p1) + sage: pc.is_maximal_cell(p1) True - sage: pc.has_maximal_cell(p3) + sage: pc.is_maximal_cell(p3) False Wrong answer due to ``maximality_check=False``:: sage: pc_invalid = PolyhedralComplex([p1, p2, p3], ....: maximality_check=False) - sage: pc_invalid.has_maximal_cell(p3) + sage: pc_invalid.is_maximal_cell(p3) True """ d = c.dimension() # return (c in self.n_maximal_cells(d)) # use set instead of list return (d in self.maximal_cells()) and (c in self.maximal_cells()[d]) - def has_cell(self, c): + def is_cell(self, c): """ Return whether the given cell ``c`` is a cell of ``self``. @@ -683,9 +686,9 @@ def has_cell(self, c): sage: p2 = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) sage: p3 = Polyhedron(vertices=[(1, 2), (0, 2)]) sage: pc = PolyhedralComplex([p1, p2]) - sage: pc.has_cell(p3) + sage: pc.is_cell(p3) True - sage: pc.has_cell(Polyhedron(vertices=[(0, 0)])) + sage: pc.is_cell(Polyhedron(vertices=[(0, 0)])) True """ d = c.dimension() @@ -946,9 +949,10 @@ def face_poset(self): sage: poset Finite poset containing 11 elements sage: d = {i:i.vertices_matrix() for i in poset} - sage: poset.plot(element_labels=d) # not tested + sage: poset.plot(element_labels=d) + Graphics object consisting of 28 graphics primitives - TESTS on nonbounded polyhedral complex:: + For a nonbounded polyhedral complex:: sage: pc = PolyhedralComplex([ ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), @@ -1735,6 +1739,14 @@ def wedge(self, right): """ The wedge (one-point union) of this cell complex with another one. Currently not implemented. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) + sage: pc.wedge(pc) + Traceback (most recent call last): + ... + NotImplementedError: wedge is not implemented for polyhedral complex """ raise NotImplementedError("wedge is not implemented for " + "polyhedral complex") @@ -1748,6 +1760,14 @@ def chain_complex(self, subcomplex=None, augmented=False, """ The chain complex associated to this polyhedral complex. Currently not implemented. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) + sage: pc.chain_complex() + Traceback (most recent call last): + ... + NotImplementedError: chain_complex is not implemented for polyhedral complex """ raise NotImplementedError("chain_complex is not implemented for " + "polyhedral complex") @@ -1757,6 +1777,14 @@ def alexander_whitney(self, cell, dim_left): The decomposition of ``cell`` in this complex into left and right factors, suitable for computing cup products. Currently not implemented. + + EXAMPLES:: + + sage: pc = PolyhedralComplex([Polyhedron(vertices=[[0], [1]])]) + sage: pc.alexander_whitney(None, 1) + Traceback (most recent call last): + ... + NotImplementedError: alexander_whitney is not implemented for polyhedral complex """ raise NotImplementedError("alexander_whitney is not implemented for " + "polyhedral complex") @@ -1939,7 +1967,7 @@ def add_cell(self, cell): raise ValueError("the given cell is not a polyhedron " + "in the same ambient space") # if cell is already in self, do nothing. - if self.has_cell(cell): + if self.is_cell(cell): return if self._backend: cell = cell.base_extend(cell.base_ring(), self._backend) @@ -2102,7 +2130,7 @@ def remove_cell(self, cell, check=False): raise ValueError("the given cell is not a polyhedron " + "in the same ambient space") # if cell is not in self, delete nothing. - if not self.has_cell(cell): # self.cells() is called + if not self.is_cell(cell): # self.cells() is called if check: raise ValueError("trying to remove a cell which is not " + "in the polyhedral complex") From 51b3c8b31d137f8535bbbb34e6cadf6587942dbe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 11 Jul 2021 13:20:23 -0700 Subject: [PATCH 289/336] Revert "Make sure C99 is used" This reverts commit 0b51340f704eef2992641b1c3dbcf76f36a76eb0. --- build/pkgs/suitesparse/patches/05-c99.patch | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 build/pkgs/suitesparse/patches/05-c99.patch diff --git a/build/pkgs/suitesparse/patches/05-c99.patch b/build/pkgs/suitesparse/patches/05-c99.patch deleted file mode 100644 index c50ee322107..00000000000 --- a/build/pkgs/suitesparse/patches/05-c99.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/SuiteSparse_config/SuiteSparse_config.mk b/SuiteSparse_config/SuiteSparse_config.mk -index 5caa89dd..1a1658f5 100644 ---- a/SuiteSparse_config/SuiteSparse_config.mk -+++ b/SuiteSparse_config/SuiteSparse_config.mk -@@ -154,7 +154,7 @@ SUITESPARSE_VERSION = 5.10.1 - - # The CF macro is used by SuiteSparse Makefiles as a combination of - # CFLAGS, CPPFLAGS, TARGET_ARCH, and system-dependent settings. -- CF ?= $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $(OPTIMIZATION) -fexceptions -fPIC -+ CF ?= -std=c99 $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $(OPTIMIZATION) -fexceptions -fPIC - - #--------------------------------------------------------------------------- - # code formatting (for Tcov on Linux only) From f2d21ed02f001674c5e168af2741056b029b6603 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 11 Jul 2021 13:22:22 -0700 Subject: [PATCH 290/336] build/pkgs/suitesparse/spkg-install.in: Put -std=c99 in CC (not CF) --- build/pkgs/suitesparse/spkg-install.in | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/build/pkgs/suitesparse/spkg-install.in b/build/pkgs/suitesparse/spkg-install.in index 78701c659f0..b6944a409ba 100644 --- a/build/pkgs/suitesparse/spkg-install.in +++ b/build/pkgs/suitesparse/spkg-install.in @@ -1,11 +1,7 @@ # -*- shell-script -*- cd src -# Trac #30478: suitesparse 5.10.1 uses -std=c99 even with g++ -# by the following setting in SuiteSparse_config/SuiteSparse_config.mk -# CF ?= -std=c99 $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $(OPTIMIZATION) -fexceptions -fPIC -# Override this: -export CF="${CFLAGS} ${CPPFLAGS} -fexceptions -fPIC" +export CC="${CC-gcc} -std=c99" echo "print configuration" $MAKE MY_METIS_LIB=none CHOLMOD_CONFIG=-DNPARTITION \ From 94fac34d7b2ed2d62ad626000e024cb4c2a9d4d2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 12 Jul 2021 09:13:58 +1000 Subject: [PATCH 291/336] Fixing last doctest. --- src/sage/categories/complete_discrete_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/complete_discrete_valuation.py b/src/sage/categories/complete_discrete_valuation.py index 75d723941ec..e7bc01ddd28 100644 --- a/src/sage/categories/complete_discrete_valuation.py +++ b/src/sage/categories/complete_discrete_valuation.py @@ -154,7 +154,7 @@ def lift_to_precision(self, absprec=None): sage: R(1,15).lift_to_precision(30) Traceback (most recent call last): ... - PrecisionError: Precision higher than allowed by the precision cap. + PrecisionError: precision higher than allowed by the precision cap sage: R(-1,2).lift_to_precision().precision_absolute() == R.precision_cap() True From fff2a79b5936a2b354d80dca78612eee5301a701 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 8 Jul 2021 14:39:24 -0700 Subject: [PATCH 292/336] src/sage/sets/set.py: Fix docstring markup --- src/sage/sets/set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 809555d1b36..a6da0fcd4a8 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -343,7 +343,7 @@ def _test_as_set_object(self, tester=None, **options): class Set_boolean_operators: r""" - Mix-in class providing the Boolean operators `__or__`, `__and__`, `__xor__`. + Mix-in class providing the Boolean operators ``__or__``, ``__and__``, ``__xor__``. The operators delegate to the methods ``union``, ``intersection``, and ``symmetric_difference``, which need to be implemented by the class. @@ -393,7 +393,7 @@ def __xor__(self, X): class Set_add_sub_operators: r""" - Mix-in class providing the operators `__add__` and `__sub__`. + Mix-in class providing the operators ``__add__`` and ``__sub__``. The operators delegate to the methods ``union`` and ``intersection``, which need to be implemented by the class. From ee3f294832c4febf69b6ca2b11f501e4e263f37c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 12 Jul 2021 09:50:56 +1000 Subject: [PATCH 293/336] Some reviewer changes to documentation. --- src/sage/geometry/polyhedral_complex.py | 364 ++++++++++++------------ 1 file changed, 185 insertions(+), 179 deletions(-) diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index 3e5cff7a2b5..351bfe65b36 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -3,27 +3,7 @@ Finite polyhedral complexes This module implements the basic structure of finite polyhedral complexes. - -A polyhedral complex `PC` is a collection of polyhedra in a certain ambient -space `\RR^n` such that - -- If a polyhedron `P` is in `PC`, then all the faces of `P` are in `PC`. - -- If polyhedra `P` and `Q` are in `PC`, then `P \cap Q` is either empty or a face of both `P` and `Q`. - -In this context, a "polyhedron" means the geometric realization -of a polyhedron. This is in contrast to :mod:`simplicial complex -`, whose cells are abstract simplices. -The concept of a polyhedral complex generalizes that of a **geometric** -simplicial complex. - -.. note:: - - This class derives from - :class:`~sage.topology.cell_complex.GenericCellComplex`, and so - inherits its methods. Some of those methods are not listed here; - see the :mod:`Generic Cell Complex ` - page instead. +For more information, see :class:`PolyhedralComplex`. AUTHORS: @@ -144,7 +124,29 @@ class PolyhedralComplex(GenericCellComplex): r""" - Define a PolyhedralComplex. + A polyhedral complex. + + A **polyhedral complex** `PC` is a collection of polyhedra in a certain + ambient space `\RR^n` such that the following hold. + + - If a polyhedron `P` is in `PC`, then all the faces of `P` are in `PC`. + + - If polyhedra `P` and `Q` are in `PC`, then `P \cap Q` is either empty + or a face of both `P` and `Q`. + + In this context, a "polyhedron" means the geometric realization + of a polyhedron. This is in contrast to :mod:`simplicial complex + `, whose cells are abstract simplices. + The concept of a polyhedral complex generalizes that of a **geometric** + simplicial complex. + + .. NOTE:: + + This class derives from + :class:`~sage.topology.cell_complex.GenericCellComplex`, and so + inherits its methods. Some of those methods are not listed here; + see the :mod:`Generic Cell Complex ` + page instead. INPUT: @@ -152,46 +154,45 @@ class PolyhedralComplex(GenericCellComplex): dimension) of cells of the Complex. Each cell is of class :class:`Polyhedron` of the same ambient dimension. To set up a :class:PolyhedralComplex, it is sufficient to provide the maximal - faces. Use keyword argument partial=``True`` to set up a partial + faces. Use keyword argument ``partial=True`` to set up a partial polyhedral complex, which is a subset of the faces (viewed as relatively open) of a polyhedral complex that is not necessarily closed under taking intersection. - - ``maximality_check`` -- boolean; default ``True``; - if it is ``True``, then the constructor checks that each given - maximal cell is indeed maximal, and ignores those that are not. - + - ``maximality_check`` -- boolean (default: ``True``); + if ``True``, then the constructor checks that each given + maximal cell is indeed maximal, and ignores those that are not - - ``face_to_face_check`` -- boolean; default ``False``; - if it is ``True``, then the constructor checks whether the cells - are face-to-face, and it raises a ValueError if they are not. + - ``face_to_face_check`` -- boolean (default: ``False``); + if ``True``, then the constructor checks whether the cells + are face-to-face, and it raises a ``ValueError`` if they are not - - ``is_mutable`` and ``is_immutable`` -- boolean; default ``True`` and - ``False``, respectively. Set ``is_mutable=False`` or ``is_immutable=True`` - to make this polyhedral complex immutable. + - ``is_mutable`` and ``is_immutable`` -- boolean (default: ``True`` and + ``False`` respectively); set ``is_mutable=False`` or ``is_immutable=True`` + to make this polyhedral complex immutable - - ``backend`` -- string; default ``None``. The name of the backend used for - computations on Sage polyhedra. If it is ``None``, then each cell has its - own backend. Otherwise, all cells will use the given backend from: + - ``backend`` -- string (optional); the name of the backend used for + computations on Sage polyhedra; if it is not given, then each cell has + its own backend; otherwise it must be one of the following: - * ``ppl`` the Parma Polyhedra Library + * ``'ppl'`` - the Parma Polyhedra Library - * ``cdd`` CDD + * ``'cdd'`` - CDD - * ``normaliz`` normaliz + * ``'normaliz'`` - normaliz - * ``polymake`` polymake + * ``'polymake'`` - polymake - * ``field`` a generic Sage implementation + * ``'field'`` - a generic Sage implementation - - ``ambient_dim`` -- integer; default ``None``; Used to set up an empty - complex in the intended ambient space. + - ``ambient_dim`` -- integer (optional); used to set up an empty + complex in the intended ambient space EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1/7, 2/7)]), - ....: Polyhedron(vertices=[(1/7, 2/7), (0, 0), (0, 1/4)])]) + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1/7, 2/7)]), + ....: Polyhedron(vertices=[(1/7, 2/7), (0, 0), (0, 1/4)])]) sage: [p.Vrepresentation() for p in pc.cells_sorted()] [(A vertex at (0, 0), A vertex at (0, 1/4), A vertex at (1/7, 2/7)), (A vertex at (0, 0), A vertex at (1/3, 1/3), A vertex at (1/7, 2/7)), @@ -232,17 +233,17 @@ class PolyhedralComplex(GenericCellComplex): Check that non-maximal cells are ignored if ``maximality_check=True``:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0)]) ]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0)]) ]) sage: pc.maximal_cells() {2: {A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices}} Check that non face-to-face can be detected:: sage: PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(2, 2), (0, 0)]) ], - ....: face_to_face_check=True) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(2, 2), (0, 0)]) ], + ....: face_to_face_check=True) Traceback (most recent call last): ... ValueError: the given cells are not face-to-face @@ -250,8 +251,8 @@ class PolyhedralComplex(GenericCellComplex): Check that all the cells must have the same ambient dimension:: sage: PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[[2], [0]]) ]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[[2], [0]]) ]) Traceback (most recent call last): ... ValueError: the given cells are not polyhedra in the same ambient space @@ -344,21 +345,18 @@ def cells(self, subcomplex=None): """ The cells of this polyhedral complex, in the form of a dictionary: the keys are integers, representing dimension, and the value - associated to an integer `d` is the set of `d`-cells. If the - optional argument ``subcomplex`` is present, then return only - the cells which are *not* in the subcomplex. + associated to an integer `d` is the set of `d`-cells. INPUT: - - ``subcomplex`` -- (optional, default ``None``) if a subcomplex - of this polyhedral complex is given, then return the cells which - are not in this subcomplex. + - ``subcomplex`` -- (optional) if a subcomplex is given then + return the cells which are **not** in this subcomplex EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: list(pc.cells().keys()) [2, 1, 0] """ @@ -396,10 +394,10 @@ def cell_iterator(self, increasing=True): INPUT: - - ``increasing`` -- (optional, default ``True``) if ``True``, return + - ``increasing`` -- (default ``True``) if ``True``, return cells in increasing order of dimension, thus starting with the - zero-dimensional cells. Otherwise it returns cells in decreasing - order of dimension. + zero-dimensional cells; otherwise it returns cells in decreasing + order of dimension .. NOTE:: @@ -408,8 +406,8 @@ def cell_iterator(self, increasing=True): EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: len(list(pc.cell_iterator())) 11 """ @@ -425,23 +423,18 @@ def cell_iterator(self, increasing=True): def _n_cells_sorted(self, n, subcomplex=None): """ Sorted list of cells of dimension ``n`` of this polyhedral complex. - If the optional argument ``subcomplex`` is present, then - return the ``n``-dimensional cells which are *not* in the - subcomplex. INPUT: - - ``n`` -- (non-negative integer) the dimension - - - ``subcomplex`` -- (optional, default ``None``) if a subcomplex - of this polyhedral complex is given, then return the cells which - are not in this subcomplex. + - ``n`` -- non-negative integer; the dimension + - ``subcomplex`` -- (optional) if a subcomplex is given then + return the cells which are **not** in this subcomplex EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: [p.Vrepresentation() for p in pc._n_cells_sorted(1)] [(A vertex at (0, 0), A vertex at (0, 2)), (A vertex at (0, 0), A vertex at (1, 1)), @@ -459,14 +452,11 @@ def cells_sorted(self, subcomplex=None): """ The sorted list of the cells of this polyhedral complex in non-increasing dimensions. - If the optional argument ``subcomplex`` is present, then return only - the cells which are *not* in the subcomplex. INPUT: - - ``subcomplex`` -- (optional, default ``None``) if a subcomplex - of this polyhedral complex is given, then return the cells which - are not in this subcomplex. + - ``subcomplex`` -- (optional) if a subcomplex is given then + return the cells which are **not** in this subcomplex EXAMPLES:: @@ -515,7 +505,7 @@ def maximal_cells(self): return self._maximal_cells def maximal_cell_iterator(self, increasing=False): - """ + r""" An iterator for the maximal cells in this polyhedral complex. INPUT: @@ -559,12 +549,12 @@ def maximal_cell_iterator(self, increasing=False): yield c def n_maximal_cells(self, n): - """ + r""" List of maximal cells of dimension ``n`` of this polyhedral complex. INPUT: - - ``n`` -- (non-negative integer) the dimension + - ``n`` -- non-negative integer; the dimension .. NOTE:: @@ -616,8 +606,8 @@ def _n_maximal_cells_sorted(self, n): EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: pc._n_maximal_cells_sorted(2)[0].vertices_list() [[0, 0], [0, 2], [1, 2]] """ @@ -633,8 +623,8 @@ def maximal_cells_sorted(self): EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: [p.vertices_list() for p in pc.maximal_cells_sorted()] [[[0, 0], [0, 2], [1, 2]], [[0, 0], [1, 1], [1, 2]]] """ @@ -702,8 +692,8 @@ def dimension(self): EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 2)]) ]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 2)]) ]) sage: pc.dimension() 2 sage: empty_pc = PolyhedralComplex([]) @@ -878,8 +868,8 @@ def _an_element_(self): ... EmptySetError: the complex is empty sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) sage: pc._an_element_().vertices_list() [[0, 0], [0, 1/2], [1, 2]] """ @@ -922,8 +912,8 @@ def __call__(self, x): EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) sage: pc(Polyhedron(vertices=[(1, 2), (0, 0)])) A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: pc(Polyhedron(vertices=[(1, 1)])) @@ -943,27 +933,27 @@ def face_poset(self): EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)])]) sage: poset = pc.face_poset() sage: poset Finite poset containing 11 elements - sage: d = {i:i.vertices_matrix() for i in poset} + sage: d = {i: i.vertices_matrix() for i in poset} sage: poset.plot(element_labels=d) Graphics object consisting of 28 graphics primitives For a nonbounded polyhedral complex:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]), - ....: Polyhedron(vertices=[(-1/2, -1/2)], lines=[(1, -1)]), - ....: Polyhedron(rays=[(1, 0)])]) + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]), + ....: Polyhedron(vertices=[(-1/2, -1/2)], lines=[(1, -1)]), + ....: Polyhedron(rays=[(1, 0)])]) sage: poset = pc.face_poset() sage: poset Finite poset containing 13 elements sage: d = {i:''.join([str(v)+'\n' - ....: for v in i.Vrepresentation()]) for i in poset} + ....: for v in i.Vrepresentation()]) for i in poset} sage: poset.show(element_labels=d, figsize=15) # not tested sage: pc = PolyhedralComplex([ ....: Polyhedron(rays=[(1,0),(0,1)]), @@ -979,14 +969,14 @@ def face_poset(self): def is_subcomplex(self, other): r""" - Return True if ``self`` is a subcomplex of ``other``. + Return whether ``self`` is a subcomplex of ``other``. INPUT: - ``other`` -- a polyhedral complex Each maximal cell of ``self`` must be a cell of ``other`` - for this to be True. + for this to be ``True``. EXAMPLES:: @@ -1023,8 +1013,9 @@ def is_compact(self): def graph(self): """ The 1-skeleton of this polyhedral complex, as a graph. - The vertices of the graph are of type ``vector``. - Raise NotImplementedError if the polyhedral complex is unbounded. + + The vertices of the graph are of type ``vector``. Raises + a ``NotImplementedError`` if the polyhedral complex is unbounded. .. WARNING:: @@ -1034,8 +1025,8 @@ def graph(self): EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: g = pc.graph(); g Graph on 4 vertices sage: g.vertices() @@ -1075,30 +1066,30 @@ def graph(self): def is_connected(self): """ - True if this cell complex is connected. + Return whether ``self`` is connected. EXAMPLES:: sage: pc1 = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: pc1.is_connected() True sage: pc2 = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(0, 2)])]) sage: pc2.is_connected() False sage: pc3 = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]), - ....: Polyhedron(vertices=[(-1/2, -1/2)], lines=[(1, -1)]), - ....: Polyhedron(rays=[(1, 0)])]) + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 1/2)]), + ....: Polyhedron(vertices=[(-1/2, -1/2)], lines=[(1, -1)]), + ....: Polyhedron(rays=[(1, 0)])]) sage: pc3.is_connected() False sage: pc4 = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), - ....: Polyhedron(rays=[(1, 0)])]) + ....: Polyhedron(vertices=[(1/3, 1/3), (0, 0), (1, 2)]), + ....: Polyhedron(rays=[(1, 0)])]) sage: pc4.is_connected() True """ @@ -1110,10 +1101,16 @@ def is_connected(self): def connected_component(self, cell=None): """ Return the connected component of this polyhedral complex - containing ``cell``. If ``cell`` is omitted, then return - the connected component containing the self._an_element_. - (If the polyhedral complex is empty or if it does not contain the - given cell, raise an error.) + containing a given cell. + + INPUT: + + - ``cell`` -- (default: ``self.an_element()``) a cell of ``self`` + + OUTPUT: + + The connected component containing ``cell``. If the polyhedral complex + is empty or if it does not contain the given cell, raise an error. EXAMPLES:: @@ -1249,7 +1246,7 @@ def connected_components(self): return results def n_skeleton(self, n): - """ + r""" The `n`-skeleton of this polyhedral complex. The `n`-skeleton of a polyhedral complex is obtained by discarding @@ -1257,7 +1254,7 @@ def n_skeleton(self, n): INPUT: - - ``n`` -- (non-negative integer) the dimension + - ``n`` -- non-negative integer; the dimension .. SEEALSO:: @@ -1266,8 +1263,8 @@ def n_skeleton(self, n): EXAMPLES:: sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), - ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) + ....: Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]), + ....: Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)])]) sage: pc.n_skeleton(2) Polyhedral complex with 2 maximal cells sage: pc.n_skeleton(1) @@ -1284,9 +1281,9 @@ def n_skeleton(self, n): backend=self._backend) def stratify(self, n): - """ + r""" Return the pure sub-polyhedral complex which is constructed from the - n-dimensional maximal cells of this polyhedral complex. + `n`-dimensional maximal cells of this polyhedral complex. .. SEEALSO:: @@ -1311,7 +1308,7 @@ def stratify(self, n): Wrong answer due to ``maximality_check=False``:: sage: pc_invalid = PolyhedralComplex([p1, p2, p3], - ....: maximality_check=False) + ....: maximality_check=False) sage: pc_invalid.stratify(1) Polyhedral complex with 1 maximal cell """ @@ -1350,27 +1347,27 @@ def boundary_subcomplex(self): Test with ``maximality_check == False``:: sage: pc_invalid = PolyhedralComplex([p2, p3], - ....: maximality_check=False) + ....: maximality_check=False) sage: pc_invalid.boundary_subcomplex() == pc_invalid.n_skeleton(1) True Test unbounded cases:: sage: pc1 = PolyhedralComplex([ - ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]])]) + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]])]) sage: pc1.boundary_subcomplex() == pc1.n_skeleton(1) True sage: pc1b = PolyhedralComplex([Polyhedron( - ....: vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0],[0,1,0]])]) + ....: vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0],[0,1,0]])]) sage: pc1b.boundary_subcomplex() == pc1b True sage: pc2 = PolyhedralComplex([ - ....: Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]])]) + ....: Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]])]) sage: pc2.boundary_subcomplex() == pc2.n_skeleton(1) True sage: pc3 = PolyhedralComplex([ - ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), - ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), + ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) sage: pc3.boundary_subcomplex() == pc3.n_skeleton(1) False """ @@ -1391,7 +1388,6 @@ def relative_boundary_cells(self): A point `P` is in the relative boundary of a set `S` if `P` is in the closure of `S` but not in the relative interior of `S`. - .. WARNING:: This may give the wrong answer if the polyhedral complex @@ -1416,23 +1412,23 @@ def relative_boundary_cells(self): Test on polyhedral complex which is not pure:: sage: pc_non_pure = PolyhedralComplex([p1, p3, p4]) - sage: set(pc_non_pure.relative_boundary_cells()) == set( - ....: [f.as_polyhedron() for f in p1.faces(1)] + [p3, p4]) + sage: (set(pc_non_pure.relative_boundary_cells()) + ....: == set([f.as_polyhedron() for f in p1.faces(1)] + [p3, p4])) True Test with ``maximality_check == False``:: sage: pc_invalid = PolyhedralComplex([p2, p3], - ....: maximality_check=False) - sage: set(pc_invalid.relative_boundary_cells()) == set( - ....: [f.as_polyhedron() for f in p2.faces(1)]) + ....: maximality_check=False) + sage: (set(pc_invalid.relative_boundary_cells()) + ....: == set([f.as_polyhedron() for f in p2.faces(1)])) True Test unbounded case:: sage: pc3 = PolyhedralComplex([ - ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), - ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), + ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) sage: len(pc3.relative_boundary_cells()) 4 """ @@ -1445,7 +1441,7 @@ def relative_boundary_cells(self): return ans def is_convex(self): - """ + r""" Return whether the set of points in ``self`` is a convex set. When ``self`` is convex, the union of its cells is a Polyhedron. @@ -1470,33 +1466,33 @@ def is_convex(self): Test unbounded cases:: sage: pc1 = PolyhedralComplex([ - ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]])]) + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]])]) sage: pc1.is_convex() True sage: pc2 = PolyhedralComplex([ - ....: Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]])]) + ....: Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]])]) sage: pc2.is_convex() True sage: pc3 = PolyhedralComplex([ - ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), - ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) + ....: Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,0], [0,1]]), + ....: Polyhedron(vertices=[[1,0], [0,-1]], rays=[[1,0], [0,-1]])]) sage: pc3.is_convex() False sage: pc4 = PolyhedralComplex([Polyhedron(rays=[[1,0], [-1,1]]), - ....: Polyhedron(rays=[[1,0], [-1,-1]])]) + ....: Polyhedron(rays=[[1,0], [-1,-1]])]) sage: pc4.is_convex() False The whole 3d space minus the first orthant is not convex:: sage: pc5 = PolyhedralComplex([ - ....: Polyhedron(rays=[[1,0,0], [0,1,0], [0,0,-1]]), - ....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,-1]]), - ....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,1]]), - ....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,-1]]), - ....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,1]]), - ....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,-1]]), - ....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,1]])]) + ....: Polyhedron(rays=[[1,0,0], [0,1,0], [0,0,-1]]), + ....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,-1]]), + ....: Polyhedron(rays=[[1,0,0], [0,-1,0], [0,0,1]]), + ....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,-1]]), + ....: Polyhedron(rays=[[-1,0,0], [0,-1,0], [0,0,1]]), + ....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,-1]]), + ....: Polyhedron(rays=[[-1,0,0], [0,1,0], [0,0,1]])]) sage: pc5.is_convex() False @@ -1506,12 +1502,12 @@ def is_convex(self): sage: l.is_convex() True sage: pc1b = PolyhedralComplex([Polyhedron( - ....: vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0],[0,1,0]])]) + ....: vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0],[0,1,0]])]) sage: pc1b.is_convex() True sage: pc4b = PolyhedralComplex([ - ....: Polyhedron(rays=[[1,0,0], [-1,1,0]]), - ....: Polyhedron(rays=[[1,0,0], [-1,-1,0]])]) + ....: Polyhedron(rays=[[1,0,0], [-1,1,0]]), + ....: Polyhedron(rays=[[1,0,0], [-1,-1,0]])]) sage: pc4b.is_convex() False """ @@ -1588,7 +1584,7 @@ def is_convex(self): def union_as_polyhedron(self): """ - Assuming the polyhedral complex is convex, return it as a Polyhedron. + Return ``self`` as a :class:`Polyhedron` if ``self`` is convex. EXAMPLES:: @@ -1737,8 +1733,11 @@ def join(self, right): def wedge(self, right): """ - The wedge (one-point union) of this cell complex with - another one. Currently not implemented. + The wedge (one-point union) of ``self`` with ``right``. + + .. TODO:: + + Implement the wedge product of two polyhedral complexes. EXAMPLES:: @@ -1759,7 +1758,10 @@ def chain_complex(self, subcomplex=None, augmented=False, base_ring=ZZ, cochain=False): """ The chain complex associated to this polyhedral complex. - Currently not implemented. + + .. TODO:: + + Implement chain complexes of a polyhedral complex. EXAMPLES:: @@ -1776,7 +1778,10 @@ def alexander_whitney(self, cell, dim_left): """ The decomposition of ``cell`` in this complex into left and right factors, suitable for computing cup products. - Currently not implemented. + + .. TODO:: + + Implement :meth:`alexander_whitney` of a polyhedral complex. EXAMPLES:: @@ -1841,7 +1846,7 @@ def set_immutable(self): def is_mutable(self): """ - Return ``True`` if mutable. + Return whether ``self`` is mutable. EXAMPLES:: @@ -1865,7 +1870,7 @@ def is_mutable(self): def is_immutable(self): """ - Return ``True`` if immutable. + Return whether ``self`` is immutable. EXAMPLES:: @@ -1891,7 +1896,7 @@ def add_cell(self, cell): - ``cell`` -- a polyhedron - This *changes* the polyhedral complex, by adding a new cell and all + This **changes** the polyhedral complex, by adding a new cell and all of its subfaces. EXAMPLES: @@ -2029,18 +2034,18 @@ def add_cell(self, cell): self._maximal_cells_sorted = None # needed for hash def remove_cell(self, cell, check=False): - """ - Remove the given cell from this polyhedral complex. In addition, - it removes all the cells that contain the given cell as a subface. + r""" + Remove ``cell`` from ``self`` and all the cells that contain ``cell`` + as a subface. INPUT: - ``cell`` -- a cell of the polyhedral complex - - ``check`` -- boolean; optional, default ``False``. - If ``True``, raise an error if ``cell`` is not a cell of this complex + - ``check`` -- boolean (default: ``False``); if ``True``, + raise an error if ``cell`` is not a cell of this complex - This does not return anything; instead, it *changes* the + This does not return anything; instead, it **changes** the polyhedral complex. EXAMPLES: @@ -2236,18 +2241,18 @@ def subdivide(self, make_simplicial=False, INPUT: - - ``make_simplicial`` -- boolean; optional, default ``False``. - If ``True``, the returned polyhedral complex is simplicial. + - ``make_simplicial`` -- boolean (default: ``False``); if ``True``, + the returned polyhedral complex is simplicial - - ``new_vertices``, ``new_rays`` -- list; optional, default ``None``. - New generators to be added during subdivision. + - ``new_vertices``, ``new_rays`` -- list (optional); new generators + to be added during subdivision EXAMPLES:: sage: square_vertices = [(1, 1, 1), (-1, 1, 1), (-1, -1, 1), (1, -1, 1)] sage: pc = PolyhedralComplex([ - ....: Polyhedron(vertices=[(0, 0, 0)] + square_vertices), - ....: Polyhedron(vertices=[(0, 0, 2)] + square_vertices)]) + ....: Polyhedron(vertices=[(0, 0, 0)] + square_vertices), + ....: Polyhedron(vertices=[(0, 0, 2)] + square_vertices)]) sage: pc.is_compact() and not pc.is_simplicial_complex() True sage: subdivided_pc = pc.subdivide(new_vertices=[(0, 0, 1)]) @@ -2430,3 +2435,4 @@ def cells_list_to_cells_dict(cells_list): else: cells_dict[d] = set([cell]) return cells_dict + From 2daca4e543da3c025077499abbcf2a7ac6a477b6 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 12 Jul 2021 11:20:14 -0400 Subject: [PATCH 294/336] updates --- src/sage/modular/modform/space.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index f46dbfcaec7..f03cff091ee 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1092,8 +1092,6 @@ def _element_constructor_(self, x, check=True): 4 + 6*q + 47*q^2 + 143*q^3 + 358*q^4 + 630*q^5 + O(q^6) """ if isinstance(x, ModularFormElement): - if x.parent() is self: - return x if not check: from copy import copy From aae7c27b2f28e2ffd5e2524767d63e49b2da73fb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 12 Jul 2021 17:34:49 +0200 Subject: [PATCH 295/336] fix ValueError when obtaining random bounded tolerance graph --- src/sage/graphs/generators/random.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index e06bc8a6d87..0d4cb717891 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -603,13 +603,21 @@ def RandomBoundedToleranceGraph(n): sage: g = graphs.RandomBoundedToleranceGraph(8) sage: g.clique_number() == g.chromatic_number() True + + TESTS: + + Check that :trac:`32186` is fixed:: + + sage: for _ in range(100): _ = graphs.RandomBoundedToleranceGraph(1) """ from sage.misc.prandom import randint + from sage.combinat.combination import Combinations from sage.graphs.generators.intersection import ToleranceGraph W = n ** 2 * 2 ** n + C = Combinations(W + 1, 2) - tolrep = [(l_r[0], l_r[1], randint(0, l_r[1] - l_r[0])) for l_r in [sorted((randint(0, W), randint(0, W))) for i in range(n)]] + tolrep = [(l_r[0], l_r[1], randint(1, l_r[1] - l_r[0])) for l_r in [C.random_element() for i in range(n)]] return ToleranceGraph(tolrep) From 0cdd853d451b14249211b1e3586be4ddcf7bdb35 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Jul 2021 14:08:10 -0500 Subject: [PATCH 296/336] Add __hash__ --- src/sage/groups/class_function.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/groups/class_function.py b/src/sage/groups/class_function.py index e86c32e589d..d0ed4e501f9 100644 --- a/src/sage/groups/class_function.py +++ b/src/sage/groups/class_function.py @@ -104,6 +104,11 @@ class ClassFunction_gap(SageObject): sage: loads(dumps(chi)) == chi True """ + + def __hash__(self): + + return hash(self.__repr__()) + def __init__(self, G, values): r""" Return the character of the group ``G`` with values given by the list From 2f77bbcbf68fb4ef5400dabd912a34f88d5d01c3 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Mon, 12 Jul 2021 17:46:10 -0400 Subject: [PATCH 297/336] solve the red patchbot plugin (pyflakes) --- src/sage/geometry/polyhedral_complex.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index 351bfe65b36..06dc60380c3 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -114,9 +114,7 @@ from sage.geometry.polyhedron.constructor import Polyhedron from sage.geometry.polyhedron.base import is_Polyhedron from sage.modules.free_module_element import vector -from sage.structure.sage_object import SageObject from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ from sage.graphs.graph import Graph from sage.combinat.posets.posets import Poset from sage.misc.misc import powerset @@ -323,7 +321,7 @@ def __init__(self, maximal_cells=None, backend=None, maximality_check=True, self._face_poset = None if maximality_check: - cells = self.cells() # compute self._cells and self._face_poset + self.cells() # compute self._cells and self._face_poset self._maximal_cells = cells_list_to_cells_dict( self._face_poset.maximal_elements()) if face_to_face_check: @@ -964,7 +962,7 @@ def face_poset(self): Finite poset containing 9 elements """ if self._face_poset is None: - cells = self.cells() # poset is obtained and cached in cells() + self.cells() # poset is obtained and cached in cells() return self._face_poset def is_subcomplex(self, other): From 451f5cf5fb60611db32bf5fbcdecfec230c087f6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Jul 2021 17:57:11 -0700 Subject: [PATCH 298/336] Sets.ParentMethods: Update doctest --- src/sage/categories/sets_cat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 3c5979a1ecb..24c93ab6678 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1714,11 +1714,11 @@ def _sympy_(self): sage: RR._sympy_().is_finite_set False - sage: F = Set([1, 2]) - sage: F is Set([1, 2]) + sage: F = Family([1, 2]) + sage: F is Family([1, 2]) False sage: sF = F._sympy_(); sF - SageSet({1, 2}) + SageSet(Family (1, 2)) sage: sF._sage_() is F True """ From 8dceb8867319657f94360773c103abce73154bd1 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 13 Jul 2021 09:41:38 +0200 Subject: [PATCH 299/336] Polyhedron_interactive -> Polyhedron_mutable --- src/sage/geometry/polyhedron/backend_ppl.py | 6 +++--- .../{base_interactive.py => base_mutable.py} | 15 +++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) rename src/sage/geometry/polyhedron/{base_interactive.py => base_mutable.py} (77%) diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index 83b1e999781..6bdd9c496a6 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -7,7 +7,7 @@ from sage.rings.integer import Integer from sage.arith.functions import LCM_list from sage.misc.functional import denominator -from .base_interactive import Polyhedron_interactive +from .base_mutable import Polyhedron_mutable from .base_QQ import Polyhedron_QQ from .base_ZZ import Polyhedron_ZZ @@ -19,7 +19,7 @@ ######################################################################### -class Polyhedron_ppl(Polyhedron_interactive): +class Polyhedron_ppl(Polyhedron_mutable): """ Polyhedra with ppl @@ -61,7 +61,7 @@ def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, **kwds): minimize = True if 'minimize' in kwds and kwds['minimize'] else False self._init_from_ppl_polyhedron(ppl_polyhedron, minimize) else: - Polyhedron_interactive.__init__(self, parent, Vrep, Hrep, **kwds) + Polyhedron_mutable.__init__(self, parent, Vrep, Hrep, **kwds) def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbose=False): """ diff --git a/src/sage/geometry/polyhedron/base_interactive.py b/src/sage/geometry/polyhedron/base_mutable.py similarity index 77% rename from src/sage/geometry/polyhedron/base_interactive.py rename to src/sage/geometry/polyhedron/base_mutable.py index a4de3ef7d71..4a99c8eb1ba 100644 --- a/src/sage/geometry/polyhedron/base_interactive.py +++ b/src/sage/geometry/polyhedron/base_mutable.py @@ -1,12 +1,15 @@ r""" -Base class for interactive and lazy polyhedra. +Base class for mutable polyhedra. + +Just like vectors and matrices they can be set immutable. +The constructor does this by default. """ from .base import Polyhedron_base from sage.misc.lazy_attribute import lazy_attribute -class Polyhedron_interactive(Polyhedron_base): +class Polyhedron_mutable(Polyhedron_base): def _clear_cache(self): r""" @@ -31,9 +34,9 @@ def Vrepresentation(self): TESTS:: - sage: from sage.geometry.polyhedron.base_interactive import Polyhedron_interactive + sage: from sage.geometry.polyhedron.base_mutable import Polyhedron_mutable sage: p = polytopes.cube() - sage: Polyhedron_interactive.Vrepresentation(p) + sage: Polyhedron_mutable.Vrepresentation(p) Traceback (most recent call last): ... NotImplementedError: a derived class must implement this @@ -48,9 +51,9 @@ def Hrepresentation(self): TESTS:: - sage: from sage.geometry.polyhedron.base_interactive import Polyhedron_interactive + sage: from sage.geometry.polyhedron.base_mutable import Polyhedron_mutable sage: p = polytopes.cube() - sage: Polyhedron_interactive.Hrepresentation(p) + sage: Polyhedron_mutable.Hrepresentation(p) Traceback (most recent call last): ... NotImplementedError: a derived class must implement this From d4f6fb760f3ad9b2efdefd54d0fb5304e3eee3c8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 13 Jul 2021 11:07:30 +0200 Subject: [PATCH 300/336] remove redundant lines in test --- src/sage/geometry/polyhedron/backend_ppl.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index d6ccc2f4ee5..bb91f857ccc 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -176,8 +176,6 @@ def Vrepresentation(self, index=None): EXAMPLES:: - sage: p = polytopes.cube() - sage: p.Vrepresentation(0) sage: p = polytopes.cube() sage: p.Vrepresentation(0) A vertex at (1, -1, -1) From 4045c4636508a45411ac0ad58cde95939389c6a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 13 Jul 2021 11:45:47 +0200 Subject: [PATCH 301/336] adding is_triangular method for matrices --- src/sage/matrix/matrix2.pyx | 50 ++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 780583b9312..f9fd7fe6a62 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -9187,13 +9187,13 @@ cdef class Matrix(Matrix1): return False return True - def is_diagonal(self): + def is_diagonal(self) -> bool: """ - Return True if this matrix is a diagonal matrix. + Return ``True`` if this matrix is a diagonal matrix. OUTPUT: - - whether self is a diagonal matrix. + boolean EXAMPLES:: @@ -9221,7 +9221,49 @@ cdef class Matrix(Matrix1): return False return True - def is_unitary(self): + def is_triangular(self, side="lower") -> bool: + """ + Return ``True`` if this matrix is a triangular matrix. + + INPUT + + - ``side`` -- either ``"lower"`` (default) or ``"upper"`` + + OUTPUT: + + boolean + + EXAMPLES:: + + sage: m = matrix(QQ,2,2,range(4)) + sage: m.is_triangular() + False + sage: m = matrix(QQ,2,[5,0,0,5]) + sage: m.is_triangular() + True + sage: m = matrix(QQ,2,[1,2,0,1]) + sage: m.is_triangular("upper") + True + """ + if not self.is_square(): + return False + cdef Py_ssize_t i, j + + if side == "upper": + for i in range(self._nrows): + for j in range(self._ncols): + if i > j: + if not self.get_unsafe(i, j).is_zero(): + return False + else: + for i in range(self._nrows): + for j in range(self._ncols): + if i < j: + if not self.get_unsafe(i, j).is_zero(): + return False + return True + + def is_unitary(self) -> bool: r""" Returns ``True`` if the columns of the matrix are an orthonormal basis. From 1ece61537afe31c8a88347312ded63ffab24d665 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 13 Jul 2021 13:12:33 +0200 Subject: [PATCH 302/336] allow ppl polyhedron to be mutable --- src/sage/geometry/polyhedron/backend_ppl.py | 57 ++++++++++--- src/sage/geometry/polyhedron/base.py | 12 ++- src/sage/geometry/polyhedron/base_mutable.py | 88 +++++++++++++++++++- src/sage/geometry/polyhedron/constructor.py | 38 ++++++--- src/sage/geometry/polyhedron/face.py | 10 ++- src/sage/geometry/polyhedron/parent.py | 56 ++++++++++++- 6 files changed, 228 insertions(+), 33 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index bb91f857ccc..d6ef52c9c44 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -37,7 +37,7 @@ class Polyhedron_ppl(Polyhedron_mutable): _backend_object_name = "ppl_polyhedron" - def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, **kwds): + def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, mutable=False, **kwds): """ Initializes the polyhedron. @@ -62,6 +62,9 @@ def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, **kwds): self._init_from_ppl_polyhedron(ppl_polyhedron, minimize) else: Polyhedron_mutable.__init__(self, parent, Vrep, Hrep, **kwds) + self._is_mutable = True + if not mutable: + self.set_immutable() def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbose=False): """ @@ -154,6 +157,27 @@ def _init_from_ppl_polyhedron(self, ppl_polyhedron, minimize=True): """ self._ppl_polyhedron = ppl_polyhedron + def set_immutable(self): + r""" + Make this polyhedron immutable. This operation cannot be undone. + + EXAMPLES:: + + sage: p = Polyhedron([[1, 1]], mutable=True) + sage: p.is_mutable() + True + sage: hasattr(p, "_Vrepresentation") + False + sage: p.set_immutable() + sage: hasattr(p, "_Vrepresentation") + True + """ + if not hasattr(self, '_Vrepresentation'): + self._init_Vrepresentation_from_ppl(True) + if not hasattr(self, '_Hrepresentation'): + self._init_Hrepresentation_from_ppl(True) + self._is_mutable = False + def Vrepresentation(self, index=None): """ Return the objects of the V-representation. Each entry is @@ -179,16 +203,16 @@ def Vrepresentation(self, index=None): sage: p = polytopes.cube() sage: p.Vrepresentation(0) A vertex at (1, -1, -1) + + :: + + sage: P = p.parent() + sage: p = P._element_constructor_(p, mutable=True) + sage: p.Vrepresentation(0) + A vertex at (1, -1, -1) sage: p._clear_cache() - sage: p.Vrepresentation() - (A vertex at (1, -1, -1), - A vertex at (1, 1, -1), - A vertex at (1, 1, 1), - A vertex at (1, -1, 1), - A vertex at (-1, -1, 1), - A vertex at (-1, -1, -1), - A vertex at (-1, 1, -1), - A vertex at (-1, 1, 1)) + sage: p.Vrepresentation(0) + A vertex at (1, -1, -1) sage: TestSuite(p).run() """ if not hasattr(self, '_Vrepresentation'): @@ -217,6 +241,8 @@ def _init_Vrepresentation_from_ppl(self, minimize): sage: p._ppl_polyhedron.minimized_generators() Generator_System {point(0/2, 1/2), point(2/1, 0/1), point(24/6, 5/6)} """ + if not self._is_mutable: + raise TypeError("Vrepresentation of mutable polyhedra cannot be recomputed") self._Vrepresentation = [] gs = self._ppl_polyhedron.minimized_generators() parent = self.parent() @@ -255,6 +281,8 @@ def _init_Hrepresentation_from_ppl(self, minimize): sage: p._ppl_polyhedron.minimized_generators() Generator_System {point(0/2, 1/2), point(2/1, 0/1), point(24/6, 5/6)} """ + if not self._is_mutable: + raise TypeError("Hrepresentation of mutable polyhedra cannot be recomputed") self._Hrepresentation = [] cs = self._ppl_polyhedron.minimized_constraints() parent = self.parent() @@ -289,7 +317,16 @@ def Hrepresentation(self, index=None): An inequality (-1, 0, 0) x + 1 >= 0 sage: p.Hrepresentation(0) == p.Hrepresentation()[0] True + + :: + + sage: P = p.parent() + sage: p = P._element_constructor_(p, mutable=True) + sage: p.Hrepresentation(0) + An inequality (-1, 0, 0) x + 1 >= 0 sage: p._clear_cache() + sage: p.Hrepresentation(0) + An inequality (-1, 0, 0) x + 1 >= 0 sage: TestSuite(p).run() """ if not hasattr(self, '_Hrepresentation'): diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 9b1e07ee26e..e52def4dcb8 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -125,6 +125,8 @@ class Polyhedron_base(Element, ConvexSet_closed): one of``Vrep`` or ``Hrep`` to pick this in case the backend cannot initialize from complete double description + - ``mutable`` -- ignored + If both ``Vrep`` and ``Hrep`` are provided, then ``Vrep_minimal`` and ``Hrep_minimal`` must be set to ``True``. @@ -171,7 +173,7 @@ class Polyhedron_base(Element, ConvexSet_closed): sage: TestSuite(P).run() """ - def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, pref_rep=None, **kwds): + def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, pref_rep=None, mutable=False, **kwds): """ Initializes the polyhedron. @@ -607,7 +609,7 @@ def base_extend(self, base_ring, backend=None): """ new_parent = self.parent().base_extend(base_ring, backend) - return new_parent(self) + return new_parent(self, copy=True) def change_ring(self, base_ring, backend=None): """ @@ -778,6 +780,12 @@ def _is_subpolyhedron(self, other): for other_H in other.Hrepresentation() for self_V in self.Vrepresentation()) + def is_mutable(self): + return False + + def is_immutable(self): + return True + @cached_method def vertex_facet_graph(self, labels=True): r""" diff --git a/src/sage/geometry/polyhedron/base_mutable.py b/src/sage/geometry/polyhedron/base_mutable.py index 4a99c8eb1ba..adf1d2d35be 100644 --- a/src/sage/geometry/polyhedron/base_mutable.py +++ b/src/sage/geometry/polyhedron/base_mutable.py @@ -10,6 +10,28 @@ class Polyhedron_mutable(Polyhedron_base): + """ + Base class for polyhedra that allow mutability. + + This should not be used directly. + """ + + def __hash__(self): + r""" + TESTS:: + + sage: p = Polyhedron([[1, 1]], mutable=True) + sage: set([p]) + Traceback (most recent call last): + ... + TypeError: mutable polyhedra are unhashable + sage: p.set_immutable() + sage: set([p]) + {A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex} + """ + if self._is_mutable: + raise TypeError("mutable polyhedra are unhashable") + return Polyhedron_base.__hash__(self) def _clear_cache(self): r""" @@ -17,15 +39,73 @@ def _clear_cache(self): TESTS:: - sage: P = polytopes.permutahedron(4) - sage: TestSuite(P).run() - sage: P._clear_cache() - sage: TestSuite(P).run() + sage: p = polytopes.permutahedron(4) + sage: P = p.parent() + sage: q = P._element_constructor_(p, mutable=True) + sage: TestSuite(q).run() + sage: q._clear_cache() + sage: TestSuite(q).run() + + :: + + sage: q.set_immutable() + sage: q._clear_cache() + Traceback (most recent call last): + ... + TypeError: cannot clear cache of immutable polyhedra """ + if not self._is_mutable: + raise TypeError("cannot clear cache of immutable polyhedra") self.parent().recycle(self) backend_object = self.__dict__["_" + self._backend_object_name] del self.__dict__ self.__dict__["_" + self._backend_object_name] = backend_object + self._is_mutable = True + + def is_mutable(self): + r""" + Return True if the polyhedron is mutable, i.e. it can be modified in place. + + EXAMPLES:: + + sage: p = Polyhedron([[1, 1]], mutable=True) + sage: p.is_mutable() + True + sage: p = Polyhedron([[1, 1]], mutable=False) + sage: p.is_mutable() + False + """ + return self._is_mutable + + def is_immutable(self): + r""" + Return True if the polyhedron is immutable, i.e. it cannot be modified in place. + + EXAMPLES:: + + sage: p = Polyhedron([[1, 1]], mutable=True) + sage: p.is_immutable() + False + sage: p = Polyhedron([[1, 1]], mutable=False) + sage: p.is_immutable() + True + """ + return not self._is_mutable + + def set_immutable(self): + r""" + Make this polyhedron immutable. This operation cannot be undone. + + TESTS:: + + sage: from sage.geometry.polyhedron.base_mutable import Polyhedron_mutable + sage: p = polytopes.cube() + sage: Polyhedron_mutable.set_immutable(p) + Traceback (most recent call last): + ... + NotImplementedError: a derived class must implement this + """ + raise NotImplementedError("a derived class must implement this") def Vrepresentation(self): r""" diff --git a/src/sage/geometry/polyhedron/constructor.py b/src/sage/geometry/polyhedron/constructor.py index 805e33b0405..7f68ca41ce0 100644 --- a/src/sage/geometry/polyhedron/constructor.py +++ b/src/sage/geometry/polyhedron/constructor.py @@ -301,7 +301,7 @@ def Polyhedron(vertices=None, rays=None, lines=None, ieqs=None, eqns=None, ambient_dim=None, base_ring=None, minimize=True, verbose=False, - backend=None): + backend=None, mutable=False): r""" Construct a polyhedron object. @@ -346,32 +346,35 @@ def Polyhedron(vertices=None, rays=None, lines=None, * ``'cdd'``: use cdd (:mod:`~sage.geometry.polyhedron.backend_cdd`) with `\QQ` or - `\RDF` coefficients depending on ``base_ring``. + `\RDF` coefficients depending on ``base_ring`` * ``'normaliz'``: use normaliz (:mod:`~sage.geometry.polyhedron.backend_normaliz`) with `\ZZ` or - `\QQ` coefficients depending on ``base_ring``. + `\QQ` coefficients depending on ``base_ring`` * ``'polymake'``: use polymake (:mod:`~sage.geometry.polyhedron.backend_polymake`) with `\QQ`, `\RDF` or - ``QuadraticField`` coefficients depending on ``base_ring``. + ``QuadraticField`` coefficients depending on ``base_ring`` * ``'ppl'``: use ppl (:mod:`~sage.geometry.polyhedron.backend_ppl`) with `\ZZ` or - `\QQ` coefficients depending on ``base_ring``. + `\QQ` coefficients depending on ``base_ring`` * ``'field'``: use python implementation (:mod:`~sage.geometry.polyhedron.backend_field`) for any field Some backends support further optional arguments: - - ``minimize`` -- boolean (default: ``True``). Whether to - immediately remove redundant H/V-representation data. Currently + - ``minimize`` -- boolean (default: ``True``); whether to + immediately remove redundant H/V-representation data; currently not used. - - ``verbose`` -- boolean (default: ``False``). Whether to print - verbose output for debugging purposes. Only supported by the cdd and - normaliz backends. + - ``verbose`` -- boolean (default: ``False``); whether to print + verbose output for debugging purposes; only supported by the cdd and + normaliz backends + + - ``mutable`` -- boolean (default: ``False``); whether the polyhedron + is mutable OUTPUT: @@ -460,6 +463,18 @@ def Polyhedron(vertices=None, rays=None, lines=None, ... ValueError: invalid base ring + Create a mutable polyhedron:: + + sage: P = Polyhedron(vertices=[[0, 1], [1, 0]], mutable=True) + sage: P.is_mutable() + True + sage: hasattr(P, "_Vrepresentation") + False + sage: P.Vrepresentation() + (A vertex at (0, 1), A vertex at (1, 0)) + sage: hasattr(P, "_Vrepresentation") + True + .. NOTE:: * Once constructed, a ``Polyhedron`` object is immutable. @@ -505,7 +520,6 @@ def Polyhedron(vertices=None, rays=None, lines=None, ....: (2, 3, 1), (3, 1, 2), (3, 2, 1)], ....: rays=[[1, 1, 1]], lines=[[1, 2, 3]], backend='ppl', ....: base_ring=ZZ) - sage: Q.n_vertices() Traceback (most recent call last): ... TypeError: no conversion of this rational to integer @@ -659,4 +673,4 @@ def Polyhedron(vertices=None, rays=None, lines=None, Hrep = [ieqs, eqns] if got_Vrep: Vrep = [vertices, rays, lines] - return parent(Vrep, Hrep, convert=convert, verbose=verbose) + return parent(Vrep, Hrep, convert=convert, verbose=verbose, mutable=mutable) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index fe7aeaab449..a679128e928 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -169,8 +169,16 @@ def __hash__(self): 2377136578164722109, 5966674064902575359, 4795242501625591634] + + The face of a mutable polyhedron is immutable:: + + sage: p = Polyhedron([[1, 1]], mutable=True) + sage: f = p.faces(0)[0] + sage: _ = hash(f) """ - return hash((self._polyhedron, self._ambient_Vrepresentation_indices)) + from .base import Polyhedron_base + # The face of an mutable polyhedron is still mutable and is invalid when the polyhedron is changed. + return hash((Polyhedron_base.__hash__(self._polyhedron), self._ambient_Vrepresentation_indices)) def vertex_generator(self): """ diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index b04524dac33..20bd1d17f88 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -278,7 +278,6 @@ def recycle(self, polyhedron): TESTS:: sage: p = Polyhedron([(0,0),(1,0),(0,1)]) - sage: _ = p.vertices() sage: n = len(p.parent()._Vertex_pool) sage: p._delete() sage: len(p.parent()._Vertex_pool) - n @@ -566,6 +565,45 @@ def _element_constructor_(self, *args, **kwds): A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 2 vertices sage: P.intersection(Q) A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 2 vertices + + The default is not to copy an object if the parent is ``self``:: + + sage: p = polytopes.cube(backend='field') + sage: P = p.parent() + sage: q = P._element_constructor_(p) + sage: q is p + True + sage: r = P._element_constructor_(p, copy=True) + sage: r is p + False + + When the parent of the object is not ``self``, the default is not to copy:: + + sage: Q = P.base_extend(AA) + sage: q = Q._element_constructor_(p) + sage: q is p + False + sage: q = Q._element_constructor_(p, copy=False) + Traceback (most recent call last): + ... + ValueError: you need to make a copy when changing the parent + + For mutable polyhedra either ``copy`` or ``mutable`` must be specified:: + + sage: p = Polyhedron(vertices=[[0, 1], [1, 0]], mutable=True) + sage: P = p.parent() + sage: q = P._element_constructor_(p) + Traceback (most recent call last): + ... + ValueError: must make a copy to obtain immutable object from mutable input + sage: q = P._element_constructor_(p, mutable=True) + sage: q is p + True + sage: r = P._element_constructor_(p, copy=True) + sage: r.is_mutable() + False + sage: r is p + False """ nargs = len(args) convert = kwds.pop('convert', True) @@ -597,8 +635,18 @@ def convert_base_ring_Hrep(lstlst): Vrep = [convert_base_ring(_) for _ in Vrep] return self.element_class(self, Vrep, Hrep, **kwds) if nargs == 1 and is_Polyhedron(args[0]): + copy = kwds.pop('copy', args[0].parent() is not self) + mutable = kwds.pop('mutable', False) + + if not copy and args[0].parent() is not self: + raise ValueError("you need to make a copy when changing the parent") + if args[0].is_mutable() and not copy and not mutable: + raise ValueError("must make a copy to obtain immutable object from mutable input") + if not copy and mutable is args[0].is_mutable(): + return args[0] + polyhedron = args[0] - return self._element_constructor_polyhedron(polyhedron, **kwds) + return self._element_constructor_polyhedron(polyhedron, mutable=mutable, **kwds) if nargs == 1 and args[0] == 0: return self.zero() raise ValueError('Cannot convert to polyhedron object.') @@ -1097,7 +1145,7 @@ def _element_constructor_polyhedron(self, polyhedron, **kwds): A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices """ if polyhedron.backend() == "ppl": - return self._element_constructor_(None, None, ppl_polyhedron=polyhedron._ppl_polyhedron) + return self._element_constructor_(None, None, ppl_polyhedron=polyhedron._ppl_polyhedron, **kwds) else: return Polyhedra_base._element_constructor_polyhedron(self, polyhedron, **kwds) @@ -1128,7 +1176,7 @@ def _element_constructor_polyhedron(self, polyhedron, **kwds): A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices """ if polyhedron.backend() == "ppl": - return self._element_constructor_(None, None, ppl_polyhedron=polyhedron._ppl_polyhedron) + return self._element_constructor_(None, None, ppl_polyhedron=polyhedron._ppl_polyhedron, **kwds) else: return Polyhedra_base._element_constructor_polyhedron(self, polyhedron, **kwds) From 72968a944ba6514566c074e76a304daf57c19135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 13 Jul 2021 14:13:03 +0200 Subject: [PATCH 303/336] better code for is_triangular --- src/sage/matrix/matrix2.pyx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index f9fd7fe6a62..dad89466c94 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -9244,23 +9244,23 @@ cdef class Matrix(Matrix1): sage: m = matrix(QQ,2,[1,2,0,1]) sage: m.is_triangular("upper") True + sage: m.is_triangular("lower") + False """ if not self.is_square(): return False cdef Py_ssize_t i, j if side == "upper": - for i in range(self._nrows): - for j in range(self._ncols): - if i > j: - if not self.get_unsafe(i, j).is_zero(): - return False + for i in range(1, self._nrows): + for j in range(i): + if not self.get_unsafe(i, j).is_zero(): + return False else: - for i in range(self._nrows): - for j in range(self._ncols): - if i < j: - if not self.get_unsafe(i, j).is_zero(): - return False + for i in range(self._nrows - 1): + for j in range(i + 1, self._ncols): + if not self.get_unsafe(i, j).is_zero(): + return False return True def is_unitary(self) -> bool: From 7548c12c494aab9a072c07fdd8969c837200ef63 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 13 Jul 2021 15:51:29 +0200 Subject: [PATCH 304/336] invalidate dependent object on mutable polyhedron --- src/sage/geometry/newton_polygon.py | 2 + src/sage/geometry/polyhedron/backend_ppl.py | 12 +-- src/sage/geometry/polyhedron/base.py | 44 ++++++++++- src/sage/geometry/polyhedron/base_mutable.py | 73 ++++++++++++++++++- src/sage/geometry/polyhedron/face.py | 11 +-- src/sage/geometry/polyhedron/parent.py | 8 +- .../geometry/polyhedron/representation.py | 4 + src/sage/geometry/relative_interior.py | 2 + 8 files changed, 139 insertions(+), 17 deletions(-) diff --git a/src/sage/geometry/newton_polygon.py b/src/sage/geometry/newton_polygon.py index 4a2a3c0df13..54d8a3c2f4d 100644 --- a/src/sage/geometry/newton_polygon.py +++ b/src/sage/geometry/newton_polygon.py @@ -53,6 +53,8 @@ def __init__(self, polyhedron, parent): Element.__init__(self, parent) self._polyhedron = polyhedron self._vertices = None + if polyhedron.is_mutable(): + polyhedron._add_dependent_object(self) def _repr_(self): """ diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index d6ef52c9c44..c6b1e9e05e8 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -36,6 +36,7 @@ class Polyhedron_ppl(Polyhedron_mutable): """ _backend_object_name = "ppl_polyhedron" + _is_mutable = True def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, mutable=False, **kwds): """ @@ -54,6 +55,8 @@ def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, mutable=False, **kwd sage: p = q.parent().element_class(q.parent(), None, None, q._ppl_polyhedron) sage: TestSuite(p).run() """ + # This is important. For some reason the element constructor copies the list sometimes. + self._dependent_objects = [] if ppl_polyhedron: if Hrep is not None or Vrep is not None: raise ValueError("only one of Vrep, Hrep, or ppl_polyhedron can be different from None") @@ -62,7 +65,6 @@ def __init__(self, parent, Vrep, Hrep, ppl_polyhedron=None, mutable=False, **kwd self._init_from_ppl_polyhedron(ppl_polyhedron, minimize) else: Polyhedron_mutable.__init__(self, parent, Vrep, Hrep, **kwds) - self._is_mutable = True if not mutable: self.set_immutable() @@ -209,10 +211,10 @@ def Vrepresentation(self, index=None): sage: P = p.parent() sage: p = P._element_constructor_(p, mutable=True) sage: p.Vrepresentation(0) - A vertex at (1, -1, -1) + A vertex at (-1, -1, -1) sage: p._clear_cache() sage: p.Vrepresentation(0) - A vertex at (1, -1, -1) + A vertex at (-1, -1, -1) sage: TestSuite(p).run() """ if not hasattr(self, '_Vrepresentation'): @@ -323,10 +325,10 @@ def Hrepresentation(self, index=None): sage: P = p.parent() sage: p = P._element_constructor_(p, mutable=True) sage: p.Hrepresentation(0) - An inequality (-1, 0, 0) x + 1 >= 0 + An inequality (0, 0, -1) x + 1 >= 0 sage: p._clear_cache() sage: p.Hrepresentation(0) - An inequality (-1, 0, 0) x + 1 >= 0 + An inequality (0, 0, -1) x + 1 >= 0 sage: TestSuite(p).run() """ if not hasattr(self, '_Hrepresentation'): diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e52def4dcb8..47d8890e85f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6621,6 +6621,48 @@ def hasse_diagram(self): Digraph on 20 vertices sage: D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3 + + Faces of an mutable polyhedron are not hashable. Hence those are not suitable as + vertices of the hasse diagram. Use the combinatorial polyhedron instead:: + + sage: P = polytopes.regular_polygon(4).pyramid() + sage: parent = P.parent() + sage: parent = parent.change_ring(QQ, backend='ppl') + sage: Q = parent._element_constructor_(P, mutable=True) + sage: Q.hasse_diagram() + Traceback (most recent call last): + ... + TypeError: mutable polyhedra are unhashable + sage: C = Q.combinatorial_polyhedron() + sage: D = C.hasse_diagram() + sage: set(D.vertices()) == set(range(20)) + True + sage: def index_to_combinatorial_face(n): + ....: return C.face_by_face_lattice_index(n) + sage: D.relabel(index_to_combinatorial_face, inplace=True) + sage: D.vertices() + [A -1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 0-dimensional face of a 3-dimensional combinatorial polyhedron, + A 0-dimensional face of a 3-dimensional combinatorial polyhedron, + A 0-dimensional face of a 3-dimensional combinatorial polyhedron, + A 0-dimensional face of a 3-dimensional combinatorial polyhedron, + A 0-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 2-dimensional face of a 3-dimensional combinatorial polyhedron, + A 2-dimensional face of a 3-dimensional combinatorial polyhedron, + A 2-dimensional face of a 3-dimensional combinatorial polyhedron, + A 2-dimensional face of a 3-dimensional combinatorial polyhedron, + A 2-dimensional face of a 3-dimensional combinatorial polyhedron, + A 3-dimensional face of a 3-dimensional combinatorial polyhedron] + sage: D.degree_polynomial() + x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3 """ from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face @@ -10489,7 +10531,7 @@ def _test_is_combinatorially_isomorphic(self, tester=None, **options): if self.n_vertices(): tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center())) - if self.n_vertices() < 20 and self.n_facets() < 20: + if self.n_vertices() < 20 and self.n_facets() < 20 and self.is_immutable(): tester.assertTrue(self.is_combinatorially_isomorphic(ZZ(4)*self, algorithm='face_lattice')) if self.n_vertices(): tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center(), algorithm='face_lattice')) diff --git a/src/sage/geometry/polyhedron/base_mutable.py b/src/sage/geometry/polyhedron/base_mutable.py index adf1d2d35be..ee4b269e3e6 100644 --- a/src/sage/geometry/polyhedron/base_mutable.py +++ b/src/sage/geometry/polyhedron/base_mutable.py @@ -56,11 +56,82 @@ def _clear_cache(self): """ if not self._is_mutable: raise TypeError("cannot clear cache of immutable polyhedra") - self.parent().recycle(self) + + # Invalidate object pointing towards this polyhedron (faces etc.). + for ob in self._dependent_objects: + ob._polyhedron = None backend_object = self.__dict__["_" + self._backend_object_name] del self.__dict__ self.__dict__["_" + self._backend_object_name] = backend_object self._is_mutable = True + self._dependent_objects = [] + + def _add_dependent_object(self, ob): + r""" + Add an object that has ``self`` has attribute ``_polyhedron``. + + When ``self`` is modified, we delete this attribute to invalidate those objects. + + EXAMPLES:: + + sage: p = Polyhedron([[1, 1]], mutable=True) + sage: class foo: + ....: def __init__(self, p): + ....: self._polyhedron = p + ....: + sage: a = foo(p) + sage: a.__dict__ + {'_polyhedron': A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex} + sage: p._add_dependent_object(a) + sage: p._clear_cache() + sage: a.__dict__ + {'_polyhedron': None} + + TESTS:: + + sage: from sage.geometry.newton_polygon import NewtonPolygon + sage: p = Polyhedron([[1, 1]], mutable=True) + sage: n = NewtonPolygon(p) + sage: n + Finite Newton polygon with 1 vertex: (1, 1) + sage: n = NewtonPolygon(p) + sage: p._clear_cache() + sage: n + ) failed: AttributeError: 'NoneType' object has no attribute 'vertices'> + + :: + + sage: f = p.faces(0)[0]; f + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: p._clear_cache() + sage: f + ) failed: AttributeError: 'NoneType' object has no attribute 'parent'> + + :: + + sage: v = p.vertices()[0] + sage: p = Polyhedron([[1, 1]], mutable=True) + sage: v = p.Vrepresentation(0); v + A vertex at (1, 1) + sage: h = p.Hrepresentation(0); h + An equation (0, 1) x - 1 == 0 + sage: p._clear_cache() + sage: v.polyhedron() is None + True + sage: h.polyhedron() is None + True + + :: + + sage: p = Polyhedron([[1, 0], [0, 1]], mutable=True) + sage: r = p.relative_interior() + sage: p._clear_cache() + sage: r + Relative interior of None + """ + if not ob._polyhedron is self: + raise ValueError + self._dependent_objects.append(ob) def is_mutable(self): r""" diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index a679128e928..ba6ccc5714a 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -158,6 +158,8 @@ def __init__(self, polyhedron, V_indices, H_indices): self._ambient_Hrepresentation_indices = tuple(H_indices) self._ambient_Vrepresentation = tuple( polyhedron.Vrepresentation(i) for i in V_indices ) self._ambient_Hrepresentation = tuple( polyhedron.Hrepresentation(i) for i in H_indices ) + if polyhedron.is_mutable(): + polyhedron._add_dependent_object(self) def __hash__(self): r""" @@ -169,16 +171,9 @@ def __hash__(self): 2377136578164722109, 5966674064902575359, 4795242501625591634] - - The face of a mutable polyhedron is immutable:: - - sage: p = Polyhedron([[1, 1]], mutable=True) - sage: f = p.faces(0)[0] - sage: _ = hash(f) """ from .base import Polyhedron_base - # The face of an mutable polyhedron is still mutable and is invalid when the polyhedron is changed. - return hash((Polyhedron_base.__hash__(self._polyhedron), self._ambient_Vrepresentation_indices)) + return hash((self._polyhedron, self._ambient_Vrepresentation_indices)) def vertex_generator(self): """ diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 20bd1d17f88..74d961d1a3e 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -296,6 +296,8 @@ def recycle(self, polyhedron): Vrep._polyhedron = None polyhedron._Hrepresentation = None polyhedron._Vrepresentation = None + if polyhedron.is_mutable(): + polyhedron._dependent_objects = [] def ambient_dim(self): r""" @@ -1144,8 +1146,9 @@ def _element_constructor_polyhedron(self, polyhedron, **kwds): sage: P(p) A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices """ + from copy import copy if polyhedron.backend() == "ppl": - return self._element_constructor_(None, None, ppl_polyhedron=polyhedron._ppl_polyhedron, **kwds) + return self._element_constructor_(None, None, ppl_polyhedron=copy(polyhedron._ppl_polyhedron), **kwds) else: return Polyhedra_base._element_constructor_polyhedron(self, polyhedron, **kwds) @@ -1175,8 +1178,9 @@ def _element_constructor_polyhedron(self, polyhedron, **kwds): sage: P(p) A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices """ + from copy import copy if polyhedron.backend() == "ppl": - return self._element_constructor_(None, None, ppl_polyhedron=polyhedron._ppl_polyhedron, **kwds) + return self._element_constructor_(None, None, ppl_polyhedron=copy(polyhedron._ppl_polyhedron), **kwds) else: return Polyhedra_base._element_constructor_polyhedron(self, polyhedron, **kwds) diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index dd538422e94..1fcefddd53b 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -435,6 +435,8 @@ def _set_data(self, polyhedron, data): self._index = len(polyhedron._Hrepresentation) polyhedron._Hrepresentation.append(self) self._polyhedron = polyhedron + if polyhedron.is_mutable(): + polyhedron._add_dependent_object(self) def is_H(self): """ @@ -1166,6 +1168,8 @@ def _set_data(self, polyhedron, data): self._index = len(polyhedron._Vrepresentation) polyhedron._Vrepresentation.append(self) self._polyhedron = polyhedron + if polyhedron.is_mutable(): + polyhedron._add_dependent_object(self) def is_V(self): """ diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 9e49420a5e4..6998b770496 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -52,6 +52,8 @@ def __init__(self, polyhedron): sage: TestSuite(RelativeInterior(P)).run() """ self._polyhedron = polyhedron + if polyhedron.is_mutable(): + polyhedron._add_dependent_object(self) def __contains__(self, point): r""" From 193a2b9edc72125bd14a158980f5866db44667c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 13 Jul 2021 17:10:42 +0200 Subject: [PATCH 305/336] details in ticket 32193 --- src/sage/matrix/matrix2.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index dad89466c94..f8c6d91c236 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -9225,7 +9225,7 @@ cdef class Matrix(Matrix1): """ Return ``True`` if this matrix is a triangular matrix. - INPUT + INPUT: - ``side`` -- either ``"lower"`` (default) or ``"upper"`` @@ -9235,13 +9235,13 @@ cdef class Matrix(Matrix1): EXAMPLES:: - sage: m = matrix(QQ,2,2,range(4)) + sage: m = matrix(QQ, 2, 2, range(4)) sage: m.is_triangular() False - sage: m = matrix(QQ,2,[5,0,0,5]) + sage: m = matrix(QQ, 2, [5, 0, 0, 5]) sage: m.is_triangular() True - sage: m = matrix(QQ,2,[1,2,0,1]) + sage: m = matrix(QQ, 2, [1, 2, 0, 1]) sage: m.is_triangular("upper") True sage: m.is_triangular("lower") @@ -9265,7 +9265,7 @@ cdef class Matrix(Matrix1): def is_unitary(self) -> bool: r""" - Returns ``True`` if the columns of the matrix are an orthonormal basis. + Return ``True`` if the columns of the matrix are an orthonormal basis. For a matrix with real entries this determines if a matrix is "orthogonal" and for a matrix with complex entries this determines From 32b992c04777e65dae686dd18b27fb5208525291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 13 Jul 2021 17:28:08 +0200 Subject: [PATCH 306/336] fixing some details about doc formatting --- src/doc/en/developer/doctesting.rst | 4 ++-- src/sage/algebras/free_algebra_quotient.py | 6 +++--- src/sage/algebras/nil_coxeter_algebra.py | 8 +++----- .../algebras/rational_cherednik_algebra.py | 4 ++-- src/sage/combinat/sf/hall_littlewood.py | 8 ++++---- src/sage/combinat/sf/jack.py | 20 +++++++++---------- src/sage/combinat/sf/llt.py | 2 +- src/sage/combinat/sf/macdonald.py | 12 +++++------ src/sage/crypto/mq/rijndael_gf.py | 6 +++--- .../hyperbolic_space/hyperbolic_geodesic.py | 4 +--- src/sage/groups/braid.py | 2 +- src/sage/interfaces/maxima_abstract.py | 6 ++++-- src/sage/modular/multiple_zeta.py | 6 +++--- .../rings/padics/padic_generic_element.pyx | 2 +- src/sage/schemes/elliptic_curves/gal_reps.py | 2 +- src/sage/schemes/plane_conics/con_field.py | 6 +++--- .../schemes/plane_conics/con_number_field.py | 4 ++-- .../plane_conics/con_rational_field.py | 4 ++-- 18 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/doc/en/developer/doctesting.rst b/src/doc/en/developer/doctesting.rst index 87ed66c3bdc..f5fec4590f3 100644 --- a/src/doc/en/developer/doctesting.rst +++ b/src/doc/en/developer/doctesting.rst @@ -1010,8 +1010,8 @@ a Python exception occurs. As an example, I modified 152 ainvs = [K(0),K(0),K(0)] + ainvs 153 self.__ainvs = tuple(ainvs) 154 if self.discriminant() == 0: - 155 raise ArithmeticError, \ - 156 -> "Invariants %s define a singular curve."%ainvs + 155 raise ArithmeticError( + 156 -> "Invariants %s define a singular curve."%ainvs) 157 PP = projective_space.ProjectiveSpace(2, K, names='xyz'); 158 x, y, z = PP.coordinate_ring().gens() 159 a1, a2, a3, a4, a6 = ainvs diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py index 13faa60d992..232a5b30f47 100644 --- a/src/sage/algebras/free_algebra_quotient.py +++ b/src/sage/algebras/free_algebra_quotient.py @@ -145,15 +145,15 @@ def __init__(self, A, mons, mats, names): raise TypeError("Argument A must be an algebra.") R = A.base_ring() # if not R.is_field(): # TODO: why? -# raise TypeError, "Base ring of argument A must be a field." +# raise TypeError("Base ring of argument A must be a field.") n = A.ngens() assert n == len(mats) self.__free_algebra = A self.__ngens = n self.__dim = len(mons) - self.__module = FreeModule(R,self.__dim) + self.__module = FreeModule(R, self.__dim) self.__matrix_action = mats - self.__monomial_basis = mons # elements of free monoid + self.__monomial_basis = mons # elements of free monoid Algebra.__init__(self, R, names, normalize=True) def _element_constructor_(self, x): diff --git a/src/sage/algebras/nil_coxeter_algebra.py b/src/sage/algebras/nil_coxeter_algebra.py index 041b9094760..f44039253ac 100644 --- a/src/sage/algebras/nil_coxeter_algebra.py +++ b/src/sage/algebras/nil_coxeter_algebra.py @@ -65,18 +65,16 @@ def __init__(self, W, base_ring = QQ, prefix='u'): self._base_ring = base_ring self._cartan_type = W.cartan_type() H = IwahoriHeckeAlgebra(W, 0, 0, base_ring=base_ring) - super(IwahoriHeckeAlgebra.T,self).__init__(H, prefix=prefix) + super(IwahoriHeckeAlgebra.T, self).__init__(H, prefix=prefix) def _repr_(self): r""" - EXAMPLES :: + EXAMPLES:: sage: NilCoxeterAlgebra(WeylGroup(['A',3,1])) # indirect doctest The Nil-Coxeter Algebra of Type A3~ over Rational Field - """ - - return "The Nil-Coxeter Algebra of Type %s over %s"%(self._cartan_type._repr_(compact=True), self.base_ring()) + return "The Nil-Coxeter Algebra of Type %s over %s" % (self._cartan_type._repr_(compact=True), self.base_ring()) def homogeneous_generator_noncommutative_variables(self, r): r""" diff --git a/src/sage/algebras/rational_cherednik_algebra.py b/src/sage/algebras/rational_cherednik_algebra.py index e551c43c568..7d7e9f7929f 100644 --- a/src/sage/algebras/rational_cherednik_algebra.py +++ b/src/sage/algebras/rational_cherednik_algebra.py @@ -184,11 +184,11 @@ def _reflections(self): d[s] = (r, r.associated_coroot(), c) return d - def _repr_(self): + def _repr_(self) -> str: r""" Return a string representation of ``self``. - EXAMPLES :: + EXAMPLES:: sage: RationalCherednikAlgebra(['A',4], 2, 1, QQ) Rational Cherednik Algebra of type ['A', 4] with c=2 and t=1 diff --git a/src/sage/combinat/sf/hall_littlewood.py b/src/sage/combinat/sf/hall_littlewood.py index f07d133e2f5..e2a2f18f775 100644 --- a/src/sage/combinat/sf/hall_littlewood.py +++ b/src/sage/combinat/sf/hall_littlewood.py @@ -66,7 +66,7 @@ def __repr__(self): - a string representing the class - EXAMPLES :: + EXAMPLES:: sage: SymmetricFunctions(QQ).hall_littlewood(1) Hall-Littlewood polynomials with t=1 over Rational Field @@ -102,7 +102,7 @@ def symmetric_function_ring( self ): - returns the ring of symmetric functions - EXAMPLES :: + EXAMPLES:: sage: HL = SymmetricFunctions(FractionField(QQ['t'])).hall_littlewood() sage: HL.symmetric_function_ring() @@ -123,7 +123,7 @@ def base_ring( self ): The base ring of the symmetric functions. - EXAMPLES :: + EXAMPLES:: sage: HL = SymmetricFunctions(QQ['t'].fraction_field()).hall_littlewood(t=1) sage: HL.base_ring() @@ -532,7 +532,7 @@ def hall_littlewood_family(self): - returns the class of Hall-Littlewood bases - EXAMPLES :: + EXAMPLES:: sage: HLP = SymmetricFunctions(FractionField(QQ['t'])).hall_littlewood(1).P() sage: HLP.hall_littlewood_family() diff --git a/src/sage/combinat/sf/jack.py b/src/sage/combinat/sf/jack.py index daee291d232..514823e82fb 100644 --- a/src/sage/combinat/sf/jack.py +++ b/src/sage/combinat/sf/jack.py @@ -491,7 +491,7 @@ def __init__(self, jack): - ``self`` -- a Jack basis of the symmetric functions - ``jack`` -- a family of Jack symmetric function bases - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(FractionField(QQ['t'])) sage: JP = Sym.jack().P(); JP.base_ring() @@ -537,7 +537,7 @@ def _m_to_self(self, x): - an element of ``self`` equivalent to ``x`` - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: JP = Sym.jack(t=2).P() @@ -565,7 +565,7 @@ def _self_to_m(self, x): - an element of the monomial basis equivalent to ``x`` - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: JP = Sym.jack(t=2).P() @@ -596,7 +596,7 @@ def c1(self, part): - a polynomial in the parameter ``t`` which is equal to the scalar product of ``J(part)`` and ``P(part)`` - EXAMPLES :: + EXAMPLES:: sage: JP = SymmetricFunctions(FractionField(QQ['t'])).jack().P() sage: JP.c1(Partition([2,1])) @@ -1029,7 +1029,7 @@ def scalar_jack(self, x, t=None): - ``self`` -- an element of the Jack `P` basis - ``x`` -- an element of the `P` basis - EXAMPLES :: + EXAMPLES:: sage: JP = SymmetricFunctions(FractionField(QQ['t'])).jack().P() sage: l = [JP(p) for p in Partitions(3)] @@ -1234,7 +1234,7 @@ def _self_to_h( self, x ): - an element of the homogeneous basis equivalent to ``x`` - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: JQp = Sym.jack(t=2).Qp() @@ -1262,7 +1262,7 @@ def _h_to_self( self, x ): - an element of the Jack `Qp` basis equivalent to ``x`` - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: JQp = Sym.jack(t=2).Qp() @@ -1320,7 +1320,7 @@ def __init__(self, Sym): - ``self`` -- a zonal basis of the symmetric functions - ``Sym`` -- a ring of the symmetric functions - EXAMPLES :: + EXAMPLES:: sage: Z = SymmetricFunctions(QQ).zonal() sage: Z([2])^2 @@ -1355,7 +1355,7 @@ def product(self, left, right): the product of ``left`` and ``right`` expanded in the basis ``self`` - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: Z = Sym.zonal() @@ -1386,7 +1386,7 @@ def scalar_zonal(self, x): - the scalar product between ``self`` and ``x`` - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: Z = Sym.zonal() diff --git a/src/sage/combinat/sf/llt.py b/src/sage/combinat/sf/llt.py index b913114435b..d7e4af8fe94 100644 --- a/src/sage/combinat/sf/llt.py +++ b/src/sage/combinat/sf/llt.py @@ -168,7 +168,7 @@ def symmetric_function_ring( self ): - returns the symmetric function ring associated to ``self``. - EXAMPLES :: + EXAMPLES:: sage: L3 = SymmetricFunctions(FractionField(QQ['t'])).llt(3) sage: L3.symmetric_function_ring() diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index f7c852aeb92..938b09d23dd 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -89,7 +89,7 @@ def __repr__(self): - a string representing the Macdonald symmetric function family - EXAMPLES :: + EXAMPLES:: sage: t = QQ['t'].gen(); SymmetricFunctions(QQ['t'].fraction_field()).macdonald(q=t,t=1) Macdonald polynomials with q=t and t=1 over Fraction Field of Univariate Polynomial Ring in t over Rational Field @@ -107,7 +107,7 @@ def __init__(self, Sym, q='q', t='t'): - ``self`` -- a family of Macdonald symmetric function bases - EXAMPLES :: + EXAMPLES:: sage: t = QQ['t'].gen(); SymmetricFunctions(QQ['t'].fraction_field()).macdonald(q=t,t=1) Macdonald polynomials with q=t and t=1 over Fraction Field of Univariate Polynomial Ring in t over Rational Field @@ -144,7 +144,7 @@ def base_ring( self ): - the base ring associated to the corresponding symmetric function ring - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ['q'].fraction_field()) sage: Mac = Sym.macdonald(t=0) @@ -166,7 +166,7 @@ def symmetric_function_ring( self ): - the symmetric function ring associated to the Macdonald bases - EXAMPLES :: + EXAMPLES:: sage: Mac = SymmetricFunctions(QQ['q'].fraction_field()).macdonald(t=0) sage: Mac.symmetric_function_ring() @@ -187,7 +187,7 @@ def P(self): - returns the `P` Macdonald basis of symmetric functions - EXAMPLES :: + EXAMPLES:: sage: Sym = SymmetricFunctions(FractionField(QQ['q','t'])) sage: P = Sym.macdonald().P(); P @@ -914,7 +914,7 @@ def macdonald_family(self): - the family of Macdonald symmetric functions associated to ``self`` - EXAMPLES :: + EXAMPLES:: sage: MacP = SymmetricFunctions(QQ['q'].fraction_field()).macdonald(t=0).P() sage: MacP.macdonald_family() diff --git a/src/sage/crypto/mq/rijndael_gf.py b/src/sage/crypto/mq/rijndael_gf.py index ad908a0ffcd..eb6119bafe0 100644 --- a/src/sage/crypto/mq/rijndael_gf.py +++ b/src/sage/crypto/mq/rijndael_gf.py @@ -46,7 +46,7 @@ - Thomas Gagne (2015-06): initial version -EXAMPLES +EXAMPLES: We build Rijndael-GF with a block length of 4 and a key length of 6:: @@ -1020,7 +1020,7 @@ def decrypt(self, ciphertext, key, format='hex'): - A string in the format ``format`` of ``ciphertext`` decrypted with key ``key``. - EXAMPLES :: + EXAMPLES:: sage: from sage.crypto.mq.rijndael_gf import RijndaelGF sage: rgf = RijndaelGF(4, 4) @@ -1473,7 +1473,7 @@ def compose(self, f, g, algorithm='encrypt', f_attr=None, g_attr=None): then ``compose`` returns `g(f(A))_{i,j}`, where `A` is an arbitrary input state matrix. - EXAMPLES + EXAMPLES: This function allows us to determine the polynomial representations of entries across multiple round functions. For example, if we diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index d8778307934..9ec022099d0 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -140,13 +140,11 @@ def __init__(self, model, start, end, **graphics_options): r""" See :class:`HyperbolicGeodesic` for full documentation. - EXAMPLES :: + EXAMPLES:: sage: HyperbolicPlane().UHP().get_geodesic(I, 2 + I) Geodesic in UHP from I to I + 2 - """ - self._model = model self._start = start self._end = end diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 81d09d8e369..a7fc91b1e7b 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -2077,7 +2077,7 @@ def mapping_class_action(self, F): A :class:`MappingClassGroupAction`. - EXAMPLES :: + EXAMPLES:: sage: B = BraidGroup(3) sage: B.inject_variables() diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index d8102801ba8..5a5fdcdc290 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -674,9 +674,11 @@ def function(self, args, defn, rep=None, latex=None): ## represented in 2-d. ## INPUT: -## flag -- bool (default: True) -## EXAMPLES +## flag -- bool (default: True) + +## EXAMPLES:: + ## sage: maxima('1/2') ## 1/2 ## sage: maxima.display2d(True) diff --git a/src/sage/modular/multiple_zeta.py b/src/sage/modular/multiple_zeta.py index d1f1f550247..8b7524a92a5 100644 --- a/src/sage/modular/multiple_zeta.py +++ b/src/sage/modular/multiple_zeta.py @@ -968,7 +968,7 @@ def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. - INPUT + INPUT: - ``x`` -- either a list, tuple, word or a multiple zeta value @@ -1884,7 +1884,7 @@ def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. - INPUT + INPUT: - ``x`` -- either a list, tuple, word or a multiple zeta value @@ -2151,7 +2151,7 @@ def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. - INPUT + INPUT: - ``x`` -- either a list, tuple, word diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 37b2393c9ce..5a9e1f58824 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -4053,7 +4053,7 @@ cdef class pAdicGenericElement(LocalGenericElement): - `Li_n(`self`)` - EXAMPLES :: + EXAMPLES:: sage: Qp(2)(-1)._polylog_res_1(6) == 0 True diff --git a/src/sage/schemes/elliptic_curves/gal_reps.py b/src/sage/schemes/elliptic_curves/gal_reps.py index b1c427c65bf..1f88e23a80c 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps.py +++ b/src/sage/schemes/elliptic_curves/gal_reps.py @@ -728,7 +728,7 @@ def image_type(self, p): - a string. - EXAMPLES :: + EXAMPLES:: sage: E = EllipticCurve('14a1') sage: rho = E.galois_representation() diff --git a/src/sage/schemes/plane_conics/con_field.py b/src/sage/schemes/plane_conics/con_field.py index 823595850b5..04ee419f880 100644 --- a/src/sage/schemes/plane_conics/con_field.py +++ b/src/sage/schemes/plane_conics/con_field.py @@ -922,7 +922,7 @@ def point(self, v, check=True): If no rational point on ``self`` is known yet, then also caches the point for use by ``self.rational_point()`` and ``self.parametrization()``. - EXAMPLES :: + EXAMPLES:: sage: c = Conic([1, -1, 1]) sage: c.point([15, 17, 8]) @@ -961,7 +961,7 @@ def random_rational_point(self, *args1, **args2): If the base field is a finite field, then the output is uniformly distributed over the points of self. - EXAMPLES :: + EXAMPLES:: sage: c = Conic(GF(2), [1,1,1,1,1,0]) sage: [c.random_rational_point() for i in range(10)] # output is random @@ -1130,7 +1130,7 @@ def symmetric_matrix(self): The symmetric matrix `M` such that `(x y z) M (x y z)^t` is the defining equation of ``self``. - EXAMPLES :: + EXAMPLES:: sage: R. = QQ[] sage: C = Conic(x^2 + x*y/2 + y^2 + z^2) diff --git a/src/sage/schemes/plane_conics/con_number_field.py b/src/sage/schemes/plane_conics/con_number_field.py index 611690274c0..fdd9e2c02ad 100644 --- a/src/sage/schemes/plane_conics/con_number_field.py +++ b/src/sage/schemes/plane_conics/con_number_field.py @@ -47,7 +47,7 @@ def __init__(self, A, f): r""" See ``Conic`` for full documentation. - EXAMPLES :: + EXAMPLES:: sage: Conic([1, 1, 1]) Projective Conic Curve over Rational Field defined by x^2 + y^2 + z^2 @@ -364,7 +364,7 @@ def local_obstructions(self, finite=True, infinite=True, read_cache=True): Local obstructions are cached. The parameter ``read_cache`` specifies whether to look at the cache before computing anything. - EXAMPLES :: + EXAMPLES:: sage: K. = QuadraticField(-1) sage: Conic(K, [1, 2, 3]).local_obstructions() diff --git a/src/sage/schemes/plane_conics/con_rational_field.py b/src/sage/schemes/plane_conics/con_rational_field.py index 3eae681ba68..756d92dcab8 100644 --- a/src/sage/schemes/plane_conics/con_rational_field.py +++ b/src/sage/schemes/plane_conics/con_rational_field.py @@ -259,7 +259,7 @@ def local_obstructions(self, finite=True, infinite=True, read_cache=True): Local obstructions are cached. The parameter ``read_cache`` specifies whether to look at the cache before computing anything. - EXAMPLES :: + EXAMPLES:: sage: Conic(QQ, [1, 1, 1]).local_obstructions() [2, -1] @@ -318,7 +318,7 @@ def parametrization(self, point=None, morphism=True): Uses the PARI/GP function ``qfparam``. - EXAMPLES :: + EXAMPLES:: sage: c = Conic([1,1,-1]) sage: c.parametrization() From d2f701149d765e18eb37fc5956dd3058b610be22 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 13 Jul 2021 17:23:56 -0500 Subject: [PATCH 307/336] Add test for __hash__ --- src/sage/groups/class_function.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/groups/class_function.py b/src/sage/groups/class_function.py index d0ed4e501f9..5a47da10538 100644 --- a/src/sage/groups/class_function.py +++ b/src/sage/groups/class_function.py @@ -106,7 +106,13 @@ class ClassFunction_gap(SageObject): """ def __hash__(self): + r""" + TESTS:: + sage: G = SymmetricGroup(5) + sage: chi1 = ClassFunction(G,[1,1,1,1,1,1,1]) + sage: d = {chi1:'trivial'} + """ return hash(self.__repr__()) def __init__(self, G, values): From d77acee79a86c1b9544b0b1d38a45cde3190087c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 13 Jul 2021 16:43:57 -0700 Subject: [PATCH 308/336] sage.game_theory: Switch to new lrsnash input format --- src/sage/game_theory/normal_form_game.py | 98 +++++++++++++++++++++--- src/sage/game_theory/parser.py | 41 ++++++---- 2 files changed, 115 insertions(+), 24 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index f164298f0fe..325d2f3b98f 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -564,7 +564,7 @@ sage: A = matrix([[3,3],[2,5],[0,6]]) sage: B = matrix([[3,3],[2,6],[3,1]]) sage: degenerate_game = NormalFormGame([A,B]) - sage: degenerate_game.obtain_nash(algorithm='lrs') # optional - lrslib + sage: degenerate_game.obtain_nash(algorithm='lrs') # random, optional - lrslib [[(0, 1/3, 2/3), (1/3, 2/3)], [(1, 0, 0), (1/2, 3)], [(1, 0, 0), (1, 3)]] sage: degenerate_game.obtain_nash(algorithm='LCP') # optional - gambit [[(0.0, 0.3333333333, 0.6666666667), (0.3333333333, 0.6666666667)], @@ -1752,17 +1752,14 @@ def _solve_lrs(self, maximization=True): if maximization is False: m1 = - m1 m2 = - m2 - game1_str, game2_str = self._Hrepresentation(m1, m2) - g1_name = tmp_filename() - with open(g1_name, 'w') as g1_file: - g1_file.write(game1_str) - g2_name = tmp_filename() - with open(g2_name, 'w') as g2_file: - g2_file.write(game2_str) + game_str = self._lrs_nash_format(m1, m2) + game_name = tmp_filename() + with open(game_name, 'w') as game_file: + game_file.write(game_str) try: - process = Popen(['lrsnash', g1_name, g2_name], + process = Popen(['lrsnash', game_name], stdout=PIPE, stderr=PIPE) except OSError as e: @@ -2243,7 +2240,11 @@ def _is_NE(self, a, b, p1_support, p2_support, M1, M2): def _Hrepresentation(self, m1, m2): r""" - Create the H-representation strings required to use lrs nash. + Create the H-representation strings required to use ``lrsnash``. + + Since lrslib 6.1, this format is referred to as "legacy format". + + This method is deprecated. EXAMPLES:: @@ -2251,6 +2252,10 @@ def _Hrepresentation(self, m1, m2): sage: B = matrix([[3, 3], [1, 4]]) sage: C = NormalFormGame([A, B]) sage: print(C._Hrepresentation(A, B)[0]) + doctest:warning... + DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it + creates the legacy input format. Use NormalFormGame._lrs_nash_format instead + See https://trac.sagemath.org/27745 for details. H-representation linearity 1 5 begin @@ -2276,6 +2281,12 @@ def _Hrepresentation(self, m1, m2): """ + from sage.misc.superseded import deprecation + deprecation(27745, + "NormalFormGame._Hrepresentation is deprecated as it " + "creates the legacy input format. " + "Use NormalFormGame._lrs_nash_format instead") + from sage.geometry.polyhedron.misc import _to_space_separated_string m = self.players[0].num_strategies n = self.players[1].num_strategies @@ -2311,6 +2322,73 @@ def _Hrepresentation(self, m1, m2): t += 'end\n' return s, t + def _lrs_nash_format(self, m1, m2): + """ + Create the input format for ``lrsnash``, version 6.1 or newer. + + EXAMPLES: + + An example from the ``lrsnash`` manual in the old and the format:: + + sage: A = matrix([[0, 6], [2, 5], [3, 3]]) + sage: B = matrix([[1, 0], [0, 2], [4, 3]]) + sage: C = NormalFormGame([A, B]) + sage: print(C._lrs_nash_format(A, B)) + 3 2 + + 0 6 + 2 5 + 3 3 + + 1 0 + 0 2 + 4 3 + + + sage: legacy_format = C._Hrepresentation(A, B) + doctest:warning... + DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it + creates the legacy input format. Use NormalFormGame._lrs_nash_format instead + See https://trac.sagemath.org/27745 for details. + sage: print('*game: player 1\n', legacy_format[0]) + *game: player 1 + H-representation + linearity 1 6 + begin + 6 5 rational + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 1 0 + 0 -1 0 -4 1 + 0 0 -2 -3 1 + -1 1 1 1 0 + end + + sage: print('*game: player 2\n', legacy_format[1]) + *game: player 2 + H-representation + linearity 1 6 + begin + 6 4 rational + 0 0 -6 1 + 0 -2 -5 1 + 0 -3 -3 1 + 0 1 0 0 + 0 0 1 0 + -1 1 1 0 + end + """ + from sage.geometry.polyhedron.misc import _to_space_separated_string + m = self.players[0].num_strategies + n = self.players[1].num_strategies + s = f'{m} {n}\n\n' + for r in m1.rows(): + s += _to_space_separated_string(r) + '\n' + s += '\n' + for r in m2.rows(): + s += _to_space_separated_string(r) + '\n' + return s + def is_degenerate(self, certificate=False): """ A function to check whether the game is degenerate or not. diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index f81e928a00e..fc356d4458a 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -24,6 +24,10 @@ def __init__(self, raw_string): sage: A = matrix([[1, 2], [3, 2]]) sage: g = NormalFormGame([A]) sage: raw_string = g._Hrepresentation(A, -A) + doctest:warning... + DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it + creates the legacy input format. Use NormalFormGame._lrs_nash_format instead + See https://trac.sagemath.org/27745 for details. sage: P = Parser(raw_string) sage: print(P.raw_string[0]) H-representation @@ -127,7 +131,7 @@ def __init__(self, raw_string): """ self.raw_string = raw_string - def format_lrs(self): + def format_lrs(self, legacy_format=False): """ Parses the output of lrs so as to return vectors corresponding to equilibria. @@ -140,6 +144,10 @@ def format_lrs(self): sage: A = matrix([[1, 2], [3, 2]]) sage: g = NormalFormGame([A]) sage: game1_str, game2_str = g._Hrepresentation(A, -A) + doctest:warning... + DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it + creates the legacy input format. Use NormalFormGame._lrs_nash_format instead + See https://trac.sagemath.org/27745 for details. sage: g1_name = tmp_filename() sage: g2_name = tmp_filename() sage: g1_file = open(g1_name, 'w') @@ -148,14 +156,14 @@ def format_lrs(self): sage: g1_file.close() sage: _ = g2_file.write(game2_str) sage: g2_file.close() - sage: process = Popen(['lrsnash', g1_name, g2_name], stdout=PIPE, stderr=PIPE) # optional - lrslib - sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # optional - lrslib + sage: process = Popen(['lrsnash', g1_name, g2_name], stdout=PIPE, stderr=PIPE) # not tested, optional - lrslib + sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # not tested, optional - lrslib The above creates a game, writes the H representation to temporary files, calls lrs and stores the output in `lrs_output` (here slicing to get rid of some system parameters that get returned):: - sage: lrs_output[:20] # optional - lrslib + sage: lrs_output[:20] # not tested, optional - lrslib [..., '***** 4 4 rational\n', '2 0 1 2 \n', @@ -172,8 +180,8 @@ def format_lrs(self): The above is pretty messy, here is the output when we put it through the parser:: - sage: nasheq = Parser(lrs_output).format_lrs() # optional - lrslib - sage: nasheq # optional - lrslib + sage: nasheq = Parser(lrs_output).format_lrs(legacy_format=True) # not tested, optional - lrslib + sage: nasheq # not tested, optional - lrslib [[(1/2, 1/2), (0, 1)], [(0, 1), (0, 1)]] Another game:: @@ -194,9 +202,9 @@ def format_lrs(self): sage: g1_file.close() sage: _ = g2_file.write(game2_str) sage: g2_file.close() - sage: process = Popen(['lrsnash', g1_name, g2_name], stdout=PIPE, stderr=PIPE) # optional - lrslib - sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # optional - lrslib - sage: print(lrs_output[:25]) # optional - lrslib + sage: process = Popen(['lrsnash', g1_name, g2_name], stdout=PIPE, stderr=PIPE) # not tested, optional - lrslib + sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # not tested, optional - lrslib + sage: print(lrs_output[:25]) # not tested, optional - lrslib [..., '***** 5 5 rational\n', '2 1/7 0 6/7 23/7 \n', @@ -214,8 +222,8 @@ def format_lrs(self): '*Player 1: vertices=6 bases=7 pivots=10\n', ...] - sage: nasheq = Parser(lrs_output).format_lrs() # optional - lrslib - sage: sorted(nasheq) # optional - lrslib + sage: nasheq = Parser(lrs_output).format_lrs(legacy_format=True) # not tested, optional - lrslib + sage: sorted(nasheq) # not tested, optional - lrslib [[(0, 1, 0), (1, 0, 0)], [(1/3, 2/3, 0), (0, 1/6, 5/6)], [(1/3, 2/3, 0), (1/7, 0, 6/7)], @@ -223,10 +231,15 @@ def format_lrs(self): """ equilibria = [] from sage.misc.sage_eval import sage_eval - from itertools import groupby + from itertools import groupby, dropwhile lines = iter(self.raw_string) - while not next(lines).startswith("*****"): - pass + if legacy_format: + # Skip until the magic stars announce the beginning of the real output + while not next(lines).startswith("*****"): + pass + else: + # Skip comment lines starting with a single star + lines = dropwhile(lambda line: line.startswith('*'), lines) for collection in [list(x[1]) for x in groupby(lines, lambda x: x == '\n')]: if collection[0].startswith('2'): s1 = tuple([sage_eval(k) for k in collection[-1].split()][1:-1]) From 794cf6a863d9cd5aa4cad876286cddd503411490 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 13 Jul 2021 17:23:31 -0700 Subject: [PATCH 309/336] build/pkgs/lrslib/spkg-configure.m4: Check for recent lrsnash --- build/pkgs/lrslib/spkg-configure.m4 | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/build/pkgs/lrslib/spkg-configure.m4 b/build/pkgs/lrslib/spkg-configure.m4 index ec8e98af163..860cca451e0 100644 --- a/build/pkgs/lrslib/spkg-configure.m4 +++ b/build/pkgs/lrslib/spkg-configure.m4 @@ -2,6 +2,24 @@ SAGE_SPKG_CONFIGURE([lrslib], [ dnl System lrslib may already be 7.x, which may be compiled with FLINT SAGE_SPKG_DEPCHECK([gmp mpir flint], [ AC_CHECK_PROGS([LRSNASH], [lrsnash]) - AS_IF([test -z "$LRSNASH"], [sage_spkg_install_lrslib=yes]) + AS_IF([test -z "$LRSNASH"], [ + sage_spkg_install_lrslib=yes + ], [ + AC_MSG_CHECKING([whether $LRSNASH can handle the new input format]) + cat > conftest.lrsnash <& AS_MESSAGE_LOG_FD 2>&1], [ + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + sage_spkg_install_lrslib=yes + ]) + rm -f conftest.lrsnash + ]) ]) ]) From 5f32d2e4654bebedc7f70717aeede35e7c95f7bc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 13 Jul 2021 17:50:33 -0700 Subject: [PATCH 310/336] build/pkgs/lrslib/: Update to 071b+autotools-2021-07-13 --- build/pkgs/lrslib/checksums.ini | 6 +++--- build/pkgs/lrslib/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/lrslib/checksums.ini b/build/pkgs/lrslib/checksums.ini index 36edfab83c9..e296ecf4a74 100644 --- a/build/pkgs/lrslib/checksums.ini +++ b/build/pkgs/lrslib/checksums.ini @@ -1,5 +1,5 @@ tarball=lrslib-VERSION.tar.gz -sha1=7c5156b10cb31e69af8a27f8faff8eb957dc1d3a -md5=54f8c29d402b7a278770ee6f8808f77e -cksum=3493213188 +sha1=4723f2b96e4b59d8366316b84214d6221b7ee7ce +md5=b379d2bdef0f5200c6274d9c50361b7c +cksum=1216904185 upstream_url=https://github.com/mkoeppe/lrslib/releases/download/lrslib-VERSION/lrslib-VERSION.tar.gz diff --git a/build/pkgs/lrslib/package-version.txt b/build/pkgs/lrslib/package-version.txt index 073b7651f1f..d0c8ec5a9a1 100644 --- a/build/pkgs/lrslib/package-version.txt +++ b/build/pkgs/lrslib/package-version.txt @@ -1 +1 @@ -071a+autotools-2021-05-18 +071b+autotools-2021-07-13 From 53c7b65f42044639b59015c87ba00b3645422bf4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 14 Jul 2021 12:02:29 +1000 Subject: [PATCH 311/336] Rewriting doctests to test the new format. --- src/sage/game_theory/parser.py | 235 ++++++++++++--------------------- 1 file changed, 82 insertions(+), 153 deletions(-) diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index fc356d4458a..906d4a770aa 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -1,4 +1,17 @@ - +""" +Parser For gambit And lrs Nash Equilibria +""" + +# **************************************************************************** +# Copyright (C) 2014 James Campbell james.campbell@tanti.org.uk +# 2015 Vincent Knight +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** class Parser(): r""" @@ -11,128 +24,33 @@ class Parser(): def __init__(self, raw_string): """ - Initialise a Parser instance by storing a raw_string + Initialise a Parser instance by storing a ``raw_string`` (currently only used with H representation of a game). - TESTS: - - Simply checking that we have the correct string output - for the H representation (which is the format required - for the ``'lrs'`` algorithm):: + EXAMPLES:: + sage: from sage.cpython.string import bytes_to_str sage: from sage.game_theory.parser import Parser - sage: A = matrix([[1, 2], [3, 2]]) - sage: g = NormalFormGame([A]) - sage: raw_string = g._Hrepresentation(A, -A) - doctest:warning... - DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it - creates the legacy input format. Use NormalFormGame._lrs_nash_format instead - See https://trac.sagemath.org/27745 for details. - sage: P = Parser(raw_string) - sage: print(P.raw_string[0]) - H-representation - linearity 1 5 - begin - 5 4 rational - 0 1 0 0 - 0 0 1 0 - 0 1 3 1 - 0 2 2 1 - -1 1 1 0 - end - - - sage: print(P.raw_string[1]) - H-representation - linearity 1 5 - begin - 5 4 rational - 0 -1 -2 1 - 0 -3 -2 1 - 0 1 0 0 - 0 0 1 0 - -1 1 1 0 - end - - - The specific case of a game with 1 strategy for each player:: - + sage: from subprocess import Popen, PIPE sage: A = matrix([[1]]) sage: B = matrix([[5]]) sage: g = NormalFormGame([A,B]) - sage: raw_string = g._Hrepresentation(A, B) - sage: P = Parser(raw_string) - sage: print(P.raw_string[0]) - H-representation - linearity 1 3 - begin - 3 3 rational - 0 1 0 - 0 -5 1 - -1 1 0 - end - - - sage: print(P.raw_string[1]) - H-representation - linearity 1 3 - begin - 3 3 rational - 0 -1 1 - 0 1 0 - -1 1 0 - end - - - Another test:: + sage: raw_string = g._lrs_nash_format(A, B) + sage: game_name = tmp_filename() + sage: with open(game_name, 'w') as game_file: + ....: _ = game_file.write(game_str) + sage: process = Popen(['lrsnash', game_name], stdout=PIPE, stderr=PIPE) # optional - lrslib + sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # optional - lrslib - sage: from sage.game_theory.parser import Parser - sage: A = matrix([[-7, -5, 5], - ....: [5, 5, 3], - ....: [1, -6, 1]]) - sage: B = matrix([[-9, 7, 9], - ....: [6, -2, -3], - ....: [-4, 6, -10]]) - sage: g = NormalFormGame([A, B]) - sage: raw_string = g._Hrepresentation(A, B) - sage: P = Parser(raw_string) - sage: print(P.raw_string[0]) - H-representation - linearity 1 7 - begin - 7 5 rational - 0 1 0 0 0 - 0 0 1 0 0 - 0 0 0 1 0 - 0 9 -6 4 1 - 0 -7 2 -6 1 - 0 -9 3 10 1 - -1 1 1 1 0 - end - - - sage: print(P.raw_string[1]) - H-representation - linearity 1 7 - begin - 7 5 rational - 0 7 5 -5 1 - 0 -5 -5 -3 1 - 0 -1 6 -1 1 - 0 1 0 0 0 - 0 0 1 0 0 - 0 0 0 1 0 - -1 1 1 1 0 - end - - - This class is also used to parse the output of algorithms from the gambit - python interface using the `format_gambit` function. + sage: Parser(lrs_output).format_lrs() # optional - lrslib + + This class is also used to parse the output of algorithms from + the gambit python interface using :meth:`format_gambit()`. """ self.raw_string = raw_string def format_lrs(self, legacy_format=False): - """ + r""" Parses the output of lrs so as to return vectors corresponding to equilibria. @@ -143,45 +61,35 @@ def format_lrs(self, legacy_format=False): sage: from subprocess import Popen, PIPE sage: A = matrix([[1, 2], [3, 2]]) sage: g = NormalFormGame([A]) - sage: game1_str, game2_str = g._Hrepresentation(A, -A) - doctest:warning... - DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it - creates the legacy input format. Use NormalFormGame._lrs_nash_format instead - See https://trac.sagemath.org/27745 for details. - sage: g1_name = tmp_filename() - sage: g2_name = tmp_filename() - sage: g1_file = open(g1_name, 'w') - sage: g2_file = open(g2_name, 'w') - sage: _ = g1_file.write(game1_str) - sage: g1_file.close() - sage: _ = g2_file.write(game2_str) - sage: g2_file.close() - sage: process = Popen(['lrsnash', g1_name, g2_name], stdout=PIPE, stderr=PIPE) # not tested, optional - lrslib - sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # not tested, optional - lrslib - - The above creates a game, writes the H representation to - temporary files, calls lrs and stores the output in `lrs_output` + sage: game_str = g._lrs_nash_format(A, -A) + sage: game_name = tmp_filename() + sage: with open(game_name, 'w') as game_file: + ....: _ = game_file.write(game_str) + sage: process = Popen(['lrsnash', game_name], stdout=PIPE, stderr=PIPE) # optional - lrslib + sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # optional - lrslib + + The above creates a game, writes the H representations to + temporary files, calls lrs and stores the output in ``lrs_output`` (here slicing to get rid of some system parameters that get returned):: - sage: lrs_output[:20] # not tested, optional - lrslib + sage: lrs_output[:-2] # optional - lrslib [..., - '***** 4 4 rational\n', '2 0 1 2 \n', '1 1/2 1/2 -2 \n', '\n', '2 0 1 2 \n', '1 0 1 -2 \n', '\n', - '\n', '*Number of equilibria found: 2\n', '*Player 1: vertices=3 bases=3 pivots=5\n', - ...] + '*Player 2: vertices=2 bases=1 pivots=6\n', + '\n'] The above is pretty messy, here is the output when we put it through the parser:: - sage: nasheq = Parser(lrs_output).format_lrs(legacy_format=True) # not tested, optional - lrslib - sage: nasheq # not tested, optional - lrslib + sage: nasheq = Parser(lrs_output).format_lrs() # optional - lrslib + sage: nasheq # optional - lrslib [[(1/2, 1/2), (0, 1)], [(0, 1), (0, 1)]] Another game:: @@ -193,22 +101,16 @@ def format_lrs(self, legacy_format=False): ....: [6, -2, -3], ....: [-4, 6, -10]]) sage: g = NormalFormGame([A, B]) - sage: game1_str, game2_str = g._Hrepresentation(A, B) - sage: g1_name = tmp_filename() - sage: g2_name = tmp_filename() - sage: g1_file = open(g1_name, 'w') - sage: g2_file = open(g2_name, 'w') - sage: _ = g1_file.write(game1_str) - sage: g1_file.close() - sage: _ = g2_file.write(game2_str) - sage: g2_file.close() - sage: process = Popen(['lrsnash', g1_name, g2_name], stdout=PIPE, stderr=PIPE) # not tested, optional - lrslib - sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # not tested, optional - lrslib - sage: print(lrs_output[:25]) # not tested, optional - lrslib + sage: game_str = g._lrs_nash_format(A, B) + sage: game_name = tmp_filename() + sage: with open(game_name, 'w') as game_file: + ....: _ = game_file.write(game_str) + sage: process = Popen(['lrsnash', game_name], stdout=PIPE, stderr=PIPE) # optional - lrslib + sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # optional - lrslib + sage: print(lrs_output[:-2]) # optional - lrslib [..., - '***** 5 5 rational\n', - '2 1/7 0 6/7 23/7 \n', '2 0 1/6 5/6 10/3 \n', + '2 1/7 0 6/7 23/7 \n', '1 1/3 2/3 0 1 \n', '\n', '2 0 0 1 5 \n', @@ -217,17 +119,43 @@ def format_lrs(self, legacy_format=False): '2 1 0 0 5 \n', '1 0 1 0 6 \n', '\n', - '\n', '*Number of equilibria found: 4\n', '*Player 1: vertices=6 bases=7 pivots=10\n', - ...] + '*Player 2: vertices=4 bases=2 pivots=14\n', + '\n'] - sage: nasheq = Parser(lrs_output).format_lrs(legacy_format=True) # not tested, optional - lrslib - sage: sorted(nasheq) # not tested, optional - lrslib + sage: nasheq = Parser(lrs_output).format_lrs() # optional - lrslib + sage: sorted(nasheq) # optional - lrslib [[(0, 1, 0), (1, 0, 0)], [(1/3, 2/3, 0), (0, 1/6, 5/6)], [(1/3, 2/3, 0), (1/7, 0, 6/7)], [(1, 0, 0), (0, 0, 1)]] + + TESTS: + + An example with the legacy format:: + + sage: from sage.cpython.string import bytes_to_str + sage: from sage.game_theory.parser import Parser + sage: from subprocess import Popen, PIPE + sage: A = matrix([[1, 2], [3, 2]]) + sage: g = NormalFormGame([A]) + sage: game1_str, game2_str = g._Hrepresentation(A, -A) + doctest:warning... + DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it + creates the legacy input format. Use NormalFormGame._lrs_nash_format instead + See https://trac.sagemath.org/27745 for details. + sage: g1_name, g2_name = tmp_filename(), tmp_filename() + sage: g1_file, g2_file = open(g1_name, 'w'), open(g2_name, 'w') + sage: _ = g1_file.write(game1_str) + sage: g1_file.close() + sage: _ = g2_file.write(game2_str) + sage: g2_file.close() + sage: process = Popen(['lrsnash', g1_name, g2_name], stdout=PIPE, stderr=PIPE) # not tested, optional - lrslib + sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # not tested, optional - lrslib + sage: nasheq = Parser(lrs_output).format_lrs(legacy_format=True) # not tested, optional - lrslib + sage: nasheq # not tested, optional - lrslib + [[(1/2, 1/2), (0, 1)], [(0, 1), (0, 1)]] """ equilibria = [] from sage.misc.sage_eval import sage_eval @@ -362,3 +290,4 @@ def format_gambit(self, gambit_game): nice_stuff.append(profile) return nice_stuff + From 95ce6df5f4c2c064039de61df8fda2c352ff2815 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 14 Jul 2021 12:04:20 +1000 Subject: [PATCH 312/336] Adding the game theory parser to the documentation. --- src/doc/en/reference/game_theory/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst index 3e43301834e..1bee7a0d46d 100644 --- a/src/doc/en/reference/game_theory/index.rst +++ b/src/doc/en/reference/game_theory/index.rst @@ -9,5 +9,6 @@ Game Theory sage/game_theory/normal_form_game sage/game_theory/catalog_normal_form_games sage/game_theory/gambit_docs + sage/game_theory/parser .. include:: ../footer.txt From ff69f289872cc47f28ddc980344d3c34913e1328 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 14 Jul 2021 12:11:07 +1000 Subject: [PATCH 313/336] Some last little fixes. --- src/sage/game_theory/parser.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index 906d4a770aa..95925e85ea0 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -35,14 +35,14 @@ def __init__(self, raw_string): sage: A = matrix([[1]]) sage: B = matrix([[5]]) sage: g = NormalFormGame([A,B]) - sage: raw_string = g._lrs_nash_format(A, B) + sage: game_str = g._lrs_nash_format(A, B) sage: game_name = tmp_filename() sage: with open(game_name, 'w') as game_file: ....: _ = game_file.write(game_str) sage: process = Popen(['lrsnash', game_name], stdout=PIPE, stderr=PIPE) # optional - lrslib sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # optional - lrslib - sage: Parser(lrs_output).format_lrs() # optional - lrslib + [[(1,), (1,)]] This class is also used to parse the output of algorithms from the gambit python interface using :meth:`format_gambit()`. @@ -178,7 +178,7 @@ def format_lrs(self, legacy_format=False): return equilibria def format_gambit(self, gambit_game): - """ + r""" Parses the output of gambit so as to return vectors corresponding to equilibria obtained using the LCP algorithm. From 6834c91fbe9d19b8ec3a13e377a64a85d94db7d4 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 14 Jul 2021 10:43:59 -0500 Subject: [PATCH 314/336] Base __hash__ off of self._group, self.values() --- src/sage/groups/class_function.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/sage/groups/class_function.py b/src/sage/groups/class_function.py index 5a47da10538..9dd6501e64c 100644 --- a/src/sage/groups/class_function.py +++ b/src/sage/groups/class_function.py @@ -84,7 +84,6 @@ class function on the conjugacy classes, in that order. ### ##################################################################### - @richcmp_method class ClassFunction_gap(SageObject): """ @@ -105,16 +104,6 @@ class ClassFunction_gap(SageObject): True """ - def __hash__(self): - r""" - TESTS:: - - sage: G = SymmetricGroup(5) - sage: chi1 = ClassFunction(G,[1,1,1,1,1,1,1]) - sage: d = {chi1:'trivial'} - """ - return hash(self.__repr__()) - def __init__(self, G, values): r""" Return the character of the group ``G`` with values given by the list @@ -151,7 +140,6 @@ def _gap_init_(self): """ return str(self._gap_classfunction) - def _gap_(self, *args): r""" Coerce self into a GAP element. @@ -171,7 +159,6 @@ def _gap_(self, *args): """ return self._gap_classfunction - def __repr__(self): r""" Return a string representation. @@ -189,7 +176,6 @@ def __repr__(self): """ return "Character of %s" % repr(self._group) - def __iter__(self): r""" Iterate through the values of self evaluated on the conjugacy @@ -234,6 +220,16 @@ def __richcmp__(self, other, op): else: return NotImplemented + def __hash__(self): + r""" + TESTS:: + + sage: G = SymmetricGroup(5) + sage: chi1 = ClassFunction(G,[1,1,1,1,1,1,1]) + sage: d = {chi1:'trivial'} + """ + return hash((self._group, tuple(self.values()))) + def __reduce__(self): r""" Add pickle support. From cdd59ab74c88fe95d8e19aab6d0495ab74a9050a Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 14 Jul 2021 18:14:26 -0500 Subject: [PATCH 315/336] Remove .values() --- src/sage/groups/class_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/class_function.py b/src/sage/groups/class_function.py index 9dd6501e64c..60d0318ac97 100644 --- a/src/sage/groups/class_function.py +++ b/src/sage/groups/class_function.py @@ -228,7 +228,7 @@ def __hash__(self): sage: chi1 = ClassFunction(G,[1,1,1,1,1,1,1]) sage: d = {chi1:'trivial'} """ - return hash((self._group, tuple(self.values()))) + return hash((self._group, tuple(self))) def __reduce__(self): r""" From 8720572f98ba5686e4309c436e8f5955d6391dda Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 15 Jul 2021 09:22:02 +0200 Subject: [PATCH 316/336] coverage in base.py --- src/sage/geometry/polyhedron/base.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 47d8890e85f..7ab06696e68 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -781,9 +781,27 @@ def _is_subpolyhedron(self, other): for self_V in self.Vrepresentation()) def is_mutable(self): + r""" + Return True if the polyhedron is mutable, i.e. it can be modified in place. + + EXAMPLES:: + + sage: p = polytopes.cube(backend='field') + sage: p.is_mutable() + False + """ return False def is_immutable(self): + r""" + Return True if the polyhedron is immutable, i.e. it cannot be modified in place. + + EXAMPLES:: + + sage: p = polytopes.cube(backend='field') + sage: p.is_immutable() + True + """ return True @cached_method From 473f729dc9e26b11193d685cd965c09cc55f29c4 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 15 Jul 2021 09:23:21 +0200 Subject: [PATCH 317/336] remove unused imports --- src/sage/geometry/polyhedron/base_mutable.py | 1 - src/sage/geometry/polyhedron/face.py | 1 - 2 files changed, 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base_mutable.py b/src/sage/geometry/polyhedron/base_mutable.py index ee4b269e3e6..d982135f1f5 100644 --- a/src/sage/geometry/polyhedron/base_mutable.py +++ b/src/sage/geometry/polyhedron/base_mutable.py @@ -6,7 +6,6 @@ """ from .base import Polyhedron_base -from sage.misc.lazy_attribute import lazy_attribute class Polyhedron_mutable(Polyhedron_base): diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index ba6ccc5714a..398d18504d5 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -172,7 +172,6 @@ def __hash__(self): 5966674064902575359, 4795242501625591634] """ - from .base import Polyhedron_base return hash((self._polyhedron, self._ambient_Vrepresentation_indices)) def vertex_generator(self): From fce9f984ec65bbe4b5f7121b58dfad4364d99b06 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 15 Jul 2021 09:30:41 +0200 Subject: [PATCH 318/336] check attribute first --- src/sage/geometry/relative_interior.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 6998b770496..cf1d8cd8143 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -52,8 +52,9 @@ def __init__(self, polyhedron): sage: TestSuite(RelativeInterior(P)).run() """ self._polyhedron = polyhedron - if polyhedron.is_mutable(): - polyhedron._add_dependent_object(self) + if hasattr(polyhedron, "is_mutable") and polyhedron.is_mutable(): + if hasattr(polyhedron, "_add_dependent_object"): + polyhedron._add_dependent_object(self) def __contains__(self, point): r""" From 2d4a8050554bf5238122e55ca549cf825cce659f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 16 Jul 2021 21:57:15 -0700 Subject: [PATCH 319/336] build/pkgs/arb/patches: Add https://github.com/fredrik-johansson/arb/commit/b6c8032e2da1b19eb7c5a5f5c2f3372643e3d170 --- ...032e2da1b19eb7c5a5f5c2f3372643e3d170.patch | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 build/pkgs/arb/patches/arb-flint-2.8-compatibility-b6c8032e2da1b19eb7c5a5f5c2f3372643e3d170.patch diff --git a/build/pkgs/arb/patches/arb-flint-2.8-compatibility-b6c8032e2da1b19eb7c5a5f5c2f3372643e3d170.patch b/build/pkgs/arb/patches/arb-flint-2.8-compatibility-b6c8032e2da1b19eb7c5a5f5c2f3372643e3d170.patch new file mode 100644 index 00000000000..5e5e1b0d19f --- /dev/null +++ b/build/pkgs/arb/patches/arb-flint-2.8-compatibility-b6c8032e2da1b19eb7c5a5f5c2f3372643e3d170.patch @@ -0,0 +1,46 @@ +From b6c8032e2da1b19eb7c5a5f5c2f3372643e3d170 Mon Sep 17 00:00:00 2001 +From: fredrik +Date: Mon, 15 Mar 2021 11:56:24 +0100 +Subject: [PATCH] compatibility fix for latest flint + +--- + acb_modular/epsilon_arg.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/acb_modular/epsilon_arg.c b/acb_modular/epsilon_arg.c +index e1332725..b42a64ad 100644 +--- a/acb_modular/epsilon_arg.c ++++ b/acb_modular/epsilon_arg.c +@@ -12,7 +12,7 @@ + #include "acb_modular.h" + + static int +-fmpz_kronecker(const fmpz_t a, const fmpz_t b) ++fmpz_kronecker1(const fmpz_t a, const fmpz_t b) + { + if (fmpz_sgn(b) < 0) + { +@@ -20,7 +20,7 @@ fmpz_kronecker(const fmpz_t a, const fmpz_t b) + fmpz_t t; + fmpz_init(t); + fmpz_neg(t, b); +- r = fmpz_kronecker(a, t); ++ r = fmpz_kronecker1(a, t); + fmpz_clear(t); + return r; + } +@@ -58,12 +58,12 @@ acb_modular_epsilon_arg(const psl2z_t g) + + if (cc % 2 == 1) + { +- u = fmpz_kronecker(a, c); ++ u = fmpz_kronecker1(a, c); + aa = aa*bb + 2*aa*cc - 3*cc + cc*dd*(1-aa*aa); + } + else + { +- u = fmpz_kronecker(c, a); ++ u = fmpz_kronecker1(c, a); + aa = aa*bb - aa*cc + 3*aa - 3 + cc*dd*(1-aa*aa); + } + From 83e309af0cc8555ae7f4053d03ea02a69a1465c6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Jul 2021 11:46:12 -0700 Subject: [PATCH 320/336] build/pkgs/{cvxopt,suitesparse}/type: Change to optional --- build/pkgs/cvxopt/type | 2 +- build/pkgs/suitesparse/type | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/cvxopt/type b/build/pkgs/cvxopt/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/cvxopt/type +++ b/build/pkgs/cvxopt/type @@ -1 +1 @@ -standard +optional diff --git a/build/pkgs/suitesparse/type b/build/pkgs/suitesparse/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/suitesparse/type +++ b/build/pkgs/suitesparse/type @@ -1 +1 @@ -standard +optional From f77a3f8874d84e590d0b187ccce0ae35fb5e2d58 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Jul 2021 12:01:30 -0700 Subject: [PATCH 321/336] src/doc/en/thematic_tutorials/numerical_sage/cvxopt.rst: Mark all tests optional - cvxopt --- .../numerical_sage/cvxopt.rst | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/doc/en/thematic_tutorials/numerical_sage/cvxopt.rst b/src/doc/en/thematic_tutorials/numerical_sage/cvxopt.rst index e5f54ec4c2c..7b365f60ba4 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/cvxopt.rst +++ b/src/doc/en/thematic_tutorials/numerical_sage/cvxopt.rst @@ -28,15 +28,15 @@ by :: - sage: import numpy - sage: from cvxopt.base import spmatrix - sage: from cvxopt.base import matrix as m - sage: from cvxopt import umfpack - sage: Integer=int - sage: V = [2,3, 3,-1,4, 4,-3,1,2, 2, 6,1] - sage: I = [0,1, 0, 2,4, 1, 2,3,4, 2, 1,4] - sage: J = [0,0, 1, 1,1, 2, 2,2,2, 3, 4,4] - sage: A = spmatrix(V,I,J) + sage: import numpy # optional - cvxopt + sage: from cvxopt.base import spmatrix # optional - cvxopt + sage: from cvxopt.base import matrix as m # optional - cvxopt + sage: from cvxopt import umfpack # optional - cvxopt + sage: Integer=int # optional - cvxopt + sage: V = [2,3, 3,-1,4, 4,-3,1,2, 2, 6,1] # optional - cvxopt + sage: I = [0,1, 0, 2,4, 1, 2,3,4, 2, 1,4] # optional - cvxopt + sage: J = [0,0, 1, 1,1, 2, 2,2,2, 3, 4,4] # optional - cvxopt + sage: A = spmatrix(V,I,J) # optional - cvxopt To solve an equation :math:`AX=B`, with :math:`B=[1,1,1,1,1]`, we could do the following. @@ -45,9 +45,9 @@ we could do the following. :: - sage: B = numpy.array([1.0]*5) - sage: B.shape=(5,1) - sage: print(B) + sage: B = numpy.array([1.0]*5) # optional - cvxopt + sage: B.shape=(5,1) # optional - cvxopt + sage: print(B) # optional - cvxopt [[1.] [1.] [1.] @@ -55,15 +55,15 @@ we could do the following. [1.]] - sage: print(A) + sage: print(A) # optional - cvxopt [ 2.00e+00 3.00e+00 0 0 0 ] [ 3.00e+00 0 4.00e+00 0 6.00e+00] [ 0 -1.00e+00 -3.00e+00 2.00e+00 0 ] [ 0 0 1.00e+00 0 0 ] [ 0 4.00e+00 2.00e+00 0 1.00e+00] - sage: C=m(B) - sage: umfpack.linsolve(A,C) - sage: print(C) + sage: C=m(B) # optional - cvxopt + sage: umfpack.linsolve(A,C) # optional - cvxopt + sage: print(C) # optional - cvxopt [ 5.79e-01] [-5.26e-02] [ 1.00e+00] @@ -81,13 +81,13 @@ We could compute the approximate minimum degree ordering by doing :: - sage: RealNumber=float - sage: Integer=int - sage: from cvxopt.base import spmatrix - sage: from cvxopt import amd - sage: A=spmatrix([10,3,5,-2,5,2],[0,2,1,2,2,3],[0,0,1,1,2,3]) - sage: P=amd.order(A) - sage: print(P) + sage: RealNumber=float # optional - cvxopt + sage: Integer=int # optional - cvxopt + sage: from cvxopt.base import spmatrix # optional - cvxopt + sage: from cvxopt import amd # optional - cvxopt + sage: A=spmatrix([10,3,5,-2,5,2],[0,2,1,2,2,3],[0,0,1,1,2,3]) # optional - cvxopt + sage: P=amd.order(A) # optional - cvxopt + sage: print(P) # optional - cvxopt [ 1] [ 0] [ 2] @@ -108,14 +108,14 @@ For a simple linear programming example, if we want to solve :: - sage: RealNumber=float - sage: Integer=int - sage: from cvxopt.base import matrix as m - sage: from cvxopt import solvers - sage: c = m([-4., -5.]) - sage: G = m([[2., 1., -1., 0.], [1., 2., 0., -1.]]) - sage: h = m([3., 3., 0., 0.]) - sage: sol = solvers.lp(c,G,h) #random + sage: RealNumber=float # optional - cvxopt + sage: Integer=int # optional - cvxopt + sage: from cvxopt.base import matrix as m # optional - cvxopt + sage: from cvxopt import solvers # optional - cvxopt + sage: c = m([-4., -5.]) # optional - cvxopt + sage: G = m([[2., 1., -1., 0.], [1., 2., 0., -1.]]) # optional - cvxopt + sage: h = m([3., 3., 0., 0.]) # optional - cvxopt + sage: sol = solvers.lp(c,G,h) # random # optional - cvxopt pcost dcost gap pres dres k/t 0: -8.1000e+00 -1.8300e+01 4e+00 0e+00 8e-01 1e+00 1: -8.8055e+00 -9.4357e+00 2e-01 1e-16 4e-02 3e-02 @@ -127,7 +127,7 @@ For a simple linear programming example, if we want to solve :: - sage: print(sol['x']) # ... below since can get -00 or +00 depending on architecture + sage: print(sol['x']) # optional - cvxopt # ... below since can get -00 or +00 depending on architecture [ 1.00e...00] [ 1.00e+00] From 88c2291489de982d2fb4b9df99b13df048bd7167 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Jul 2021 12:15:06 -0700 Subject: [PATCH 322/336] src/sage/numerical/backends/cvxopt_backend.pyx: Mark all tests # optional - cvxopt --- .../numerical/backends/cvxopt_backend.pyx | 385 +++++++++--------- 1 file changed, 192 insertions(+), 193 deletions(-) diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index 8273744c883..11911062428 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -30,13 +30,13 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: - sage: p = MixedIntegerLinearProgram(solver="CVXOPT") + sage: p = MixedIntegerLinearProgram(solver="CVXOPT") # optional - cvxopt TESTS: :trac:`20332`:: - sage: p + sage: p # optional - cvxopt Mixed Integer Program (no objective, 0 variables, 0 constraints) """ @@ -62,7 +62,7 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt """ @@ -101,15 +101,15 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = MixedIntegerLinearProgram(solver = "CVXOPT") - sage: b = p.new_variable() - sage: p.add_constraint(b[1] + b[2] <= 6) - sage: p.add_constraint(b[2] <= 5) - sage: p.set_objective(b[1] + b[2]) - sage: cp = copy(p.get_backend()) - sage: cp.solve() + sage: p = MixedIntegerLinearProgram(solver = "CVXOPT") # optional - cvxopt + sage: b = p.new_variable() # optional - cvxopt + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - cvxopt + sage: p.add_constraint(b[2] <= 5) # optional - cvxopt + sage: p.set_objective(b[1] + b[2]) # optional - cvxopt + sage: cp = copy(p.get_backend()) # optional - cvxopt + sage: cp.solve() # optional - cvxopt 0 - sage: cp.get_objective_value() + sage: cp.get_objective_value() # optional - cvxopt 6.0 """ cdef CVXOPTBackend cp = type(self)() @@ -160,35 +160,35 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.ncols() # optional - cvxopt 0 - sage: p.add_variable() + sage: p.add_variable() # optional - cvxopt 0 - sage: p.ncols() + sage: p.ncols() # optional - cvxopt 1 - sage: p.add_variable() + sage: p.add_variable() # optional - cvxopt 1 - sage: p.add_variable(lower_bound=-2.0) + sage: p.add_variable(lower_bound=-2.0) # optional - cvxopt 2 - sage: p.add_variable(continuous=True) + sage: p.add_variable(continuous=True) # optional - cvxopt 3 - sage: p.add_variable(name='x',obj=1.0) + sage: p.add_variable(name='x',obj=1.0) # optional - cvxopt 4 - sage: p.col_name(3) + sage: p.col_name(3) # optional - cvxopt 'x_3' - sage: p.col_name(4) + sage: p.col_name(4) # optional - cvxopt 'x' - sage: p.objective_coefficient(4) + sage: p.objective_coefficient(4) # optional - cvxopt 1.00000000000000 TESTS:: - sage: p.add_variable(integer=True) + sage: p.add_variable(integer=True) # optional - cvxopt Traceback (most recent call last): ... RuntimeError: CVXOPT only supports continuous variables - sage: p.add_variable(binary=True) + sage: p.add_variable(binary=True) # optional - cvxopt Traceback (most recent call last): ... RuntimeError: CVXOPT only supports continuous variables @@ -211,11 +211,11 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "cvxopt") - sage: p.add_variables(5) + sage: p = get_solver(solver = "cvxopt") # optional - cvxopt + sage: p.add_variables(5) # optional - cvxopt 4 - sage: p.set_variable_type(3, -1) - sage: p.set_variable_type(3, -2) + sage: p.set_variable_type(3, -1) # optional - cvxopt + sage: p.set_variable_type(3, -2) # optional - cvxopt Traceback (most recent call last): ... ValueError: ... @@ -237,11 +237,11 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.is_maximization() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.is_maximization() # optional - cvxopt True - sage: p.set_sense(-1) - sage: p.is_maximization() + sage: p.set_sense(-1) # optional - cvxopt + sage: p.is_maximization() # optional - cvxopt False """ if sense == 1: @@ -263,13 +263,13 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variable() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variable() # optional - cvxopt 0 - sage: p.objective_coefficient(0) + sage: p.objective_coefficient(0) # optional - cvxopt 0.0 - sage: p.objective_coefficient(0,2) - sage: p.objective_coefficient(0) + sage: p.objective_coefficient(0,2) # optional - cvxopt + sage: p.objective_coefficient(0) # optional - cvxopt 2.0 """ if coeff is not None: @@ -290,12 +290,12 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: - sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(5) + sage: from sage.numerical.backends.generic_backend import get_solver # optional - cvxopt + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variables(5) # optional - cvxopt 4 - sage: p.set_objective([1, 1, 2, 1, 3]) - sage: [p.objective_coefficient(x) for x in range(5)] + sage: p.set_objective([1, 1, 2, 1, 3]) # optional - cvxopt + sage: [p.objective_coefficient(x) for x in range(5)] # optional - cvxopt [1, 1, 2, 1, 3] """ for i in range(len(coeff)): @@ -334,14 +334,14 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.ncols() # optional - cvxopt 0 - sage: p.nrows() + sage: p.nrows() # optional - cvxopt 0 - sage: p.add_linear_constraints(5, 0, None) - sage: p.add_col(range(5), range(5)) - sage: p.nrows() + sage: p.add_linear_constraints(5, 0, None) # optional - cvxopt + sage: p.add_col(range(5), range(5)) # optional - cvxopt + sage: p.nrows() # optional - cvxopt 5 """ column = [] @@ -377,16 +377,16 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(5) + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variables(5) # optional - cvxopt 4 - sage: p.add_linear_constraint(zip(range(5), range(5)), 2.0, 2.0) - sage: p.row(0) + sage: p.add_linear_constraint(zip(range(5), range(5)), 2.0, 2.0) # optional - cvxopt + sage: p.row(0) # optional - cvxopt ([1, 2, 3, 4], [1, 2, 3, 4]) - sage: p.row_bounds(0) + sage: p.row_bounds(0) # optional - cvxopt (2.00000000000000, 2.00000000000000) - sage: p.add_linear_constraint(zip(range(5), range(5)), 1.0, 1.0, name='foo') - sage: p.row_name(-1) + sage: p.add_linear_constraint(zip(range(5), range(5)), 1.0, 1.0, name='foo') # optional - cvxopt + sage: p.row_name(-1) # optional - cvxopt 'foo' """ coefficients = list(coefficients) @@ -414,76 +414,76 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: - sage: p = MixedIntegerLinearProgram(solver = "cvxopt", maximization=False) - sage: x=p.new_variable(nonnegative=True) - sage: p.set_objective(-4*x[0] - 5*x[1]) - sage: p.add_constraint(2*x[0] + x[1] <= 3) - sage: p.add_constraint(2*x[1] + x[0] <= 3) - sage: N(p.solve(), digits=2) + sage: p = MixedIntegerLinearProgram(solver = "cvxopt", maximization=False) # optional - cvxopt + sage: x=p.new_variable(nonnegative=True) # optional - cvxopt + sage: p.set_objective(-4*x[0] - 5*x[1]) # optional - cvxopt + sage: p.add_constraint(2*x[0] + x[1] <= 3) # optional - cvxopt + sage: p.add_constraint(2*x[1] + x[0] <= 3) # optional - cvxopt + sage: N(p.solve(), digits=2) # optional - cvxopt -9.0 - sage: p = MixedIntegerLinearProgram(solver = "cvxopt", maximization=False) - sage: x=p.new_variable(nonnegative=True) - sage: p.set_objective(x[0] + 2*x[1]) - sage: p.add_constraint(-5*x[0] + x[1] <= 7) - sage: p.add_constraint(-5*x[0] + x[1] >= 7) - sage: p.add_constraint(x[0] + x[1] >= 26 ) - sage: p.add_constraint( x[0] >= 3) - sage: p.add_constraint( x[1] >= 4) - sage: N(p.solve(),digits=4) + sage: p = MixedIntegerLinearProgram(solver = "cvxopt", maximization=False) # optional - cvxopt + sage: x=p.new_variable(nonnegative=True) # optional - cvxopt + sage: p.set_objective(x[0] + 2*x[1]) # optional - cvxopt + sage: p.add_constraint(-5*x[0] + x[1] <= 7) # optional - cvxopt + sage: p.add_constraint(-5*x[0] + x[1] >= 7) # optional - cvxopt + sage: p.add_constraint(x[0] + x[1] >= 26 ) # optional - cvxopt + sage: p.add_constraint( x[0] >= 3) # optional - cvxopt + sage: p.add_constraint( x[1] >= 4) # optional - cvxopt + sage: N(p.solve(),digits=4) # optional - cvxopt 48.83 - sage: p = MixedIntegerLinearProgram(solver = "cvxopt") - sage: x=p.new_variable(nonnegative=True) - sage: p.set_objective(x[0] + x[1] + 3*x[2]) - sage: p.solver_parameter("show_progress",True) - sage: p.add_constraint(x[0] + 2*x[1] <= 4) - sage: p.add_constraint(5*x[2] - x[1] <= 8) - sage: N(p.solve(), digits=2) + sage: p = MixedIntegerLinearProgram(solver = "cvxopt") # optional - cvxopt + sage: x=p.new_variable(nonnegative=True) # optional - cvxopt + sage: p.set_objective(x[0] + x[1] + 3*x[2]) # optional - cvxopt + sage: p.solver_parameter("show_progress",True) # optional - cvxopt + sage: p.add_constraint(x[0] + 2*x[1] <= 4) # optional - cvxopt + sage: p.add_constraint(5*x[2] - x[1] <= 8) # optional - cvxopt + sage: N(p.solve(), digits=2) # optional - cvxopt pcost dcost gap pres dres k/t ... 8.8 sage: #CVXOPT gives different values for variables compared to the other solvers. - sage: c = MixedIntegerLinearProgram(solver = "cvxopt") - sage: p = MixedIntegerLinearProgram(solver = "ppl") - sage: g = MixedIntegerLinearProgram() - sage: xc=c.new_variable(nonnegative=True) - sage: xp=p.new_variable(nonnegative=True) - sage: xg=g.new_variable(nonnegative=True) - sage: c.set_objective(xc[2]) - sage: p.set_objective(xp[2]) - sage: g.set_objective(xg[2]) + sage: c = MixedIntegerLinearProgram(solver = "cvxopt") # optional - cvxopt + sage: p = MixedIntegerLinearProgram(solver = "ppl") # optional - cvxopt + sage: g = MixedIntegerLinearProgram() # optional - cvxopt + sage: xc=c.new_variable(nonnegative=True) # optional - cvxopt + sage: xp=p.new_variable(nonnegative=True) # optional - cvxopt + sage: xg=g.new_variable(nonnegative=True) # optional - cvxopt + sage: c.set_objective(xc[2]) # optional - cvxopt + sage: p.set_objective(xp[2]) # optional - cvxopt + sage: g.set_objective(xg[2]) # optional - cvxopt sage: #we create a cube for all three solvers - sage: c.add_constraint(xc[0] <= 100) - sage: c.add_constraint(xc[1] <= 100) - sage: c.add_constraint(xc[2] <= 100) - sage: p.add_constraint(xp[0] <= 100) - sage: p.add_constraint(xp[1] <= 100) - sage: p.add_constraint(xp[2] <= 100) - sage: g.add_constraint(xg[0] <= 100) - sage: g.add_constraint(xg[1] <= 100) - sage: g.add_constraint(xg[2] <= 100) - sage: N(c.solve(),digits=4) + sage: c.add_constraint(xc[0] <= 100) # optional - cvxopt + sage: c.add_constraint(xc[1] <= 100) # optional - cvxopt + sage: c.add_constraint(xc[2] <= 100) # optional - cvxopt + sage: p.add_constraint(xp[0] <= 100) # optional - cvxopt + sage: p.add_constraint(xp[1] <= 100) # optional - cvxopt + sage: p.add_constraint(xp[2] <= 100) # optional - cvxopt + sage: g.add_constraint(xg[0] <= 100) # optional - cvxopt + sage: g.add_constraint(xg[1] <= 100) # optional - cvxopt + sage: g.add_constraint(xg[2] <= 100) # optional - cvxopt + sage: N(c.solve(),digits=4) # optional - cvxopt 100.0 - sage: N(c.get_values(xc[0]),digits=3) + sage: N(c.get_values(xc[0]),digits=3) # optional - cvxopt 50.0 - sage: N(c.get_values(xc[1]),digits=3) + sage: N(c.get_values(xc[1]),digits=3) # optional - cvxopt 50.0 - sage: N(c.get_values(xc[2]),digits=4) + sage: N(c.get_values(xc[2]),digits=4) # optional - cvxopt 100.0 - sage: N(p.solve(),digits=4) + sage: N(p.solve(),digits=4) # optional - cvxopt 100.0 - sage: N(p.get_values(xp[0]),2) + sage: N(p.get_values(xp[0]),2) # optional - cvxopt 0.00 - sage: N(p.get_values(xp[1]),2) + sage: N(p.get_values(xp[1]),2) # optional - cvxopt 0.00 - sage: N(p.get_values(xp[2]),digits=4) + sage: N(p.get_values(xp[2]),digits=4) # optional - cvxopt 100.0 - sage: N(g.solve(),digits=4) + sage: N(g.solve(),digits=4) # optional - cvxopt 100.0 - sage: N(g.get_values(xg[0]),2) + sage: N(g.get_values(xg[0]),2) # optional - cvxopt 0.00 - sage: N(g.get_values(xg[1]),2) + sage: N(g.get_values(xg[1]),2) # optional - cvxopt 0.00 - sage: N(g.get_values(xg[2]),digits=4) + sage: N(g.get_values(xg[2]),digits=4) # optional - cvxopt 100.0 """ from cvxopt import matrix, solvers @@ -567,18 +567,18 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "cvxopt") - sage: p.add_variables(2) + sage: p = get_solver(solver = "cvxopt") # optional - cvxopt + sage: p.add_variables(2) # optional - cvxopt 1 - sage: p.add_linear_constraint([(0,1), (1,2)], None, 3) - sage: p.set_objective([2, 5]) - sage: p.solve() + sage: p.add_linear_constraint([(0,1), (1,2)], None, 3) # optional - cvxopt + sage: p.set_objective([2, 5]) # optional - cvxopt + sage: p.solve() # optional - cvxopt 0 - sage: N(p.get_objective_value(),4) + sage: N(p.get_objective_value(),4) # optional - cvxopt 7.5 - sage: N(p.get_variable_value(0),4) + sage: N(p.get_variable_value(0),4) # optional - cvxopt 3.6e-7 - sage: N(p.get_variable_value(1),4) + sage: N(p.get_variable_value(1),4) # optional - cvxopt 1.5 """ sum = self.obj_constant_term @@ -599,18 +599,18 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(2) + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variables(2) # optional - cvxopt 1 - sage: p.add_linear_constraint([(0,1), (1, 2)], None, 3) - sage: p.set_objective([2, 5]) - sage: p.solve() + sage: p.add_linear_constraint([(0,1), (1, 2)], None, 3) # optional - cvxopt + sage: p.set_objective([2, 5]) # optional - cvxopt + sage: p.solve() # optional - cvxopt 0 - sage: N(p.get_objective_value(),4) + sage: N(p.get_objective_value(),4) # optional - cvxopt 7.5 - sage: N(p.get_variable_value(0),4) + sage: N(p.get_variable_value(0),4) # optional - cvxopt 3.6e-7 - sage: N(p.get_variable_value(1),4) + sage: N(p.get_variable_value(1),4) # optional - cvxopt 1.5 """ return self.answer['x'][variable] @@ -622,12 +622,12 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.ncols() # optional - cvxopt 0 - sage: p.add_variables(2) + sage: p.add_variables(2) # optional - cvxopt 1 - sage: p.ncols() + sage: p.ncols() # optional - cvxopt 2 """ @@ -640,13 +640,13 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.nrows() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.nrows() # optional - cvxopt 0 - sage: p.add_variables(5) + sage: p.add_variables(5) # optional - cvxopt 4 - sage: p.add_linear_constraints(2, 2.0, None) - sage: p.nrows() + sage: p.add_linear_constraints(2, 2.0, None) # optional - cvxopt + sage: p.nrows() # optional - cvxopt 2 """ return len(self.row_upper_bound) @@ -659,11 +659,11 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.is_maximization() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.is_maximization() # optional - cvxopt True - sage: p.set_sense(-1) - sage: p.is_maximization() + sage: p.set_sense(-1) # optional - cvxopt + sage: p.is_maximization() # optional - cvxopt False """ if self.is_maximize == 1: @@ -683,11 +683,11 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.problem_name() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.problem_name() # optional - cvxopt '' - sage: p.problem_name("There once was a french fry") - sage: print(p.problem_name()) + sage: p.problem_name("There once was a french fry") # optional - cvxopt + sage: print(p.problem_name()) # optional - cvxopt There once was a french fry """ if name is None: @@ -713,13 +713,13 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(5) + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variables(5) # optional - cvxopt 4 - sage: p.add_linear_constraint(list(zip(range(5), range(5))), 2, 2) - sage: p.row(0) + sage: p.add_linear_constraint(list(zip(range(5), range(5))), 2, 2) # optional - cvxopt + sage: p.row(0) # optional - cvxopt ([1, 2, 3, 4], [1, 2, 3, 4]) - sage: p.row_bounds(0) + sage: p.row_bounds(0) # optional - cvxopt (2, 2) """ coeff = [] @@ -750,13 +750,13 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variables(5) + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variables(5) # optional - cvxopt 4 - sage: p.add_linear_constraint(list(zip(range(5), range(5))), 2, 2) - sage: p.row(0) + sage: p.add_linear_constraint(list(zip(range(5), range(5))), 2, 2) # optional - cvxopt + sage: p.row(0) # optional - cvxopt ([1, 2, 3, 4], [1, 2, 3, 4]) - sage: p.row_bounds(0) + sage: p.row_bounds(0) # optional - cvxopt (2, 2) """ return (self.row_lower_bound[index], self.row_upper_bound[index]) @@ -778,13 +778,13 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variable() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variable() # optional - cvxopt 0 - sage: p.col_bounds(0) + sage: p.col_bounds(0) # optional - cvxopt (0.0, None) - sage: p.variable_upper_bound(0, 5) - sage: p.col_bounds(0) + sage: p.variable_upper_bound(0, 5) # optional - cvxopt + sage: p.col_bounds(0) # optional - cvxopt (0.0, 5) """ return (self.col_lower_bound[index], self.col_upper_bound[index]) @@ -801,16 +801,16 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.ncols() # optional - cvxopt 0 - sage: p.add_variable() + sage: p.add_variable() # optional - cvxopt 0 - sage: p.set_variable_type(0,0) + sage: p.set_variable_type(0,0) # optional - cvxopt Traceback (most recent call last): ... ValueError: ... - sage: p.is_variable_binary(0) + sage: p.is_variable_binary(0) # optional - cvxopt False """ @@ -828,17 +828,17 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.ncols() # optional - cvxopt 0 - sage: p.add_variable() + sage: p.add_variable() # optional - cvxopt 0 - sage: p.set_variable_type(0,-1) - sage: p.set_variable_type(0,1) + sage: p.set_variable_type(0,-1) # optional - cvxopt + sage: p.set_variable_type(0,1) # optional - cvxopt Traceback (most recent call last): ... ValueError: ... - sage: p.is_variable_integer(0) + sage: p.is_variable_integer(0) # optional - cvxopt False """ return False @@ -855,18 +855,18 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.ncols() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.ncols() # optional - cvxopt 0 - sage: p.add_variable() + sage: p.add_variable() # optional - cvxopt 0 - sage: p.is_variable_continuous(0) + sage: p.is_variable_continuous(0) # optional - cvxopt True - sage: p.set_variable_type(0,1) + sage: p.set_variable_type(0,1) # optional - cvxopt Traceback (most recent call last): ... ValueError: ... - sage: p.is_variable_continuous(0) + sage: p.is_variable_continuous(0) # optional - cvxopt True """ @@ -883,9 +883,9 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_linear_constraints(1, 2, None, names=["Empty constraint 1"]) - sage: p.row_name(0) + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_linear_constraints(1, 2, None, names=["Empty constraint 1"]) # optional - cvxopt + sage: p.row_name(0) # optional - cvxopt 'Empty constraint 1' """ if self.row_name_var[index] is not None: @@ -906,10 +906,10 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variable(name="I am a variable") + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variable(name="I am a variable") # optional - cvxopt 0 - sage: p.col_name(0) + sage: p.col_name(0) # optional - cvxopt 'I am a variable' """ if self.col_name_var[index] is not None: @@ -931,13 +931,13 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variable() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variable() # optional - cvxopt 0 - sage: p.col_bounds(0) + sage: p.col_bounds(0) # optional - cvxopt (0.0, None) - sage: p.variable_upper_bound(0, 5) - sage: p.col_bounds(0) + sage: p.variable_upper_bound(0, 5) # optional - cvxopt + sage: p.col_bounds(0) # optional - cvxopt (0.0, 5) """ if value is not False: @@ -960,14 +960,13 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - - sage: p = get_solver(solver = "CVXOPT") - sage: p.add_variable() + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.add_variable() # optional - cvxopt 0 - sage: p.col_bounds(0) + sage: p.col_bounds(0) # optional - cvxopt (0.0, None) - sage: p.variable_lower_bound(0, 5) - sage: p.col_bounds(0) + sage: p.variable_lower_bound(0, 5) # optional - cvxopt + sage: p.col_bounds(0) # optional - cvxopt (5, None) """ if value is not False: @@ -994,11 +993,11 @@ cdef class CVXOPTBackend(GenericBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.solver_parameter("show_progress") + sage: p = get_solver(solver="CVXOPT") # optional - cvxopt + sage: p.solver_parameter("show_progress") # optional - cvxopt False - sage: p.solver_parameter("show_progress", True) - sage: p.solver_parameter("show_progress") + sage: p.solver_parameter("show_progress", True) # optional - cvxopt + sage: p.solver_parameter("show_progress") # optional - cvxopt True """ if value is None: From c3bf30c615ad11c8ebdc7c4f98b1d5e1573daaa8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Jul 2021 12:22:38 -0700 Subject: [PATCH 323/336] src/sage/numerical/backends/cvxopt_sdp_backend.pyx: Mark all tests # optional - cvxopt --- .../numerical/backends/cvxopt_sdp_backend.pyx | 130 +++++++++--------- 1 file changed, 64 insertions(+), 66 deletions(-) diff --git a/src/sage/numerical/backends/cvxopt_sdp_backend.pyx b/src/sage/numerical/backends/cvxopt_sdp_backend.pyx index 3d76b76c1b8..ced0da89b05 100644 --- a/src/sage/numerical/backends/cvxopt_sdp_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_sdp_backend.pyx @@ -36,7 +36,7 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt """ @@ -68,9 +68,9 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): EXAMPLES:: - sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) - sage: x = p.new_variable() - sage: p.set_objective(x[0] - x[1] + x[2]) + sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) # optional - cvxopt + sage: x = p.new_variable() # optional - cvxopt + sage: p.set_objective(x[0] - x[1] + x[2]) # optional - cvxopt sage: a1 = matrix([[-7., -11.], [-11., 3.]]) sage: a2 = matrix([[7., -18.], [-18., 8.]]) sage: a3 = matrix([[-2., -8.], [-8., 1.]]) @@ -79,13 +79,13 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): sage: b2 = matrix([[0., 10., 16.], [10., -10., -10.], [16., -10., 3.]]) sage: b3 = matrix([[-5., 2., -17.], [2., -6., 8.], [-17., 8., 6.]]) sage: b4 = matrix([[14., 9., 40.], [9., 91., 10.], [40., 10., 15.]]) - sage: p.add_constraint(a1*x[0] + a3*x[2] <= a4) - sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) - sage: N(p.solve(), digits=4) + sage: p.add_constraint(a1*x[0] + a3*x[2] <= a4) # optional - cvxopt + sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) # optional - cvxopt + sage: N(p.solve(), digits=4) # optional - cvxopt -3.225 - sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) - sage: x = p.new_variable() - sage: p.set_objective(x[0] - x[1] + x[2]) + sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) # optional - cvxopt + sage: x = p.new_variable() # optional - cvxopt + sage: p.set_objective(x[0] - x[1] + x[2]) # optional - cvxopt sage: a1 = matrix([[-7., -11.], [-11., 3.]]) sage: a2 = matrix([[7., -18.], [-18., 8.]]) sage: a3 = matrix([[-2., -8.], [-8., 1.]]) @@ -94,9 +94,9 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): sage: b2 = matrix([[0., 10., 16.], [10., -10., -10.], [16., -10., 3.]]) sage: b3 = matrix([[-5., 2., -17.], [2., -6., 8.], [-17., 8., 6.]]) sage: b4 = matrix([[14., 9., 40.], [9., 91., 10.], [40., 10., 15.]]) - sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) - sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) - sage: N(p.solve(), digits=4) + sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) # optional - cvxopt + sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) # optional - cvxopt + sage: N(p.solve(), digits=4) # optional - cvxopt -3.154 """ @@ -171,9 +171,9 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): EXAMPLES:: - sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) - sage: x = p.new_variable() - sage: p.set_objective(x[0] - x[1] + x[2]) + sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) # optional - cvxopt + sage: x = p.new_variable() # optional - cvxopt + sage: p.set_objective(x[0] - x[1] + x[2]) # optional - cvxopt sage: a1 = matrix([[-7., -11.], [-11., 3.]]) sage: a2 = matrix([[7., -18.], [-18., 8.]]) sage: a3 = matrix([[-2., -8.], [-8., 1.]]) @@ -182,11 +182,11 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): sage: b2 = matrix([[0., 10., 16.], [10., -10., -10.], [16., -10., 3.]]) sage: b3 = matrix([[-5., 2., -17.], [2., -6., 8.], [-17., 8., 6.]]) sage: b4 = matrix([[14., 9., 40.], [9., 91., 10.], [40., 10., 15.]]) - sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) - sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) - sage: N(p.solve(), digits=4) + sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) # optional - cvxopt + sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) # optional - cvxopt + sage: N(p.solve(), digits=4) # optional - cvxopt -3.154 - sage: N(p.get_backend().get_objective_value(), digits=4) + sage: N(p.get_backend().get_objective_value(), digits=4) # optional - cvxopt -3.154 """ sum = self.obj_constant_term @@ -204,20 +204,20 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): TESTS:: - sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') - sage: x = p.new_variable() - sage: p.set_objective(x[0] - x[1]) + sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') # optional - cvxopt + sage: x = p.new_variable() # optional - cvxopt + sage: p.set_objective(x[0] - x[1]) # optional - cvxopt sage: a1 = matrix([[1, 2.], [2., 3.]]) sage: a2 = matrix([[3, 4.], [4., 5.]]) sage: a3 = matrix([[5, 6.], [6., 7.]]) sage: b1 = matrix([[1, 1.], [1., 1.]]) sage: b2 = matrix([[2, 2.], [2., 2.]]) sage: b3 = matrix([[3, 3.], [3., 3.]]) - sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) - sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) - sage: p.solve(); # tol 1e-08 + sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) # optional - cvxopt + sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) # optional - cvxopt + sage: p.solve(); # tol 1e-08 # optional - cvxopt -3.0 - sage: p.get_backend()._get_answer() + sage: p.get_backend()._get_answer() # optional - cvxopt {...} """ return self.answer @@ -232,11 +232,9 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): EXAMPLES:: - sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "cvxopt") - sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) - sage: x = p.new_variable() - sage: p.set_objective(x[0] - x[1] + x[2]) + sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) # optional - cvxopt + sage: x = p.new_variable() # optional - cvxopt + sage: p.set_objective(x[0] - x[1] + x[2]) # optional - cvxopt sage: a1 = matrix([[-7., -11.], [-11., 3.]]) sage: a2 = matrix([[7., -18.], [-18., 8.]]) sage: a3 = matrix([[-2., -8.], [-8., 1.]]) @@ -245,15 +243,15 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): sage: b2 = matrix([[0., 10., 16.], [10., -10., -10.], [16., -10., 3.]]) sage: b3 = matrix([[-5., 2., -17.], [2., -6., 8.], [-17., 8., 6.]]) sage: b4 = matrix([[14., 9., 40.], [9., 91., 10.], [40., 10., 15.]]) - sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) - sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) - sage: N(p.solve(), digits=4) + sage: p.add_constraint(a1*x[0] + a2*x[1] + a3*x[2] <= a4) # optional - cvxopt + sage: p.add_constraint(b1*x[0] + b2*x[1] + b3*x[2] <= b4) # optional - cvxopt + sage: N(p.solve(), digits=4) # optional - cvxopt -3.154 - sage: N(p.get_backend().get_variable_value(0), digits=3) + sage: N(p.get_backend().get_variable_value(0), digits=3) # optional - cvxopt -0.368 - sage: N(p.get_backend().get_variable_value(1), digits=4) + sage: N(p.get_backend().get_variable_value(1), digits=4) # optional - cvxopt 1.898 - sage: N(p.get_backend().get_variable_value(2), digits=3) + sage: N(p.get_backend().get_variable_value(2), digits=3) # optional - cvxopt -0.888 """ return self.answer['x'][variable] @@ -272,34 +270,34 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): EXAMPLES:: - sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') - sage: x = p.new_variable() - sage: p.set_objective(x[0] - x[1]) + sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') # optional - cvxopt + sage: x = p.new_variable() # optional - cvxopt + sage: p.set_objective(x[0] - x[1]) # optional - cvxopt sage: a1 = matrix([[1, 2.], [2., 3.]]) sage: a2 = matrix([[3, 4.], [4., 5.]]) sage: a3 = matrix([[5, 6.], [6., 7.]]) sage: b1 = matrix([[1, 1.], [1., 1.]]) sage: b2 = matrix([[2, 2.], [2., 2.]]) sage: b3 = matrix([[3, 3.], [3., 3.]]) - sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) - sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) - sage: p.solve() # tol 1e-08 + sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) # optional - cvxopt + sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) # optional - cvxopt + sage: p.solve() # tol 1e-08 # optional - cvxopt -3.0 - sage: B=p.get_backend() - sage: x=p.get_values(x).values() - sage: -(a3*B.dual_variable(0)).trace()-(b3*B.dual_variable(1)).trace() # tol 1e-07 + sage: B=p.get_backend() # optional - cvxopt + sage: x=p.get_values(x).values() # optional - cvxopt + sage: -(a3*B.dual_variable(0)).trace()-(b3*B.dual_variable(1)).trace() # tol 1e-07 # optional - cvxopt -3.0 - sage: g = sum((B.slack(j)*B.dual_variable(j)).trace() for j in range(2)); g # tol 1.5e-08 + sage: g = sum((B.slack(j)*B.dual_variable(j)).trace() for j in range(2)); g # tol 1.5e-08 # optional - cvxopt 0.0 TESTS:: - sage: B.dual_variable(7) + sage: B.dual_variable(7) # optional - cvxopt Traceback (most recent call last): ... IndexError: list index out of range - sage: abs(g - B._get_answer()['gap']) # tol 1e-22 + sage: abs(g - B._get_answer()['gap']) # tol 1e-22 # optional - cvxopt 0.0 """ @@ -322,33 +320,33 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): EXAMPLES:: - sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') - sage: x = p.new_variable() - sage: p.set_objective(x[0] - x[1]) + sage: p = SemidefiniteProgram(maximization = False, solver='cvxopt') # optional - cvxopt + sage: x = p.new_variable() # optional - cvxopt + sage: p.set_objective(x[0] - x[1]) # optional - cvxopt sage: a1 = matrix([[1, 2.], [2., 3.]]) sage: a2 = matrix([[3, 4.], [4., 5.]]) sage: a3 = matrix([[5, 6.], [6., 7.]]) sage: b1 = matrix([[1, 1.], [1., 1.]]) sage: b2 = matrix([[2, 2.], [2., 2.]]) sage: b3 = matrix([[3, 3.], [3., 3.]]) - sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) - sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) - sage: p.solve() # tol 1e-08 + sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) # optional - cvxopt + sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) # optional - cvxopt + sage: p.solve() # tol 1e-08 # optional - cvxopt -3.0 - sage: B = p.get_backend() - sage: B1 = B.slack(1); B1 # tol 1e-08 + sage: B = p.get_backend() # optional - cvxopt + sage: B1 = B.slack(1); B1 # tol 1e-08 # optional - cvxopt [0.0 0.0] [0.0 0.0] - sage: B1.is_positive_definite() + sage: B1.is_positive_definite() # optional - cvxopt True - sage: x = sorted(p.get_values(x).values()) - sage: x[0]*b1 + x[1]*b2 - b3 + B1 # tol 1e-09 + sage: x = sorted(p.get_values(x).values()) # optional - cvxopt + sage: x[0]*b1 + x[1]*b2 - b3 + B1 # tol 1e-09 # optional - cvxopt [0.0 0.0] [0.0 0.0] TESTS:: - sage: B.slack(7) + sage: B.slack(7) # optional - cvxopt Traceback (most recent call last): ... IndexError: list index out of range @@ -379,11 +377,11 @@ cdef class CVXOPTSDPBackend(MatrixSDPBackend): EXAMPLES:: sage: from sage.numerical.backends.generic_sdp_backend import get_solver - sage: p = get_solver(solver = "CVXOPT") - sage: p.solver_parameter("show_progress") + sage: p = get_solver(solver = "CVXOPT") # optional - cvxopt + sage: p.solver_parameter("show_progress") # optional - cvxopt False - sage: p.solver_parameter("show_progress", True) - sage: p.solver_parameter("show_progress") + sage: p.solver_parameter("show_progress", True) # optional - cvxopt + sage: p.solver_parameter("show_progress") # optional - cvxopt True """ if value is None: From 198a681dbdf96ac61128555f9230af4182dfa776 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Jul 2021 13:07:05 -0700 Subject: [PATCH 324/336] src/sage/numerical/optimize.py: Deprecate linear_program --- src/sage/numerical/optimize.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index f09634c3bf2..7ac1829f3dc 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -11,6 +11,7 @@ ---------------------- """ +from sage.misc.superseded import deprecation from sage.modules.free_module_element import vector from sage.rings.real_double import RDF @@ -553,6 +554,9 @@ def linear_program(c, G, h, A=None, b=None, solver=None): - Maximize `-h'z - b'y` subject to `G'z + A'y + c = 0` and `z \geq 0`. + This function is deprecated. Use :class:`MixedIntegerLinearProgram` instead. + + This function depends on the optional package ``cvxopt``. INPUT: @@ -594,17 +598,20 @@ def linear_program(c, G, h, A=None, b=None, solver=None): sage: c=vector(RDF,[-4,-5]) sage: G=matrix(RDF,[[2,1],[1,2],[-1,0],[0,-1]]) sage: h=vector(RDF,[3,3,0,0]) - sage: sol=linear_program(c,G,h) - sage: sol['x'] + sage: sol=linear_program(c,G,h) # optional - cvxopt + doctest:warning... + DeprecationWarning: linear_program is deprecated; use MixedIntegerLinearProgram instead + See https://trac.sagemath.org/32226 for details. + sage: sol['x'] # optional - cvxopt (0.999..., 1.000...) Here we solve the same problem with 'glpk' interface to 'cvxopt':: - sage: sol=linear_program(c,G,h,solver='glpk') + sage: sol=linear_program(c,G,h,solver='glpk') # optional - cvxopt GLPK Simplex Optimizer... ... OPTIMAL LP SOLUTION FOUND - sage: sol['x'] + sage: sol['x'] # optional - cvxopt (1.0, 1.0) Next, we maximize `x+y-50` subject to `50x + 24y \leq 2400`, @@ -613,15 +620,17 @@ def linear_program(c, G, h, A=None, b=None, solver=None): sage: v=vector([-1.0,-1.0,-1.0]) sage: m=matrix([[50.0,24.0,0.0],[30.0,33.0,0.0],[-1.0,0.0,0.0],[0.0,-1.0,0.0],[0.0,0.0,1.0],[0.0,0.0,-1.0]]) sage: h=vector([2400.0,2100.0,-45.0,-5.0,1.0,-1.0]) - sage: sol=linear_program(v,m,h) - sage: sol['x'] + sage: sol=linear_program(v,m,h) # optional - cvxopt + sage: sol['x'] # optional - cvxopt (45.000000..., 6.2499999..., 1.00000000...) - sage: sol=linear_program(v,m,h,solver='glpk') + sage: sol=linear_program(v,m,h,solver='glpk') # optional - cvxopt GLPK Simplex Optimizer... OPTIMAL LP SOLUTION FOUND - sage: sol['x'] + sage: sol['x'] # optional - cvxopt (45.0..., 6.25..., 1.0...) """ + deprecation(32226, 'linear_program is deprecated; use MixedIntegerLinearProgram instead') + from cvxopt.base import matrix as m from cvxopt import solvers solvers.options['show_progress']=False From 9955c1f5c423fba9b076d1fd5565b7a4fbae53b0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Jul 2021 13:08:07 -0700 Subject: [PATCH 325/336] src/sage/numerical/sdp.pyx, src/sage/numerical/backends/generic_sdp_backend.pyx: Use MatrixSDPBackend as fall back, mark tests using 'solve' # optional - cvxopt --- .../backends/generic_sdp_backend.pyx | 15 ++- src/sage/numerical/sdp.pyx | 114 +++++++++--------- 2 files changed, 67 insertions(+), 62 deletions(-) diff --git a/src/sage/numerical/backends/generic_sdp_backend.pyx b/src/sage/numerical/backends/generic_sdp_backend.pyx index 1ac2b02edeb..1ad7bddc186 100644 --- a/src/sage/numerical/backends/generic_sdp_backend.pyx +++ b/src/sage/numerical/backends/generic_sdp_backend.pyx @@ -663,6 +663,10 @@ def default_sdp_solver(solver=None): except ValueError: pass + from warnings import warn + warn("default_sdp_solver set to 'Matrix' (MatrixSDPBackend), which can construct but not solve problems. Install cvxopt for actual solver functionality") + default_sdp_solver("Matrix") + if callable(solver): default_solver = solver return @@ -675,9 +679,12 @@ def default_sdp_solver(solver=None): default_solver = solver except ImportError: raise ValueError("CVXOPT is not available. Please refer to the documentation to install it.") + elif solver == "Matrix": + default_solver = solver else: - raise ValueError("'solver' should be set to 'CVXOPT', a class, or None.") + raise ValueError("'solver' should be set to 'CVXOPT', 'Matrix', a class, or None.") + cpdef GenericSDPBackend get_solver(solver=None, base_ring=None): """ @@ -729,6 +736,8 @@ cpdef GenericSDPBackend get_solver(solver=None, base_ring=None): if solver == "Cvxopt": from sage.numerical.backends.cvxopt_sdp_backend import CVXOPTSDPBackend return CVXOPTSDPBackend(base_ring=base_ring) - + elif solver == "Matrix": + from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend + return MatrixSDPBackend(base_ring=base_ring) else: - raise ValueError("'solver' should be set to 'CVXOPT', a class, or None (in which case the default one is used).") + raise ValueError("'solver' should be set to 'CVXOPT', 'Matrix', a class, or None (in which case the default one is used).") diff --git a/src/sage/numerical/sdp.pyx b/src/sage/numerical/sdp.pyx index e7c67a34a13..452a058aa6e 100644 --- a/src/sage/numerical/sdp.pyx +++ b/src/sage/numerical/sdp.pyx @@ -1,5 +1,5 @@ r""" -SemiDefinite Programming +Semidefinite Programming A semidefinite program (:wikipedia:`SDP `) is an optimization problem (:wikipedia:`Optimization_(mathematics)>`) @@ -72,17 +72,17 @@ The following example shows all these steps:: sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) sage: p.add_constraint(c1*x[0] + c2*x[1] >= matrix.zero(2,2,sparse=True)) - sage: p.solver_parameter("show_progress", True) - sage: opt = p.solve() + sage: p.solver_parameter("show_progress", True) # optional - cvxopt + sage: opt = p.solve() # optional - cvxopt pcost dcost gap pres dres k/t 0: ... ... Optimal solution found. - sage: print('Objective Value: {}'.format(N(opt,3))) + sage: print('Objective Value: {}'.format(N(opt,3))) # optional - cvxopt Objective Value: 1.0 - sage: [N(x, 3) for x in sorted(p.get_values(x).values())] + sage: [N(x, 3) for x in sorted(p.get_values(x).values())] # optional - cvxopt [3.0e-8, 1.0] - sage: p.show() + sage: p.show() # optional - cvxopt Maximization: x_0 - x_1 Constraints: @@ -97,20 +97,20 @@ of primal and dual problems. Thus we can get the optimizer `X` of the dual probl as follows, as diagonal blocks, one per each constraint, via :meth:`~SemidefiniteProgram.dual_variable`. E.g.:: - sage: p.dual_variable(1) # rel tol 2e-03 + sage: p.dual_variable(1) # rel tol 2e-03 # optional - cvxopt [ 85555.0 -85555.0] [-85555.0 85555.0] We can see that the optimal value of the dual is equal (up to numerical noise) to `opt`.:: - sage: opt-((p.dual_variable(0)*a3).trace()+(p.dual_variable(1)*b3).trace()) # tol 8e-08 + sage: opt-((p.dual_variable(0)*a3).trace()+(p.dual_variable(1)*b3).trace()) # tol 8e-08 # optional - cvxopt 0.0 Dual variable blocks at optimality are orthogonal to "slack variables", that is, matrices `C-\sum_k x_k A_k`, cf. (Primal problem) above, available via :meth:`~SemidefiniteProgram.slack`. E.g.:: - sage: (p.slack(0)*p.dual_variable(0)).trace() # tol 2e-07 + sage: (p.slack(0)*p.dual_variable(0)).trace() # tol 2e-07 # optional - cvxopt 0.0 @@ -122,21 +122,21 @@ More interesting example, the :func:`Lovasz theta =SemidefiniteProgram() sage: p.add_constraint((1/7)*matrix.identity(7)>=-y[0]*c2-y[1]*c3) sage: p.set_objective(y[0]*(c2**2).trace()+y[1]*(c3**2).trace()) - sage: x=p.solve(); x+1 + sage: x=p.solve(); x+1 # optional - cvxopt 3.31766... Unlike in the previous example, the slack variable is very far from 0:: - sage: p.slack(0).trace() # tol 1e-14 + sage: p.slack(0).trace() # tol 1e-14 # optional - cvxopt 1.0 The default CVXOPT backend computes with the Real Double Field, for example:: - sage: p = SemidefiniteProgram(solver='cvxopt') - sage: p.base_ring() + sage: p = SemidefiniteProgram(solver='cvxopt') # optional - cvxopt + sage: p.base_ring() # optional - cvxopt Real Double Field - sage: x = p.new_variable() - sage: 0.5 + 3/2*x[1] + sage: x = p.new_variable() # optional - cvxopt + sage: 0.5 + 3/2*x[1] # optional - cvxopt 0.5 + 1.5*x_0 For representing an SDP with exact data, there is another backend:: @@ -275,7 +275,7 @@ cdef class SemidefiniteProgram(SageObject): Computation of a basic Semidefinite Program:: - sage: p = SemidefiniteProgram(solver = "cvxopt", maximization=False) + sage: p = SemidefiniteProgram(maximization=False) sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1]) sage: a1 = matrix([[1, 2.], [2., 3.]]) @@ -286,7 +286,7 @@ cdef class SemidefiniteProgram(SageObject): sage: b3 = matrix([[3, 3.], [3., 3.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) - sage: N(p.solve(), 2) + sage: N(p.solve(), 2) # optional - cvxopt -3.0 """ @@ -726,25 +726,21 @@ cdef class SemidefiniteProgram(SageObject): sage: b3 = matrix([[3, 3.], [3., 3.]]) sage: p.add_constraint(a1*x[3] + a2*x[5] <= a3) sage: p.add_constraint(b1*x[3] + b2*x[5] <= b3) - sage: N(p.solve(),3) + sage: N(p.solve(),3) # optional - cvxopt -3.0 To return the optimal value of ``x[3]``:: - sage: N(p.get_values(x[3]),3) + sage: N(p.get_values(x[3]),3) # optional - cvxopt -1.0 To get a dictionary identical to ``x`` containing optimal values for the corresponding variables :: - sage: x_sol = p.get_values(x) - sage: sorted(x_sol) + sage: x_sol = p.get_values(x) # optional - cvxopt + sage: sorted(x_sol) # optional - cvxopt [3, 5] - Obviously, it also works with variables of higher dimension:: - - sage: x_sol = p.get_values(x) - """ val = [] for l in lists: @@ -801,10 +797,10 @@ cdef class SemidefiniteProgram(SageObject): sage: a2 = matrix([[1,1],[1,1]]) sage: a3 = matrix([[1,-1],[-1,1]]) sage: p.add_constraint(a1*x[1]+a2*x[2] <= a3) - sage: N(p.solve(),digits=3) + sage: N(p.solve(),digits=3) # optional - cvxopt 16.2 sage: p.set_objective(None) - sage: _ = p.solve() + sage: _ = p.solve() # optional - cvxopt """ cdef list values = [] @@ -860,7 +856,7 @@ cdef class SemidefiniteProgram(SageObject): sage: a2 = matrix([[1,1],[1,1]]) sage: a3 = matrix([[1,-1],[-1,1]]) sage: p.add_constraint(a1*x[1]+a2*x[2] <= a3) - sage: N(p.solve(),digits=3) + sage: N(p.solve(),digits=3) # optional - cvxopt 16.2 One can also define double-bounds or equality using the symbol @@ -873,14 +869,14 @@ cdef class SemidefiniteProgram(SageObject): sage: a2 = matrix([[1,1],[1,1]]) sage: a3 = matrix([[1,-1],[-1,1]]) sage: p.add_constraint(a3 >= a1*x[1] + a2*x[2]) - sage: N(p.solve(),digits=3) + sage: N(p.solve(),digits=3) # optional - cvxopt 16.2 TESTS: Complex constraints:: - sage: p = SemidefiniteProgram(solver = "cvxopt") + sage: p = SemidefiniteProgram() sage: b = p.new_variable() sage: a1 = matrix([[1,2],[2,3]]) sage: a2 = matrix([[1,-2],[-2,4]]) @@ -895,7 +891,7 @@ cdef class SemidefiniteProgram(SageObject): Empty constraint:: - sage: p=SemidefiniteProgram() + sage: p = SemidefiniteProgram() sage: p.add_constraint(sum([])) @@ -942,7 +938,7 @@ cdef class SemidefiniteProgram(SageObject): The SDP from the header of this module:: - sage: p = SemidefiniteProgram(solver = "cvxopt", maximization = False) + sage: p = SemidefiniteProgram(maximization=False) sage: x = p.new_variable() sage: p.set_objective(x[0] - x[1]) sage: a1 = matrix([[1, 2.], [2., 3.]]) @@ -953,12 +949,12 @@ cdef class SemidefiniteProgram(SageObject): sage: b3 = matrix([[3, 3.], [3., 3.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) - sage: N(p.solve(),4) + sage: N(p.solve(),4) # optional - cvxopt -11. - sage: x = p.get_values(x) - sage: N(x[0],4) + sage: x = p.get_values(x) # optional - cvxopt + sage: N(x[0],4) # optional - cvxopt -8.0 - sage: N(x[1],4) + sage: N(x[1],4) # optional - cvxopt 3.0 """ self._backend.solve() @@ -994,20 +990,20 @@ cdef class SemidefiniteProgram(SageObject): sage: b3 = matrix([[3, 3.], [3., 3.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) - sage: p.solve() # tol 1e-08 + sage: p.solve() # tol 1e-08 # optional - cvxopt -3.0 - sage: x = p.get_values(x).values() - sage: -(a3*p.dual_variable(0)).trace()-(b3*p.dual_variable(1)).trace() # tol 1e-07 + sage: x = p.get_values(x).values() # optional - cvxopt + sage: -(a3*p.dual_variable(0)).trace()-(b3*p.dual_variable(1)).trace() # tol 1e-07 # optional - cvxopt -3.0 Dual variable is orthogonal to the slack :: - sage: g = sum((p.slack(j)*p.dual_variable(j)).trace() for j in range(2)); g # tol 1.2e-08 + sage: g = sum((p.slack(j)*p.dual_variable(j)).trace() for j in range(2)); g # tol 1.2e-08 # optional - cvxopt 0.0 TESTS:: - sage: p.dual_variable(7) + sage: p.dual_variable(7) # optional - cvxopt Traceback (most recent call last): ... IndexError: list index out of range @@ -1041,21 +1037,21 @@ cdef class SemidefiniteProgram(SageObject): sage: b3 = matrix([[3, 3.], [3., 3.]]) sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) - sage: p.solve() # tol 1e-08 + sage: p.solve() # tol 1e-08 # optional - cvxopt -3.0 - sage: B1 = p.slack(1); B1 # tol 1e-08 + sage: B1 = p.slack(1); B1 # tol 1e-08 # optional - cvxopt [0.0 0.0] [0.0 0.0] - sage: B1.is_positive_definite() + sage: B1.is_positive_definite() # optional - cvxopt True - sage: x = sorted(p.get_values(x).values()) - sage: x[0]*b1 + x[1]*b2 - b3 + B1 # tol 1e-09 + sage: x = sorted(p.get_values(x).values()) # optional - cvxopt + sage: x[0]*b1 + x[1]*b2 - b3 + B1 # tol 1e-09 # optional - cvxopt [0.0 0.0] [0.0 0.0] TESTS:: - sage: p.slack(7) + sage: p.slack(7) # optional - cvxopt Traceback (most recent call last): ... IndexError: list index out of range @@ -1082,20 +1078,20 @@ cdef class SemidefiniteProgram(SageObject): EXAMPLES:: - sage: p. = SemidefiniteProgram(solver = "cvxopt", maximization = False) - sage: p.solver_parameter("show_progress", True) - sage: p.solver_parameter("show_progress") + sage: p. = SemidefiniteProgram(solver = "cvxopt", maximization = False) # optional - cvxopt + sage: p.solver_parameter("show_progress", True) # optional - cvxopt + sage: p.solver_parameter("show_progress") # optional - cvxopt True - sage: p.set_objective(x[0] - x[1]) + sage: p.set_objective(x[0] - x[1]) # optional - cvxopt sage: a1 = matrix([[1, 2.], [2., 3.]]) sage: a2 = matrix([[3, 4.], [4., 2.]]) sage: a3 = matrix([[5, 6.], [6., 7.]]) sage: b1 = matrix([[1, 1.], [1., 1.]]) sage: b2 = matrix([[2, 2.], [2., 1.]]) sage: b3 = matrix([[3, 3.], [3., 3.]]) - sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) - sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) - sage: N(p.solve(),4) + sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3) # optional - cvxopt + sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3) # optional - cvxopt + sage: N(p.solve(),4) # optional - cvxopt pcost dcost gap pres dres k/t 0: 1... ... @@ -1183,13 +1179,13 @@ class SDPSolverException(RuntimeError): No solution:: - sage: p = SemidefiniteProgram(solver="cvxopt") - sage: x = p.new_variable() - sage: p.set_objective(x[0]) + sage: p = SemidefiniteProgram(solver="cvxopt") # optional - cvxopt + sage: x = p.new_variable() # optional - cvxopt + sage: p.set_objective(x[0]) # optional - cvxopt sage: a = matrix([[1,2],[2,4]]) sage: b = matrix([[1,9],[9,4]]) - sage: p.add_constraint( a*x[0] == b ) - sage: p.solve() + sage: p.add_constraint( a*x[0] == b ) # optional - cvxopt + sage: p.solve() # optional - cvxopt Traceback (most recent call last): ... SDPSolverException: ... From 4608b0a285c912040446311f9b97cb416ec868bd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Jul 2021 13:18:42 -0700 Subject: [PATCH 326/336] src/setup.cfg.m4: Remove cvxopt --- src/setup.cfg.m4 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 12039bf1d93..cf4143a171f 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -65,7 +65,6 @@ dnl From Makefile.in: DOC_DEPENDENCIES | sed "2,\$s/^/ /;"')dnl dnl Other Python packages that are standard spkg, used in doctests esyscmd(`sage-get-system-packages install-requires \ - cvxopt \ rpy2 \ fpylll \ | sed "2,\$s/^/ /;"')dnl From ff0c762abb64bf847c9e5c07aa252640f5c01139 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Jul 2021 10:15:50 -0700 Subject: [PATCH 327/336] build/pkgs/arb/package-version.txt: Bump patch level --- build/pkgs/arb/package-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/arb/package-version.txt b/build/pkgs/arb/package-version.txt index ef0f38abe16..0d13ee7427e 100644 --- a/build/pkgs/arb/package-version.txt +++ b/build/pkgs/arb/package-version.txt @@ -1 +1 @@ -2.19.0 +2.19.0.p0 From 2cde511c8dcc1b70f5484e7723c4aa8fdd284ad9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Jul 2021 12:45:31 -0700 Subject: [PATCH 328/336] configure.ac: Add option --disable-cvxopt --- configure.ac | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configure.ac b/configure.ac index 4fe290b225d..1171cada021 100644 --- a/configure.ac +++ b/configure.ac @@ -428,6 +428,14 @@ AS_IF([test "x$enable_download_from_upstream_url" = "xyes"], [ ]) AC_SUBST([SAGE_SPKG_OPTIONS]) +AC_ARG_ENABLE([cvxopt], + AS_HELP_STRING([--disable-cvxopt], + [disable build of the cvxopt package and its prerequisite suitesparse]), [ + for pkg in cvxopt suitesparse; do + AS_VAR_SET([SAGE_ENABLE_$pkg], [$enableval]) + done + ]) + AC_ARG_ENABLE([notebook], AS_HELP_STRING([--disable-notebook], [disable build of the Jupyter notebook and related packages]), [ From 2ab2f15ce27a07728437a959f10a5551d464e1e5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Jul 2021 12:45:39 -0700 Subject: [PATCH 329/336] Revert "build/pkgs/{cvxopt,suitesparse}/type: Change to optional" This reverts commit 83e309af0cc8555ae7f4053d03ea02a69a1465c6. --- build/pkgs/cvxopt/type | 2 +- build/pkgs/suitesparse/type | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/cvxopt/type b/build/pkgs/cvxopt/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/cvxopt/type +++ b/build/pkgs/cvxopt/type @@ -1 +1 @@ -optional +standard diff --git a/build/pkgs/suitesparse/type b/build/pkgs/suitesparse/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/suitesparse/type +++ b/build/pkgs/suitesparse/type @@ -1 +1 @@ -optional +standard From 2a6dd749b21ae9a5cb326c79e7e2a4aa77da6965 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Mon, 19 Jul 2021 09:31:58 +0900 Subject: [PATCH 330/336] Fix a few typographic errors in schemes folder --- src/sage/schemes/curves/curve.py | 17 +++++++++++++---- src/sage/schemes/generic/algebraic_scheme.py | 6 +++--- src/sage/schemes/toric/variety.py | 6 +++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/curves/curve.py b/src/sage/schemes/curves/curve.py index b513ae901e6..35eff517c38 100644 --- a/src/sage/schemes/curves/curve.py +++ b/src/sage/schemes/curves/curve.py @@ -86,7 +86,7 @@ def _repr_type(self): return "Generic" def _latex_(self): - """ + r""" Return a latex representation of this curve. EXAMPLES:: @@ -94,15 +94,24 @@ def _latex_(self): sage: x,y,z = PolynomialRing(QQ, 3, names='x,y,z').gens() sage: C = Curve(y^2*z - x^3 - 17*x*z^2 + y*z^2) sage: latex(C) - -x^{3} + y^{2} z - 17 x z^{2} + y z^{2} + \text{Projective Plane curve over $\Bold{Q}$ + defined by $-x^{3} + y^{2} z - 17 x z^{2} + y z^{2}$} sage: A2 = AffineSpace(2, QQ, names=['x','y']) sage: x, y = A2.coordinate_ring().gens() sage: C = Curve(y^2 - x^3 - 17*x + y) sage: latex(C) - -x^{3} + y^{2} - 17 x + y + \text{Affine Plane curve over $\Bold{Q}$ + defined by $-x^{3} + y^{2} - 17 x + y$} """ - return latex(self.defining_polynomial()) + if (self.defining_ideal().is_zero() + and self.ambient_space().dimension() == 1): + ambient_type, ring = self._repr_type(), latex(self.base_ring()) + return fr"\text{{{ambient_type} line over ${ring}$}}" + else: + ambient_type, ring = self._repr_type(), latex(self.base_ring()) + polys = ', '.join(f'${latex(p)}$' for p in self.defining_polynomials()) + return fr"\text{{{ambient_type} curve over ${ring}$ defined by {polys}}}" def defining_polynomial(self): """ diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index 4416daf1f70..9d0a2dddd89 100644 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -247,7 +247,7 @@ def __init__(self, A): scheme.Scheme.__init__(self, A.base_scheme()) def _latex_(self): - """ + r""" Return a LaTeX representation of this algebraic scheme. TESTS:: @@ -257,9 +257,9 @@ def _latex_(self): sage: S = AlgebraicScheme(P); S Subscheme of Projective Space of dimension 3 over Integer Ring sage: S._latex_() - '\text{Subscheme of } {\\mathbf P}_{\\Bold{Z}}^3' + '\\text{Subscheme of ${\\mathbf P}_{\\Bold{Z}}^3$}' """ - return "\text{Subscheme of } %s" % latex(self.__A) + return r"\text{{Subscheme of ${}$}}".format(latex(self.__A)) def is_projective(self): """ diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index 72b1a0151a2..d70e01a482d 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -3174,7 +3174,7 @@ def _repr_(self): sage: toric_varieties.P2().cohomology_ring()._repr_() 'Rational cohomology ring of a 2-d CPR-Fano toric variety covered by 3 affine patches' """ - return 'Rational cohomology ring of a '+self._variety._repr_() + return f'Rational cohomology ring of a {self._variety._repr_()}' def _latex_(self): r""" @@ -3188,9 +3188,9 @@ def _latex_(self): sage: cohomology_ring = toric_varieties.P2().cohomology_ring() sage: print(cohomology_ring._latex_()) - H^\ast\left(\mathbb{P}_{\Delta^{2}_{15}},\QQ\right) + H^\ast\left(\mathbb{P}_{\Delta^{2}_{15}},\Bold{Q}\right) """ - return 'H^\\ast\\left('+self._variety._latex_()+',\\QQ\\right)' + return fr'H^\ast\left({self._variety._latex_()},{latex(QQ)}\right)' def _element_constructor_(self,x): r""" From ed1484874b03f2b6f2e1897537401db48e078232 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 21 Jul 2021 09:00:33 +1000 Subject: [PATCH 331/336] Remove readline lib file from the documentation. --- src/doc/en/reference/libs/index.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index f31986b8385..6403a5171a6 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -145,13 +145,6 @@ ratpoints sage/libs/ratpoints -Readline --------- -.. toctree:: - :maxdepth: 2 - - sage/libs/readline - Symmetrica ---------- .. toctree:: From d4156f754ca211962bd58057a547a1c726d60e33 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 20 Jul 2021 19:23:23 -0700 Subject: [PATCH 332/336] build/pkgs/singular/patches/0001-factory-canonicalform.h-Add-more-FACTORY_PUBLIC.patch: New --- ...onicalform.h-Add-more-FACTORY_PUBLIC.patch | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 build/pkgs/singular/patches/0001-factory-canonicalform.h-Add-more-FACTORY_PUBLIC.patch diff --git a/build/pkgs/singular/patches/0001-factory-canonicalform.h-Add-more-FACTORY_PUBLIC.patch b/build/pkgs/singular/patches/0001-factory-canonicalform.h-Add-more-FACTORY_PUBLIC.patch new file mode 100644 index 00000000000..7f083e9e869 --- /dev/null +++ b/build/pkgs/singular/patches/0001-factory-canonicalform.h-Add-more-FACTORY_PUBLIC.patch @@ -0,0 +1,97 @@ +From 5830ce6deabefaeaec492f1054cc619af4e4e2d2 Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Tue, 20 Jul 2021 19:19:32 -0700 +Subject: [PATCH] factory/canonicalform.h: Add more FACTORY_PUBLIC + +--- + factory/canonicalform.h | 42 ++++++++++++++++++++--------------------- + 1 file changed, 21 insertions(+), 21 deletions(-) + +diff --git a/factory/canonicalform.h b/factory/canonicalform.h +index 82dfa57e1..8fd158385 100644 +--- a/factory/canonicalform.h ++++ b/factory/canonicalform.h +@@ -172,27 +172,27 @@ public: + int ilog2() const; + + // comparison operators +- friend bool operator == ( const CanonicalForm&, const CanonicalForm& ); +- friend bool operator != ( const CanonicalForm&, const CanonicalForm& ); +- friend bool operator > ( const CanonicalForm&, const CanonicalForm& ); +- friend bool operator < ( const CanonicalForm&, const CanonicalForm& ); ++ friend FACTORY_PUBLIC bool operator == ( const CanonicalForm&, const CanonicalForm& ); ++ friend FACTORY_PUBLIC bool operator != ( const CanonicalForm&, const CanonicalForm& ); ++ friend FACTORY_PUBLIC bool operator > ( const CanonicalForm&, const CanonicalForm& ); ++ friend FACTORY_PUBLIC bool operator < ( const CanonicalForm&, const CanonicalForm& ); + + // arithmetic operators +- friend CF_NO_INLINE CanonicalForm operator - ( const CanonicalForm& ); ++ friend CF_NO_INLINE FACTORY_PUBLIC CanonicalForm operator - ( const CanonicalForm& ); + +- friend void FACTORY_PUBLIC divrem ( const CanonicalForm&, const CanonicalForm&, CanonicalForm&, CanonicalForm& ); +- friend bool divremt ( const CanonicalForm&, const CanonicalForm&, CanonicalForm&, CanonicalForm& ); +- friend bool tryDivremt ( const CanonicalForm&, const CanonicalForm&, CanonicalForm&, CanonicalForm&, const CanonicalForm&, bool& ); ++ friend FACTORY_PUBLIC void divrem ( const CanonicalForm&, const CanonicalForm&, CanonicalForm&, CanonicalForm& ); ++ friend FACTORY_PUBLIC bool divremt ( const CanonicalForm&, const CanonicalForm&, CanonicalForm&, CanonicalForm& ); ++ friend FACTORY_PUBLIC bool tryDivremt ( const CanonicalForm&, const CanonicalForm&, CanonicalForm&, CanonicalForm&, const CanonicalForm&, bool& ); + +- friend CanonicalForm bgcd ( const CanonicalForm &, const CanonicalForm & ); +- friend CanonicalForm bextgcd ( const CanonicalForm &, const CanonicalForm &, CanonicalForm &, CanonicalForm & ); ++ friend FACTORY_PUBLIC CanonicalForm bgcd ( const CanonicalForm &, const CanonicalForm & ); ++ friend FACTORY_PUBLIC CanonicalForm bextgcd ( const CanonicalForm &, const CanonicalForm &, CanonicalForm &, CanonicalForm & ); + + // input/output + #ifndef NOSTREAMIO + void print( OSTREAM&, char * ) const; + void print( OSTREAM& ) const; +- friend OSTREAM& operator << ( OSTREAM&, const CanonicalForm& ); +- friend ISTREAM& operator >> ( ISTREAM&, CanonicalForm& ); ++ friend FACTORY_PUBLIC OSTREAM& operator << ( OSTREAM&, const CanonicalForm& ); ++ friend FACTORY_PUBLIC ISTREAM& operator >> ( ISTREAM&, CanonicalForm& ); + #endif /* NOSTREAMIO */ + + // obsolete methods +@@ -234,7 +234,7 @@ mod ( const CanonicalForm&, const CanonicalForm& ); + /*BEGINPUBLIC*/ + + //{{{ function declarations from canonicalform.cc +-CanonicalForm blcm ( const CanonicalForm & f, const CanonicalForm & g ); ++CanonicalForm FACTORY_PUBLIC blcm ( const CanonicalForm & f, const CanonicalForm & g ); + + CanonicalForm FACTORY_PUBLIC power ( const CanonicalForm & f, int n ); + +@@ -244,23 +244,23 @@ CanonicalForm FACTORY_PUBLIC power ( const Variable & v, int n ); + //{{{ function declarations from cf_gcd.cc + CanonicalForm FACTORY_PUBLIC gcd ( const CanonicalForm&, const CanonicalForm& ); + +-CanonicalForm gcd_poly ( const CanonicalForm & f, const CanonicalForm & g ); ++CanonicalForm FACTORY_PUBLIC gcd_poly ( const CanonicalForm & f, const CanonicalForm & g ); + +-CanonicalForm lcm ( const CanonicalForm&, const CanonicalForm& ); ++CanonicalForm FACTORY_PUBLIC lcm ( const CanonicalForm&, const CanonicalForm& ); + +-CanonicalForm pp ( const CanonicalForm& ); ++CanonicalForm FACTORY_PUBLIC pp ( const CanonicalForm& ); + +-CanonicalForm content ( const CanonicalForm& ); ++CanonicalForm FACTORY_PUBLIC content ( const CanonicalForm& ); + +-CanonicalForm content ( const CanonicalForm&, const Variable& ); ++CanonicalForm FACTORY_PUBLIC content ( const CanonicalForm&, const Variable& ); + +-CanonicalForm icontent ( const CanonicalForm & f ); ++CanonicalForm FACTORY_PUBLIC icontent ( const CanonicalForm & f ); + +-CanonicalForm vcontent ( const CanonicalForm & f, const Variable & x ); ++CanonicalForm FACTORY_PUBLIC vcontent ( const CanonicalForm & f, const Variable & x ); + //}}} + + //{{{ function declarations from cf_ops.cc +-CanonicalForm swapvar ( const CanonicalForm &, const Variable &, const Variable & ); ++CanonicalForm FACTORY_PUBLIC swapvar ( const CanonicalForm &, const Variable &, const Variable & ); + + CanonicalForm FACTORY_PUBLIC replacevar ( const CanonicalForm &, const Variable &, const Variable & ); + +-- +2.31.1 + From 551d3ae99249456904eb6d2cdc97dbe9ed6201b9 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 21 Jul 2021 00:11:09 -0400 Subject: [PATCH 333/336] Trac #32185: special case constant symbolic relations. The mixed_order() code in sage/symbolic/comparison.pyx uses a bunch of heuristics to figure out how to compare two things. To compare (say) x and y, it creates the expression (x - y), and then looks at what type of thing (x - y) is. In most cases, this seems to work; however, when x is a symbolic constant and y is zero, the expression (x - y) reduces to just x, a symbolic constant. The mixed_order() code is not expecting this, and it fails to compare x.pyobject() to the integer zero. To work around this problem, this commit adds an additional check for whether or not (x - y) is a symbolic constant, and falls through to a numerical comparison in that case. --- src/sage/symbolic/comparison.pyx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/symbolic/comparison.pyx b/src/sage/symbolic/comparison.pyx index a701b76ca53..7d45c4476c5 100644 --- a/src/sage/symbolic/comparison.pyx +++ b/src/sage/symbolic/comparison.pyx @@ -258,6 +258,16 @@ cpdef int mixed_order(lhs, rhs) except -2: sage: mixed_order(SR(oo), sqrt(2)) 1 + + Ensure that :trac:`32185` is fixed:: + + sage: mixed_order(pi, 0) + 1 + sage: mixed_order(golden_ratio, 0) + 1 + sage: mixed_order(log2, 0) + 1 + """ if lhs is rhs: return 0 @@ -345,7 +355,7 @@ class _mixed_key(object): return pynac_result == relational_true det_ex = self.ex - other.ex - if not has_symbol_or_function((rel)._gobj): + if not has_symbol_or_function((rel)._gobj) and not det_ex.is_constant(): while hasattr(det_ex, 'pyobject') and isinstance(det_ex, Expression): try: det_ex = det_ex.pyobject() From 82020efc61c10fbd585d09a75e0febb6c9be5b17 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 22 Jul 2021 17:58:01 +0200 Subject: [PATCH 334/336] doctest tag in the first line --- src/sage/geometry/polyhedron/backend_polymake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index e005183094f..6f595395153 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -417,8 +417,8 @@ def _init_from_Vrepresentation_and_Hrepresentation(self, Vrep, Hrep): sage: parent = Polyhedra_polymake(ZZ, 1, 'polymake') sage: Vrep = [[[0], [1]], [], []] sage: Hrep = [[[0, 1], [1, -1]], []] - sage: p = Polyhedron_polymake(parent, Vrep, Hrep, - ....: Vrep_minimal=True, Hrep_minimal=True) # indirect doctest # optional - polymake + sage: p = Polyhedron_polymake(parent, Vrep, Hrep, # indirect doctest # optional - polymake + ....: Vrep_minimal=True, Hrep_minimal=True) sage: p # optional - polymake A 1-dimensional polyhedron in ZZ^1 defined as the convex hull of 2 vertices """ From 32d34b71534cea23b6b4cb382ce13064c5b5e928 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 23 Jul 2021 08:52:27 +1000 Subject: [PATCH 335/336] Added sorted to doctest in polyhedral_complex.py for consistent output. --- src/sage/geometry/polyhedral_complex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index 06dc60380c3..10e8c34f79d 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -1404,7 +1404,7 @@ def relative_boundary_cells(self): sage: all(p.dimension() == 1 for p in rbd_cells) True sage: pc_lower_dim = PolyhedralComplex([p3]) - sage: [p.vertices() for p in pc_lower_dim.relative_boundary_cells()] + sage: sorted([p.vertices() for p in pc_lower_dim.relative_boundary_cells()]) [(A vertex at (0, 2),), (A vertex at (1, 2),)] Test on polyhedral complex which is not pure:: From 8bae3ff7ad670e12cead6860cbd216f819e8d6d2 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Sat, 24 Jul 2021 17:34:36 +0200 Subject: [PATCH 336/336] Updated SageMath version to 9.4.beta6 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sagelib/package-version.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index d0132ce162d..618a0985132 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.4.beta5", - "version": "9.4.beta5", + "title": "sagemath/sage: 9.4.beta6", + "version": "9.4.beta6", "upload_type": "software", - "publication_date": "2021-07-18", + "publication_date": "2021-07-24", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.4.beta5", + "identifier": "https://github.com/sagemath/sage/tree/9.4.beta6", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index 9aeb663dea5..ba6c89c9a88 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.4.beta5, Release Date: 2021-07-18 +SageMath version 9.4.beta6, Release Date: 2021-07-24 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 738066670a5..3bb9983d8ea 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=694ed11a1e402aa7714737ea571ca38845fe425b -md5=35e45f13544608d4e981acee1065ce88 -cksum=98651158 +sha1=c55867da552d5ed30328a35a480dc1ab14ec232c +md5=5cbace690cae40b89993e3a71fe4200c +cksum=912186647 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 98aac3a7ccd..3e74f1098db 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -47955602029e3ee3ac6477115cbc5d5e72956ee6 +25ff4f4daf3cd93053f7b8c1e39117ce67e96b6c diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index 0439ed38bee..2b12acd816c 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.4.beta5 +9.4.beta6 diff --git a/src/VERSION.txt b/src/VERSION.txt index 0439ed38bee..2b12acd816c 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.4.beta5 +9.4.beta6 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 4595e6aee5d..901e9d7e877 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.4.beta5' -SAGE_RELEASE_DATE='2021-07-18' -SAGE_VERSION_BANNER='SageMath version 9.4.beta5, Release Date: 2021-07-18' +SAGE_VERSION='9.4.beta6' +SAGE_RELEASE_DATE='2021-07-24' +SAGE_VERSION_BANNER='SageMath version 9.4.beta6, Release Date: 2021-07-24' diff --git a/src/sage/version.py b/src/sage/version.py index b97a07ff370..be6e001eed6 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '9.4.beta5' -date = '2021-07-18' -banner = 'SageMath version 9.4.beta5, Release Date: 2021-07-18' +version = '9.4.beta6' +date = '2021-07-24' +banner = 'SageMath version 9.4.beta6, Release Date: 2021-07-24'