From 52e40245450f85b069c95b629a72fe254c207a9f Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 11 Oct 2012 20:08:19 +0000 Subject: [PATCH 001/184] Trac 13591: improve add_bigoh for padics --- .../rings/padics/local_generic_element.pyx | 101 ++++++++++++++++-- 1 file changed, 90 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index 4ff1d223df5..824193b0ee5 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -391,21 +391,100 @@ cdef class LocalGenericElement(CommutativeRingElement): # this doctest doesn't actually test this function, since _sub_ is overridden. return self + (-right) - def add_bigoh(self, prec): + def add_bigoh(self, absprec): """ - Returns self to reduced precision ``prec``. + Returns a copy of ``self`` with absolute precision decreased to + ``absprec``. + + INPUT: + + - ``absprec`` -- an integer or positive infinity + + OUTPUT: + + a copy of ``self`` with absolute precision set to the minimum of + ``absprec`` and the precisions of ``self`` EXAMPLES:: - sage: K = Qp(11, 5) - sage: L. = K.extension(x^20 - 11) - sage: b = a^3 + 3*a^5; b - a^3 + 3*a^5 + O(a^103) - sage: b.add_bigoh(17) - a^3 + 3*a^5 + O(a^17) - sage: b.add_bigoh(150) - a^3 + 3*a^5 + O(a^103) + + sage: K = QpCR(3,4) + sage: o = K(1); o + 1 + O(3^4) + sage: o.add_bigoh(2) + 1 + O(3^2) + sage: o.add_bigoh(-5) + O(3^-5) + + One cannot use ``add_bigoh`` to lift to a higher precision; this + can be accomplished with :meth:`lift_to_precision`:: + + sage: o.add_bigoh(5) + 1 + O(3^4) + + Only over a field, negative values for ``absprec`` are allowed:: + + sage: R = ZpCA(3,4) + sage: R(3).add_bigoh(-5) + Traceback (most recent call last): + ... + ValueError: The precision of a local element can not be negative unless it is defined over a field. + + The precision of fixed-mod elements can not be decreased:: + + sage: R = ZpFM(3,4) + sage: R(3).add_bigoh(5) + 3 + O(3^4) + sage: R(3).add_bigoh(1) + Traceback (most recent call last): + ... + ValueError: The precision of a local element with a fixed modulus can not be decreased. + + TESTS: + + Test that this also works for infinity:: + + sage: R = ZpCR(3,4) + sage: R(3).add_bigoh(infinity) + 3 + O(3^5) + sage: R(0).add_bigoh(infinity) + 0 + + Test that this works over unramified extensions:: + + sage: R = ZpCA(3,5); S. = R[]; W. = R.extension( t^2 + 1 ) + sage: (t + 3).add_bigoh(1) + t + O(3) + + sage: R = ZpCR(3,5); S. = R[]; W. = R.extension( t^2 + 1 ) + sage: (t + 3).add_bigoh(1) + t + O(3) + + sage: R = QpCR(3,5); S. = R[]; W. = R.extension( t^2 + 1 ) + sage: (t + 3).add_bigoh(1) + t + O(3) + + Test that this works over Eisenstein extensions:: + + sage: R = ZpCA(3,5); S. = R[]; W. = R.extension( t^2 - 3 ) + sage: (t + 3).add_bigoh(2) + t + O(t^2) + + sage: R = ZpCR(3,5); S. = R[]; W. = R.extension( t^2 - 3 ) + sage: (t + 3).add_bigoh(2) + t + O(t^2) + + sage: R = QpCR(3,5); S. = R[]; W. = R.extension( t^2 - 3 ) + sage: (t + 3).add_bigoh(2) + t + O(t^2) + """ - return self.parent()(self, absprec=prec) + if not self.parent().is_field() and absprec < 0: + raise ValueError("The precision of a local element can not be negative unless it is defined over a field.") + if absprec >= self.precision_absolute(): + return self + if self.parent().is_fixed_mod(): + raise ValueError("The precision of a local element with a fixed modulus can not be decreased.") + return self.parent()(self, absprec=absprec) #def copy(self): # raise NotImplementedError From 60cb928fceacfe8cc8f4de04c5164d9ac04aa841 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 25 Jun 2014 20:42:52 +0200 Subject: [PATCH 002/184] Added sig_on/sig_off to prevent a crash in p-adic add_bigoh() --- src/sage/libs/linkages/padics/mpz.pxi | 8 +++++--- src/sage/rings/padics/pow_computer.pyx | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/libs/linkages/padics/mpz.pxi b/src/sage/libs/linkages/padics/mpz.pxi index dbeeaa84198..1ff565c5071 100644 --- a/src/sage/libs/linkages/padics/mpz.pxi +++ b/src/sage/libs/linkages/padics/mpz.pxi @@ -133,7 +133,9 @@ cdef inline bint creduce(mpz_t out, mpz_t a, long prec, PowComputer_class prime_ - returns True if the reduction is zero; False otherwise. """ + sig_on() mpz_mod(out, a, prime_pow.pow_mpz_t_tmp(prec)[0]) + sig_off() return mpz_sgn(out) == 0 cdef inline bint creduce_small(mpz_t out, mpz_t a, long prec, PowComputer_class prime_pow) except -1: @@ -306,7 +308,7 @@ cdef inline int cinvert(mpz_t out, mpz_t a, long prec, PowComputer_class prime_p success = mpz_invert(out, a, prime_pow.pow_mpz_t_tmp(prec)[0]) if not success: raise ZeroDivisionError - + cdef inline int cmul(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_class prime_pow) except -1: """ Multiplication. @@ -380,7 +382,7 @@ cdef inline bint cisone(mpz_t out, PowComputer_class prime_pow) except -1: - returns True if `a = 1`, and False otherwise. """ return mpz_cmp_ui(out, 1) == 0 - + cdef inline bint ciszero(mpz_t out, PowComputer_class prime_pow) except -1: """ Returns whether this element is equal to 0. @@ -395,7 +397,7 @@ cdef inline bint ciszero(mpz_t out, PowComputer_class prime_pow) except -1: - returns True if `a = 0`, and False otherwise. """ return mpz_cmp_ui(out, 0) == 0 - + cdef inline int cpow(mpz_t out, mpz_t a, mpz_t n, long prec, PowComputer_class prime_pow) except -1: """ Exponentiation. diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index def84543a3e..92bc522e4da 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -439,7 +439,9 @@ cdef class PowComputer_base(PowComputer_class): for i from 2 <= i <= cache_limit: mpz_init(self.small_powers[i]) mpz_mul(self.small_powers[i], self.small_powers[i - 1], prime.value) + sig_on() mpz_pow_ui(self.top_power, prime.value, prec_cap) + sig_off() self.deg = 1 self.e = 1 self.f = 1 @@ -505,7 +507,9 @@ cdef class PowComputer_base(PowComputer_class): return &(self.small_powers[n]) if n == self.prec_cap: return &(self.top_power) + sig_on() mpz_pow_ui(self.temp_m, self.prime.value, n) + sig_off() return &(self.temp_m) pow_comp_cache = {} From 3b222dfc8ef941d1b3f0d2c3b0a2be1a132e7b17 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 25 Jun 2014 20:43:52 +0200 Subject: [PATCH 003/184] Removed duplicate copyright header --- src/sage/rings/padics/local_generic_element.pyx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index 824193b0ee5..fb2e1cbe8e4 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -20,17 +20,6 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -#***************************************************************************** -# Copyright (C) 2007-2013 David Roe -# William Stein -# -# 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. -# -# http://www.gnu.org/licenses/ -#***************************************************************************** - from sage.rings.infinity import infinity from sage.structure.element cimport ModuleElement, RingElement, CommutativeRingElement From 15a2722fe11b4f3ba3ea4a08f6ef5eb6da95db54 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 25 Jun 2014 20:44:22 +0200 Subject: [PATCH 004/184] a padic element's add_bigoh() can now handle negative inputs (see #13591) --- src/sage/rings/padics/CA_template.pxi | 10 ++- src/sage/rings/padics/CR_template.pxi | 10 ++- src/sage/rings/padics/FM_template.pxi | 24 +++++- .../rings/padics/local_generic_element.pyx | 76 +++++++------------ 4 files changed, 66 insertions(+), 54 deletions(-) diff --git a/src/sage/rings/padics/CA_template.pxi b/src/sage/rings/padics/CA_template.pxi index 140aae3ca64..0b48f6c2c7b 100644 --- a/src/sage/rings/padics/CA_template.pxi +++ b/src/sage/rings/padics/CA_template.pxi @@ -528,9 +528,17 @@ cdef class CAElement(pAdicTemplateElement): else: if not PY_TYPE_CHECK(absprec, Integer): absprec = Integer(absprec) - aprec = mpz_get_si((absprec).value) + if mpz_fits_slong_p((absprec).value) == 0: + if mpz_sgn((absprec).value) == -1: + raise ValueError("absprec must fit into a signed long") + else: + aprec = self.prime_pow.prec_cap + else: + aprec = mpz_get_si((absprec).value) if aprec >= self.absprec: return self + if aprec < 0: + return self.parent().fraction_field()(self).add_bigoh(absprec) cdef CAElement ans = self._new_c() ans.absprec = aprec creduce(ans.value, self.value, ans.absprec, ans.prime_pow) diff --git a/src/sage/rings/padics/CR_template.pxi b/src/sage/rings/padics/CR_template.pxi index dd534f83cd1..86140dd1ed5 100644 --- a/src/sage/rings/padics/CR_template.pxi +++ b/src/sage/rings/padics/CR_template.pxi @@ -875,7 +875,15 @@ cdef class CRElement(pAdicTemplateElement): else: if not PY_TYPE_CHECK(absprec, Integer): absprec = Integer(absprec) - aprec = mpz_get_si((absprec).value) + if mpz_fits_slong_p((absprec).value) == 0: + if mpz_sgn((absprec).value) == -1: + raise ValueError("absprec must fit into a signed long") + else: + aprec = self.prime_pow.prec_cap + else: + aprec = mpz_get_si((absprec).value) + if aprec < 0 and not self.parent().is_field(): + return self.parent().fraction_field()(self).add_bigoh(absprec) if aprec < self.ordp: ans = self._new_c() ans._set_inexact_zero(aprec) diff --git a/src/sage/rings/padics/FM_template.pxi b/src/sage/rings/padics/FM_template.pxi index 7c348a2b9a5..a0f9cd4a33f 100644 --- a/src/sage/rings/padics/FM_template.pxi +++ b/src/sage/rings/padics/FM_template.pxi @@ -426,14 +426,34 @@ cdef class FMElement(pAdicTemplateElement): sage: R = Zp(7,4,'fixed-mod','series'); a = R(8); a.add_bigoh(1) 1 + O(7^4) + + TESTS: + + We handle very large and very small values for ``absprec`` correctly:: + + sage: a = R(7) + sage: a.add_bigoh(2^1000) + 7 + O(7^4) + sage: a.add_bigoh(-2^1000) + Traceback (most recent call last): + ... + ValueError: absprec must be at least 0 + """ - cdef long aprec, newprec + cdef long aprec if PY_TYPE_CHECK(absprec, int): aprec = absprec else: if not PY_TYPE_CHECK(absprec, Integer): absprec = Integer(absprec) - aprec = mpz_get_si((absprec).value) + if mpz_sgn((absprec).value) == -1: + aprec = -1 + elif mpz_fits_slong_p((absprec).value) == 0: + aprec = self.prime_pow.prec_cap + else: + aprec = mpz_get_si((absprec).value) + if aprec < 0: + raise ValueError("absprec must be at least 0") if aprec >= self.prime_pow.prec_cap: return self cdef FMElement ans = self._new_c() diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index fb2e1cbe8e4..8cb77f5cbd5 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -8,11 +8,12 @@ AUTHORS: - David Roe: initial version -- Julian Rueth (2012-10-15): added inverse_of_unit() +- Julian Rueth (2012-10-15, 2014-06-25): added inverse_of_unit(); improved + add_bigoh() """ #***************************************************************************** -# Copyright (C) 2007,2008,2009 David Roe -# 2012 Julian Rueth +# Copyright (C) 2007-2013 David Roe +# 2012-2014 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -382,17 +383,12 @@ cdef class LocalGenericElement(CommutativeRingElement): def add_bigoh(self, absprec): """ - Returns a copy of ``self`` with absolute precision decreased to + Return a copy of this element with ablsolute precision decreased to ``absprec``. INPUT: - - ``absprec`` -- an integer or positive infinity - - OUTPUT: - - a copy of ``self`` with absolute precision set to the minimum of - ``absprec`` and the precisions of ``self`` + - ``absprec`` -- an integer or positive infinity EXAMPLES:: @@ -410,23 +406,32 @@ cdef class LocalGenericElement(CommutativeRingElement): sage: o.add_bigoh(5) 1 + O(3^4) - Only over a field, negative values for ``absprec`` are allowed:: + Negative values of ``absprec`` return an element in the fraction field + of the element's parent:: sage: R = ZpCA(3,4) sage: R(3).add_bigoh(-5) - Traceback (most recent call last): - ... - ValueError: The precision of a local element can not be negative unless it is defined over a field. + O(3^-5) - The precision of fixed-mod elements can not be decreased:: + For fixed-mod elements this method truncates the element:: sage: R = ZpFM(3,4) + sage: R(3).add_bigoh(1) + O(3^4) + + If ``absprec`` exceeds the precision of the element, then this method + has no effect:: + sage: R(3).add_bigoh(5) 3 + O(3^4) - sage: R(3).add_bigoh(1) + + However, a negative value for ``absprec`` leads to an error, since + there is no fraction field for fixed-mod elements:: + + sage: R(3).add_bigoh(-1) Traceback (most recent call last): ... - ValueError: The precision of a local element with a fixed modulus can not be decreased. + ValueError: absprec must be at least 0 TESTS: @@ -438,42 +443,13 @@ cdef class LocalGenericElement(CommutativeRingElement): sage: R(0).add_bigoh(infinity) 0 - Test that this works over unramified extensions:: - - sage: R = ZpCA(3,5); S. = R[]; W. = R.extension( t^2 + 1 ) - sage: (t + 3).add_bigoh(1) - t + O(3) - - sage: R = ZpCR(3,5); S. = R[]; W. = R.extension( t^2 + 1 ) - sage: (t + 3).add_bigoh(1) - t + O(3) - - sage: R = QpCR(3,5); S. = R[]; W. = R.extension( t^2 + 1 ) - sage: (t + 3).add_bigoh(1) - t + O(3) - - Test that this works over Eisenstein extensions:: - - sage: R = ZpCA(3,5); S. = R[]; W. = R.extension( t^2 - 3 ) - sage: (t + 3).add_bigoh(2) - t + O(t^2) - - sage: R = ZpCR(3,5); S. = R[]; W. = R.extension( t^2 - 3 ) - sage: (t + 3).add_bigoh(2) - t + O(t^2) - - sage: R = QpCR(3,5); S. = R[]; W. = R.extension( t^2 - 3 ) - sage: (t + 3).add_bigoh(2) - t + O(t^2) - """ - if not self.parent().is_field() and absprec < 0: - raise ValueError("The precision of a local element can not be negative unless it is defined over a field.") + parent = self.parent() if absprec >= self.precision_absolute(): return self - if self.parent().is_fixed_mod(): - raise ValueError("The precision of a local element with a fixed modulus can not be decreased.") - return self.parent()(self, absprec=absprec) + if absprec < 0: + parent = parent.fraction_field() + return parent(self, absprec=absprec) #def copy(self): # raise NotImplementedError From 6758b1d7f60a2ffd28636234fcc5c55cb90e8a37 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 25 Jun 2014 20:45:08 +0200 Subject: [PATCH 005/184] Added tests for padic add_bigoh() --- src/sage/rings/padics/CA_template.pxi | 8 ++++++ src/sage/rings/padics/local_generic.py | 39 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/sage/rings/padics/CA_template.pxi b/src/sage/rings/padics/CA_template.pxi index 0b48f6c2c7b..36b68b4f465 100644 --- a/src/sage/rings/padics/CA_template.pxi +++ b/src/sage/rings/padics/CA_template.pxi @@ -521,6 +521,14 @@ cdef class CAElement(pAdicTemplateElement): 2 + 3 + 3^2 + 3^3 + O(3^5) sage: a.add_bigoh(3) 2 + 3 + 3^2 + O(3^3) + + TESTS: + + Verify that :trac:`13591` has been resolved:: + + sage: k(3).add_bigoh(-1) + O(3^-1) + """ cdef long aprec, newprec if PY_TYPE_CHECK(absprec, int): diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 5b54181dc6c..6daa70bb737 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -534,3 +534,42 @@ def ext(self, *args, **kwds): """ return self.extension(*args, **kwds) + def _test_add_bigoh(self, **options): + r""" + Perform tests on ``add_bigoh``. + + EXAMPLES:: + + sage: K = Qp(3) + sage: K._test_add_bigoh() + + """ + tester = self._tester(**options) + for x in tester.some_elements(): + tester.assertEqual(x.add_bigoh(x.precision_absolute()), x) + from sage.rings.all import infinity + tester.assertEqual(x.add_bigoh(infinity), x) + tester.assertEqual(x.add_bigoh(x.precision_absolute()+1), x) + + y = x.add_bigoh(0) + tester.assertIs(y.parent(), self) + if self.is_capped_absolute() or self.is_capped_relative(): + tester.assertEqual(y, self.zero()) + tester.assertEqual(y.precision_absolute(), 0) + elif self.is_fixed_mod(): + tester.assertEqual(y, x) + else: + raise NotImplementedError + + # if absprec < 0, then the result is in the fraction field (see #13591) + try: + y = x.add_bigoh(-1) + except ValueError: + tester.assertTrue(self.is_fixed_mod()) + else: + tester.assertIs(y.parent(), self.fraction_field()) + tester.assertEqual(y.precision_absolute(), -1) + + # make sure that we handle very large values correctly + absprec = Integer(2)**1000 + tester.assertEqual(x.add_bigoh(absprec), x) From fb83d40572f5b3e817f723940ccc88c9d86898eb Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 28 Jun 2014 07:03:35 +0200 Subject: [PATCH 006/184] Fix handling of +infinity in add_bigoh() of padics. --- src/sage/rings/padics/CA_template.pxi | 4 +++- src/sage/rings/padics/FM_template.pxi | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/CA_template.pxi b/src/sage/rings/padics/CA_template.pxi index 36b68b4f465..f1a6b8dcc12 100644 --- a/src/sage/rings/padics/CA_template.pxi +++ b/src/sage/rings/padics/CA_template.pxi @@ -503,7 +503,7 @@ cdef class CAElement(pAdicTemplateElement): INPUT: - - ``absprec`` -- an integer + - ``absprec`` -- an integer or infinity OUTPUT: @@ -531,6 +531,8 @@ cdef class CAElement(pAdicTemplateElement): """ cdef long aprec, newprec + if absprec is infinity: + return self if PY_TYPE_CHECK(absprec, int): aprec = absprec else: diff --git a/src/sage/rings/padics/FM_template.pxi b/src/sage/rings/padics/FM_template.pxi index a0f9cd4a33f..8f23d24ef1f 100644 --- a/src/sage/rings/padics/FM_template.pxi +++ b/src/sage/rings/padics/FM_template.pxi @@ -416,7 +416,7 @@ cdef class FMElement(pAdicTemplateElement): INPUT: - - ``absprec`` -- an integer + - ``absprec`` -- an integer or infinity OUTPUT: @@ -441,6 +441,8 @@ cdef class FMElement(pAdicTemplateElement): """ cdef long aprec + if absprec is infinity: + return self if PY_TYPE_CHECK(absprec, int): aprec = absprec else: From 2c847138d49bbb1ff7acf4537a7c8be065d7db5f Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 28 Jun 2014 07:04:11 +0200 Subject: [PATCH 007/184] fix _test_add_bigoh() for capped relative padics --- src/sage/rings/padics/local_generic.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 6daa70bb737..51ec8d3ccc1 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -553,11 +553,13 @@ def _test_add_bigoh(self, **options): y = x.add_bigoh(0) tester.assertIs(y.parent(), self) - if self.is_capped_absolute() or self.is_capped_relative(): - tester.assertEqual(y, self.zero()) + if self.is_capped_absolute(): tester.assertEqual(y.precision_absolute(), 0) + tester.assertEqual(y, self.zero()) + elif self.is_capped_relative(): + tester.assertLessEqual(y.precision_absolute(), 0) elif self.is_fixed_mod(): - tester.assertEqual(y, x) + tester.assertGreaterEqual((x-y).valuation(), 0) else: raise NotImplementedError @@ -568,7 +570,7 @@ def _test_add_bigoh(self, **options): tester.assertTrue(self.is_fixed_mod()) else: tester.assertIs(y.parent(), self.fraction_field()) - tester.assertEqual(y.precision_absolute(), -1) + tester.assertLessEqual(y.precision_absolute(), -1) # make sure that we handle very large values correctly absprec = Integer(2)**1000 From cd0c153468f2b4b8ad4b3a21e3832ebdbf48083b Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 9 Jul 2014 20:09:49 +0200 Subject: [PATCH 008/184] Removed unnecessary sig_on()/sig_off() from p-adics code --- src/sage/libs/linkages/padics/mpz.pxi | 6 ++++-- src/sage/rings/padics/pow_computer.pyx | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/libs/linkages/padics/mpz.pxi b/src/sage/libs/linkages/padics/mpz.pxi index 1ff565c5071..ee0578f8154 100644 --- a/src/sage/libs/linkages/padics/mpz.pxi +++ b/src/sage/libs/linkages/padics/mpz.pxi @@ -133,9 +133,11 @@ cdef inline bint creduce(mpz_t out, mpz_t a, long prec, PowComputer_class prime_ - returns True if the reduction is zero; False otherwise. """ - sig_on() + # The following could fail if the value returned by + # prime_pow.pow_mpz_t_tmp(prec) is zero. We could add a sig_on()/sig_off() + # to keep sage from crashing. This comes at a performance penalty, however. + # A correct implementation of prime_pow should never return zero. mpz_mod(out, a, prime_pow.pow_mpz_t_tmp(prec)[0]) - sig_off() return mpz_sgn(out) == 0 cdef inline bint creduce_small(mpz_t out, mpz_t a, long prec, PowComputer_class prime_pow) except -1: diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index 92bc522e4da..75804f132c1 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -507,9 +507,9 @@ cdef class PowComputer_base(PowComputer_class): return &(self.small_powers[n]) if n == self.prec_cap: return &(self.top_power) - sig_on() + if n > self.prec_cap: + raise ValueError("n must be at most prec_cap") mpz_pow_ui(self.temp_m, self.prime.value, n) - sig_off() return &(self.temp_m) pow_comp_cache = {} From bfb13b1915c0bf67bf9b7a5b63db7cb5db2c9542 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 9 Jul 2014 20:42:16 +0200 Subject: [PATCH 009/184] made PowComputer.pow_mpz_t_tmp more robust to invalid/extreme inputs --- src/sage/rings/padics/pow_computer.pxd | 2 +- src/sage/rings/padics/pow_computer.pyx | 23 +++++++++++---- src/sage/rings/padics/pow_computer_ext.pxd | 2 +- src/sage/rings/padics/pow_computer_ext.pyx | 34 ++++++++-------------- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/sage/rings/padics/pow_computer.pxd b/src/sage/rings/padics/pow_computer.pxd index 41c93761c75..acc62c5febb 100644 --- a/src/sage/rings/padics/pow_computer.pxd +++ b/src/sage/rings/padics/pow_computer.pxd @@ -21,7 +21,7 @@ cdef class PowComputer_class(SageObject): cdef Integer pow_Integer(self, long n) cdef mpz_t* pow_mpz_t_top(self) - cdef mpz_t* pow_mpz_t_tmp(self, long n) + cdef mpz_t* pow_mpz_t_tmp(self, long n) except NULL cdef class PowComputer_base(PowComputer_class): cdef mpz_t* small_powers diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index 75804f132c1..6381099c02b 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -181,7 +181,7 @@ cdef class PowComputer_class(SageObject): raise ValueError, "result too big" return self.pow_Integer(mpz_get_ui(_n.value)) - cdef mpz_t* pow_mpz_t_tmp(self, long n): + cdef mpz_t* pow_mpz_t_tmp(self, long n) except NULL: """ Provides fast access to an mpz_t* pointing to self.prime^n. @@ -261,7 +261,7 @@ cdef class PowComputer_class(SageObject): """ cdef Integer _n = Integer(n) cdef Integer ans = PY_NEW(Integer) - mpz_set(ans.value, self.pow_mpz_t_tmp(mpz_get_ui(_n.value))[0]) + mpz_set(ans.value, self.pow_mpz_t_tmp(mpz_get_si(_n.value))[0]) return ans cdef mpz_t* pow_mpz_t_top(self): @@ -493,7 +493,7 @@ cdef class PowComputer_base(PowComputer_class): """ return &self.top_power - cdef mpz_t* pow_mpz_t_tmp(self, long n): + cdef mpz_t* pow_mpz_t_tmp(self, long n) except NULL: """ Computes self.prime^n. @@ -502,14 +502,27 @@ cdef class PowComputer_base(PowComputer_class): sage: PC = PowComputer(3, 5, 10) sage: PC._pow_mpz_t_tmp_test(4) 81 + sage: PC._pow_mpz_t_tmp_test(-1) + Traceback (most recent call last): + ... + ValueError: n must be non-negative + """ if n <= self.cache_limit: return &(self.small_powers[n]) if n == self.prec_cap: return &(self.top_power) - if n > self.prec_cap: - raise ValueError("n must be at most prec_cap") + if n < 0: + raise ValueError("n must be non-negative") + # n may exceed self.prec_cap. Very large values can, however, lead to + # out-of-memory situations in the following computation. This + # sig_on()/sig_off() prevents sage from crashing in such cases. + # It does not have a significant impact on performance. For small + # values of n the powers are taken from self.small_powers, for large + # values, the computation dominates the cost of the sig_on()/sig_off(). + sig_on() mpz_pow_ui(self.temp_m, self.prime.value, n) + sig_off() return &(self.temp_m) pow_comp_cache = {} diff --git a/src/sage/rings/padics/pow_computer_ext.pxd b/src/sage/rings/padics/pow_computer_ext.pxd index 93ba3988ca8..606885d4855 100644 --- a/src/sage/rings/padics/pow_computer_ext.pxd +++ b/src/sage/rings/padics/pow_computer_ext.pxd @@ -16,7 +16,7 @@ cdef class PowComputer_ext(PowComputer_class): cdef object _ext_type cdef object _prec_type - cdef ZZ_c* pow_ZZ_tmp(self, long n) + cdef ZZ_c* pow_ZZ_tmp(self, long n) except NULL cdef ZZ_c* pow_ZZ_top(self) cdef void cleanup_ext(self) diff --git a/src/sage/rings/padics/pow_computer_ext.pyx b/src/sage/rings/padics/pow_computer_ext.pyx index b46b86ecf71..b04a31479d9 100644 --- a/src/sage/rings/padics/pow_computer_ext.pyx +++ b/src/sage/rings/padics/pow_computer_ext.pyx @@ -585,7 +585,7 @@ cdef class PowComputer_ext(PowComputer_class): mpz_clear(self.temp_m) ZZ_destruct(&self.temp_z) - cdef mpz_t* pow_mpz_t_tmp(self, long n): + cdef mpz_t* pow_mpz_t_tmp(self, long n) except NULL: """ Provides fast access to an mpz_t* pointing to self.prime^n. @@ -608,34 +608,25 @@ cdef class PowComputer_ext(PowComputer_class): sage: PC._pow_mpz_t_tmp_test(4) #indirect doctest 625 """ - # READ THE DOCSTRING if n < 0: - # Exception will be ignored by Cython - raise ValueError("n must be positive") + raise ValueError("n must be non-negative") if n <= self.cache_limit: ZZ_to_mpz(&self.temp_m, &(self.small_powers[n])) elif n == self.prec_cap: ZZ_to_mpz(&self.temp_m, &self.top_power) else: + sig_on() + # n may exceed self.prec_cap. Very large values can, however, lead to + # out-of-memory situations in the following computation. This + # sig_on()/sig_off() prevents sage from crashing in such cases. + # It does not have a significant impact on performance. For small + # values of n the powers are taken from self.small_powers, for large + # values, the computation dominates the cost of the sig_on()/sig_off(). mpz_pow_ui(self.temp_m, self.prime.value, n) + sig_off() return &self.temp_m - #def _pow_mpz_t_tmp_test(self, n): - # """ - # Test for the pow_mpz_t_tmp function. See that function's documentation for important warnings. - # - # EXAMPLES: - # sage: PC = PowComputer_ext_maker(5, 10, 10, 20, False, ntl.ZZ_pX([-5, 0, 1], 5^10), 'small', 'e',ntl.ZZ_pX([1],5^10)) - # sage: PC._pow_mpz_t_tmp_test(4) #indirect doctest - # 625 - # """ - # cdef Integer _n = Integer(n) - # if _n < 0: raise ValueError - # cdef Integer ans = PY_NEW(Integer) - # mpz_set(ans.value, self.pow_mpz_t_tmp(mpz_get_si(_n.value))[0]) - # return ans - - cdef ZZ_c* pow_ZZ_tmp(self, long n): + cdef ZZ_c* pow_ZZ_tmp(self, long n) except NULL: """ Provides fast access to a ZZ_c* pointing to self.prime^n. @@ -656,8 +647,7 @@ cdef class PowComputer_ext(PowComputer_class): 625 """ if n < 0: - # Exception will be ignored by Cython - raise ValueError("n must be positive") + raise ValueError("n must be non-negative") if n <= self.cache_limit: return &(self.small_powers[n]) if n == self.prec_cap: From 9cd2d5d20b88629f8b77995090953f434c0efb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 11 Aug 2015 13:41:52 -0500 Subject: [PATCH 010/184] Fixup the preceding merge. The conflicts in the following two files were resolved incorrectly: src/sage/rings/padics/pow_computer.pyx src/sage/rings/padics/pow_computer_ext.pyx --- src/sage/rings/padics/pow_computer.pyx | 10 ++++++++++ src/sage/rings/padics/pow_computer_ext.pyx | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index b788235d937..c145f2c74ab 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -512,7 +512,17 @@ cdef class PowComputer_base(PowComputer_class): return self.small_powers[n] if n == self.prec_cap: return self.top_power + if n < 0: + raise ValueError("n must be non-negative") + # n may exceed self.prec_cap. Very large values can, however, lead to + # out-of-memory situations in the following computation. This + # sig_on()/sig_off() prevents sage from crashing in such cases. + # It does not have a significant impact on performance. For small + # values of n the powers are taken from self.small_powers, for large + # values, the computation dominates the cost of the sig_on()/sig_off(). + sig_on() mpz_pow_ui(self.temp_m, self.prime.value, n) + sig_off() return self.temp_m pow_comp_cache = {} diff --git a/src/sage/rings/padics/pow_computer_ext.pyx b/src/sage/rings/padics/pow_computer_ext.pyx index 7e2764e8396..2e2889ec4a1 100644 --- a/src/sage/rings/padics/pow_computer_ext.pyx +++ b/src/sage/rings/padics/pow_computer_ext.pyx @@ -623,9 +623,10 @@ cdef class PowComputer_ext(PowComputer_class): # values of n the powers are taken from self.small_powers, for large # values, the computation dominates the cost of the sig_on()/sig_off(). mpz_pow_ui(self.temp_m, self.prime.value, n) + sig_off() return self.temp_m - cdef ZZ_c* pow_ZZ_tmp(self, long n): + cdef ZZ_c* pow_ZZ_tmp(self, long n) except NULL: """ Provides fast access to a ZZ_c* pointing to self.prime^n. From 9ca8df8885d967a6b36bb6cb89fb79484cc25563 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 27 Mar 2016 11:53:03 +0200 Subject: [PATCH 011/184] Generic function change_precision --- src/sage/rings/padics/local_generic.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 8ee98a1b41d..7cd5123cebe 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -164,6 +164,17 @@ def _latex_(self): """ return self._repr_(do_latex = True) + def change_precision(self, prec): + (functor, ring) = self.construction() + if hasattr(functor, "prec"): + functor.prec = prec + else: + try: + ring = ring.change_precision(prec) + except AttributeError: + raise NotImplementedError + return functor(ring) + def precision_cap(self): r""" Returns the precision cap for ``self``. From f42ea43a88329352349c8b7b6831afccc658ec92 Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Mon, 20 Feb 2017 19:18:32 -0700 Subject: [PATCH 012/184] Transpose databases of small reflexive polytopes --- build/pkgs/polytopes_db/SPKG.txt | 18 ------------------ build/pkgs/polytopes_db/checksums.ini | 6 +++--- build/pkgs/polytopes_db/package-version.txt | 2 +- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/build/pkgs/polytopes_db/SPKG.txt b/build/pkgs/polytopes_db/SPKG.txt index 12375de91bd..3f2aae989ff 100644 --- a/build/pkgs/polytopes_db/SPKG.txt +++ b/build/pkgs/polytopes_db/SPKG.txt @@ -19,21 +19,3 @@ GPL == Dependencies == None - -== Changelog == - -=== polytopes_db-20120220 (Volker Braun 2012 Feb 20) === -* #12553: Added PALP binary data file version. In the future, - this will be the only data file format. - -=== polytopes_db-20100210.p2 (R. Andrew Ohana, 2012-05-17) === -* #13123: Move SAGE_DATA to SAGE_LOCAL/share - -=== polytopes_db-20100210.p1 (Keshav Kini, 2012-03-18) === -* #12694: Normalize directory structure - -=== polytopes_db-20100210 (?, ?) === -* Binary files replaced by text ones. - -=== polytopes_db-20080430 (?, ?) === -* 2008-04-30 Original version. diff --git a/build/pkgs/polytopes_db/checksums.ini b/build/pkgs/polytopes_db/checksums.ini index c662abac41b..ceb452697f3 100644 --- a/build/pkgs/polytopes_db/checksums.ini +++ b/build/pkgs/polytopes_db/checksums.ini @@ -1,4 +1,4 @@ tarball=polytopes_db-VERSION.tar.bz2 -sha1=4d83146f9739e1c907b8592f620f97b7eb02bb3d -md5=99bd633f4dfcdfef33c22b0b1b33919a -cksum=3316537984 +sha1=6a9d12740588ea5c0efc0e82776d8386e7887b47 +md5=64c5fba9568891a2dbde5d433ce668e2 +cksum=3276110922 diff --git a/build/pkgs/polytopes_db/package-version.txt b/build/pkgs/polytopes_db/package-version.txt index 2bf1d04f5a3..7c2a5aa9867 100644 --- a/build/pkgs/polytopes_db/package-version.txt +++ b/build/pkgs/polytopes_db/package-version.txt @@ -1 +1 @@ -20120220 +20170220 From 7e6d6c6111fb01967347bc886cea43b0e4657fd7 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sat, 11 Mar 2017 20:17:42 +0100 Subject: [PATCH 013/184] Fix doc builder logic --- src/sage_setup/docbuild/__init__.py | 155 ++++++++--------------- src/sage_setup/docbuild/ext/multidocs.py | 5 +- 2 files changed, 56 insertions(+), 104 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index c155b4e629b..6541fa8ce12 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -683,34 +683,17 @@ def _wrapper(self, build_type, *args, **kwds): This is the wrapper around the builder_helper methods that goes through and makes sure things are up to date. """ - # Delete the auto-generated .rst files, if the inherited - # and/or underscored members options have changed. - global options - inherit_prev = self.get_cache().get('option_inherited') - underscore_prev = self.get_cache().get('option_underscore') - if (inherit_prev is None or inherit_prev != options.inherited or - underscore_prev is None or underscore_prev != options.underscore): - logger.info("Detected change(s) in inherited and/or underscored members option(s).") - self.clean_auto() - self.get_cache.clear_cache() - # After "sage -clone", refresh the .rst file mtimes in # environment.pickle. if options.update_mtimes: logger.info("Checking for .rst file mtimes to update...") self.update_mtimes() - #Update the .rst files for modified Python modules - logger.info("Updating .rst files with modified modules...") - for module_name in self.get_modified_modules(): - self.write_auto_rest_file(module_name.replace(os.path.sep, '.')) - - #Write the .rst files for newly included modules - logger.info("Writing .rst files for newly-included modules...") - for module_name in self.get_newly_included_modules(save=True): + # Write .rst files for new and updated modules. + for module_name in self.get_new_and_updated_modules(): self.write_auto_rest_file(module_name) - #Copy over the custom .rst files from _sage + # Copy over the custom .rst files from _sage _sage = os.path.join(self.dir, '_sage') if os.path.exists(_sage): logger.info("Copying over custom .rst files from %s ...", _sage) @@ -718,52 +701,6 @@ def _wrapper(self, build_type, *args, **kwds): getattr(DocBuilder, build_type)(self, *args, **kwds) - def cache_filename(self): - """ - Returns the filename where the pickle of the dictionary of - already generated ReST files is stored. - """ - return os.path.join(self._doctrees_dir(), 'reference.pickle') - - @cached_method - def get_cache(self): - """ - Retrieve the cache of already generated ReST files. If it - doesn't exist, then we just return an empty dictionary. If it - is corrupted, return an empty dictionary. - """ - filename = self.cache_filename() - if not os.path.exists(filename): - return {} - from six.moves import cPickle - file = open(self.cache_filename(), 'rb') - try: - cache = cPickle.load(file) - except Exception: - logger.debug("Cache file '%s' is corrupted; ignoring it..."% filename) - cache = {} - else: - logger.debug("Loaded .rst file cache: %s", filename) - finally: - file.close() - return cache - - def save_cache(self): - """ - Save the cache of already generated ReST files. - """ - cache = self.get_cache() - - global options - cache['option_inherited'] = options.inherited - cache['option_underscore'] = options.underscore - - from six.moves import cPickle - file = open(self.cache_filename(), 'wb') - cPickle.dump(cache, file) - file.close() - logger.debug("Saved .rst file cache: %s", self.cache_filename()) - def get_sphinx_environment(self): """ Returns the Sphinx environment for this project. @@ -840,7 +777,6 @@ def get_modified_modules(self): logger.info("Sphinx found %d modified modules", len(changed)) except OSError as err: logger.debug("Sphinx failed to determine modified modules: %s", err) - self.clean_auto() return for name in changed: # Only pay attention to files in a directory sage/... In @@ -880,28 +816,61 @@ def get_all_included_modules(self): for module in self.get_modules(filename): yield module - def get_newly_included_modules(self, save=False): + def get_new_and_updated_modules(self): """ - Returns an iterator for all modules that appear in the - toctrees that don't appear in the cache. + Return an iterator for all new and updated modules that appear in + the toctrees, and remove obsolete old modules. """ - cache = self.get_cache() - new_modules = 0 + env = self.get_sphinx_environment() + + new_modules = [] + updated_modules = [] + old_modules = [] for module in self.get_all_included_modules(): - if module not in cache: - cache[module] = True - new_modules += 1 + docname = module.replace('.', os.path.sep) + + if docname not in env.all_docs: + new_modules.append(module) + yield module + continue + + # get the timestamp of the rst doc for the module + mtime = env.all_docs[docname] + try: + __import__(module) + except ImportError as err: + logger.error("Warning: Could not import %s %s", module, err) + raise + newtime = os.path.getmtime(sys.modules[module].__file__) + + if newtime > mtime: + updated_modules.append(module) yield module - logger.info("Found %d newly included modules", new_modules) - if save: - self.save_cache() + else: # keep good old module + old_modules.append(module) + + removed_modules = [] + for docname in env.all_docs.keys(): + if docname.startswith('sage'): + module = docname.replace(os.path.sep, '.') + if module not in old_modules and module not in updated_modules: + try: + os.remove(os.path.join(self.dir, docname) + '.rst') + except OSError: # already removed + pass + logger.debug("Deleted auto-generated .rst file %s".format(docname)) + removed_modules.append(module) - def print_newly_included_modules(self): + logger.info("Found %d new modules", len(new_modules)) + logger.info("Found %d updated modules", len(updated_modules)) + logger.info("Removed %d obsolete modules", len(removed_modules)) + + def print_new_and_updated_modules(self): """ - Prints all of the modules that appear in the toctrees that - don't appear in the cache. + Print all the modules that appear in the toctrees that + are newly included or updated. """ - for module_name in self.get_newly_included_modules(): + for module_name in self.get_new_and_updated_modules(): print(module_name) def get_modules(self, filename): @@ -998,23 +967,6 @@ def write_auto_rest_file(self, module_name): outfile.close() - def clean_auto(self): - """ - Remove the cache file for the autogenerated files as well as - the files themselves. - """ - if os.path.exists(self.cache_filename()): - os.unlink(self.cache_filename()) - logger.debug("Deleted .rst cache file: %s", self.cache_filename()) - - import shutil - try: - shutil.rmtree(os.path.join(self.dir, 'sage')) - logger.debug("Deleted auto-generated .rst files in: %s", - os.path.join(self.dir, 'sage')) - except OSError: - pass - def get_unincluded_modules(self): """ Returns an iterator for all the modules in the Sage library @@ -1062,7 +1014,6 @@ def print_included_modules(self): for module_name in self.get_all_included_modules(): print(module_name) - class SingleFileBuilder(DocBuilder): """ This is the class used to build the documentation for a single @@ -1329,8 +1280,8 @@ def help_commands(name='all', s=u""): # To do: Generate the lists dynamically, using class attributes, # as with the Builders above. command_dict = { 'reference' : [ - 'print_included_modules', 'print_modified_modules (*)', - 'print_unincluded_modules', 'print_newly_included_modules (*)', + 'print_included_modules', 'print_modified_modules (*)', + 'print_unincluded_modules', 'print_new_and_updated_modules (*)', ] } for doc in command_dict: if name == 'all' or doc == name: diff --git a/src/sage_setup/docbuild/ext/multidocs.py b/src/sage_setup/docbuild/ext/multidocs.py index 63dbb712616..ed6f77268a9 100644 --- a/src/sage_setup/docbuild/ext/multidocs.py +++ b/src/sage_setup/docbuild/ext/multidocs.py @@ -262,13 +262,14 @@ def init_subdoc(app): # Master file with indexes computed by merging indexes: # Monkey patch index fetching to silence warning about broken index def load_indexer(docnames): - app.builder.info(bold('Skipping loading of indexes'), nonl=1) + app.builder.info(bold('skipping loading of indexes... '), nonl=1) app.builder.load_indexer = load_indexer else: app.info(bold("Compiling a sub-document")) - app.connect('env-updated', fetch_citation) app.connect('html-page-context', fix_path_html) + if not app.config.multidoc_first_pass: + app.connect('env-updated', fetch_citation) # Monkey patch copy_static_files to make a symlink to "../" def link_static_files(): From 839c5f23b9831f219ab35ba9a82652cfe6e5469b Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Mon, 13 Mar 2017 17:55:14 +0100 Subject: [PATCH 014/184] Quickfix for a bug in new code --- src/sage_setup/docbuild/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 6541fa8ce12..aa699b2a44c 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -822,6 +822,10 @@ def get_new_and_updated_modules(self): the toctrees, and remove obsolete old modules. """ env = self.get_sphinx_environment() + if env is None: + all_docs = {} + else: + all_docs = env.all_docs new_modules = [] updated_modules = [] @@ -829,13 +833,13 @@ def get_new_and_updated_modules(self): for module in self.get_all_included_modules(): docname = module.replace('.', os.path.sep) - if docname not in env.all_docs: + if docname not in all_docs: new_modules.append(module) yield module continue # get the timestamp of the rst doc for the module - mtime = env.all_docs[docname] + mtime = all_docs[docname] try: __import__(module) except ImportError as err: @@ -850,7 +854,7 @@ def get_new_and_updated_modules(self): old_modules.append(module) removed_modules = [] - for docname in env.all_docs.keys(): + for docname in all_docs.keys(): if docname.startswith('sage'): module = docname.replace(os.path.sep, '.') if module not in old_modules and module not in updated_modules: From 45d53c410b089d58d758c1bc7c9b3bb3c7a4d6b9 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Mon, 13 Mar 2017 18:18:53 +0100 Subject: [PATCH 015/184] Quickfix for the second bug in new code --- src/sage_setup/docbuild/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index aa699b2a44c..49ea2cead88 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -855,7 +855,7 @@ def get_new_and_updated_modules(self): removed_modules = [] for docname in all_docs.keys(): - if docname.startswith('sage'): + if docname.startswith('sage' + os.path.sep): module = docname.replace(os.path.sep, '.') if module not in old_modules and module not in updated_modules: try: From 70b80c9c98013cb466083818bfc131cd5f51047d Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Tue, 14 Mar 2017 19:55:33 +0100 Subject: [PATCH 016/184] Make options work again --- src/sage_setup/docbuild/__init__.py | 113 +++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 10 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 49ea2cead88..031ffbc4266 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -683,24 +683,89 @@ def _wrapper(self, build_type, *args, **kwds): This is the wrapper around the builder_helper methods that goes through and makes sure things are up to date. """ - # After "sage -clone", refresh the .rst file mtimes in + # Force regeneration of all modules if the inherited + # and/or underscored members options have changed. + global options + cache = self.get_cache() + force = False + try: + if (cache['option_inherited'] != options.inherited or + cache['option_underscore'] != options.underscore): + logger.info("Detected change(s) in inherited and/or underscored members option(s).") + force = True + except KeyError: + force = True + cache['option_inherited'] = options.inherited + cache['option_underscore'] = options.underscore + self.save_cache() + + # After "sage -clone", refresh the ReST file mtimes in # environment.pickle. if options.update_mtimes: - logger.info("Checking for .rst file mtimes to update...") + logger.info("Checking for ReST file mtimes to update...") self.update_mtimes() - # Write .rst files for new and updated modules. - for module_name in self.get_new_and_updated_modules(): - self.write_auto_rest_file(module_name) + if force: + # Write ReST files for all modules from scratch. + self.clean_auto() + for module in self.get_all_included_modules(): + self.write_auto_rest_file(module) + else: + # Write ReST files for new and updated modules. + for module in self.get_new_and_updated_modules(): + self.write_auto_rest_file(module) - # Copy over the custom .rst files from _sage + # Copy over the custom ReST files from _sage _sage = os.path.join(self.dir, '_sage') if os.path.exists(_sage): - logger.info("Copying over custom .rst files from %s ...", _sage) + logger.info("Copying over custom ReST files from %s ...", _sage) shutil.copytree(_sage, os.path.join(self.dir, 'sage')) getattr(DocBuilder, build_type)(self, *args, **kwds) + def cache_filename(self): + """ + Return the filename where the pickle of the reference cache + is stored. + """ + return os.path.join(self._doctrees_dir(), 'reference.pickle') + + @cached_method + def get_cache(self): + """ + Retrieve the reference cache which contains the options previously used + by the reference builder. + + If it doesn't exist, then we just return an empty dictionary. If it + is corrupted, return an empty dictionary. + """ + filename = self.cache_filename() + if not os.path.exists(filename): + return {} + from six.moves import cPickle + file = open(self.cache_filename(), 'rb') + try: + cache = cPickle.load(file) + except Exception: + logger.debug("Cache file '%s' is corrupted; ignoring it..."% filename) + cache = {} + else: + logger.debug("Loaded the reference cache: %s", filename) + finally: + file.close() + return cache + + def save_cache(self): + """ + Pickle the current reference cache for later retrieval. + """ + cache = self.get_cache() + from six.moves import cPickle + file = open(self.cache_filename(), 'wb') + cPickle.dump(cache, file) + file.close() + logger.debug("Saved the reference cache: %s", self.cache_filename()) + def get_sphinx_environment(self): """ Returns the Sphinx environment for this project. @@ -730,7 +795,7 @@ def update_mtimes(self): import time for doc in env.all_docs: env.all_docs[doc] = time.time() - logger.info("Updated %d .rst file mtimes", len(env.all_docs)) + logger.info("Updated %d ReST file mtimes", len(env.all_docs)) # This is the only place we need to save (as opposed to # load) Sphinx's pickle, so we do it right here. env_pickle = os.path.join(self._doctrees_dir(), @@ -816,7 +881,7 @@ def get_all_included_modules(self): for module in self.get_modules(filename): yield module - def get_new_and_updated_modules(self): + def get_new_and_updated_modules(self, save=False): """ Return an iterator for all new and updated modules that appear in the toctrees, and remove obsolete old modules. @@ -862,13 +927,29 @@ def get_new_and_updated_modules(self): os.remove(os.path.join(self.dir, docname) + '.rst') except OSError: # already removed pass - logger.debug("Deleted auto-generated .rst file %s".format(docname)) + logger.debug("Deleted auto-generated ReST file %s".format(docname)) removed_modules.append(module) logger.info("Found %d new modules", len(new_modules)) logger.info("Found %d updated modules", len(updated_modules)) logger.info("Removed %d obsolete modules", len(removed_modules)) + def get_newly_included_modules(self, save=False): + """ + Returns an iterator for all modules that appear in the + toctrees that don't appear in the cache. + """ + cache = self.get_cache() + new_modules = 0 + for module in self.get_all_included_modules(): + if module not in cache: + cache[module] = True + new_modules += 1 + yield module + logger.info("Found %d newly included modules", new_modules) + if save: + self.save_cache() + def print_new_and_updated_modules(self): """ Print all the modules that appear in the toctrees that @@ -971,6 +1052,18 @@ def write_auto_rest_file(self, module_name): outfile.close() + def clean_auto(self): + """ + Remove all autogenerated ReST files. + """ + import shutil + try: + shutil.rmtree(os.path.join(self.dir, 'sage')) + logger.debug("Deleted auto-generated ReST files in: %s", + os.path.join(self.dir, 'sage')) + except OSError: + pass + def get_unincluded_modules(self): """ Returns an iterator for all the modules in the Sage library From 0adc50e2b26b40d68c53b8cb8878c684bbc917db Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 15 Mar 2017 02:47:15 +0100 Subject: [PATCH 017/184] Correct ReST to reST --- src/sage_setup/docbuild/__init__.py | 34 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 031ffbc4266..c430efcec27 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -653,11 +653,11 @@ def get_all_documents(self, refdir): class ReferenceSubBuilder(DocBuilder): """ This class builds sub-components of the reference manual. It is - resposible for making sure the auto generated ReST files for the + resposible for making sure the auto generated reST files for the Sage library are up to date. When building any output, we must first go through and check - to see if we need to update any of the autogenerated ReST + to see if we need to update any of the autogenerated reST files. There are two cases where this would happen: 1. A new module gets added to one of the toctrees. @@ -699,26 +699,26 @@ def _wrapper(self, build_type, *args, **kwds): cache['option_underscore'] = options.underscore self.save_cache() - # After "sage -clone", refresh the ReST file mtimes in + # After "sage -clone", refresh the reST file mtimes in # environment.pickle. if options.update_mtimes: - logger.info("Checking for ReST file mtimes to update...") + logger.info("Checking for reST file mtimes to update...") self.update_mtimes() if force: - # Write ReST files for all modules from scratch. + # Write reST files for all modules from scratch. self.clean_auto() for module in self.get_all_included_modules(): self.write_auto_rest_file(module) else: - # Write ReST files for new and updated modules. + # Write reST files for new and updated modules. for module in self.get_new_and_updated_modules(): self.write_auto_rest_file(module) - # Copy over the custom ReST files from _sage + # Copy over the custom reST files from _sage _sage = os.path.join(self.dir, '_sage') if os.path.exists(_sage): - logger.info("Copying over custom ReST files from %s ...", _sage) + logger.info("Copying over custom reST files from %s ...", _sage) shutil.copytree(_sage, os.path.join(self.dir, 'sage')) getattr(DocBuilder, build_type)(self, *args, **kwds) @@ -787,7 +787,7 @@ class Foo(object): def update_mtimes(self): """ - Updates the modification times for ReST files in the Sphinx + Updates the modification times for reST files in the Sphinx environment for this project. """ env = self.get_sphinx_environment() @@ -795,7 +795,7 @@ def update_mtimes(self): import time for doc in env.all_docs: env.all_docs[doc] = time.time() - logger.info("Updated %d ReST file mtimes", len(env.all_docs)) + logger.info("Updated %d reST file mtimes", len(env.all_docs)) # This is the only place we need to save (as opposed to # load) Sphinx's pickle, so we do it right here. env_pickle = os.path.join(self._doctrees_dir(), @@ -927,7 +927,7 @@ def get_new_and_updated_modules(self, save=False): os.remove(os.path.join(self.dir, docname) + '.rst') except OSError: # already removed pass - logger.debug("Deleted auto-generated ReST file %s".format(docname)) + logger.debug("Deleted auto-generated reST file %s".format(docname)) removed_modules.append(module) logger.info("Found %d new modules", len(new_modules)) @@ -960,8 +960,8 @@ def print_new_and_updated_modules(self): def get_modules(self, filename): """ - Given a filename for a ReST file, return an iterator for - all of the autogenerated ReST files that it includes. + Given a filename for a reST file, return an iterator for + all of the autogenerated reST files that it includes. """ #Create the regular expression used to detect an autogenerated file auto_re = re.compile('^\s*(..\/)*(sage(nb)?\/[\w\/]*)\s*$') @@ -1014,7 +1014,7 @@ def auto_rest_filename(self, module_name): def write_auto_rest_file(self, module_name): """ - Writes the autogenerated ReST file for module_name. + Writes the autogenerated reST file for module_name. """ if not module_name.startswith('sage'): return @@ -1054,12 +1054,12 @@ def write_auto_rest_file(self, module_name): def clean_auto(self): """ - Remove all autogenerated ReST files. + Remove all autogenerated reST files. """ import shutil try: shutil.rmtree(os.path.join(self.dir, 'sage')) - logger.debug("Deleted auto-generated ReST files in: %s", + logger.debug("Deleted auto-generated reST files in: %s", os.path.join(self.dir, 'sage')) except OSError: pass @@ -1538,7 +1538,7 @@ def setup_parser(): help="pass comma-separated OPTS to sphinx-build") advanced.add_option("-U", "--update-mtimes", dest="update_mtimes", default=False, action="store_true", - help="before building reference manual, update modification times for auto-generated ReST files") + help="before building reference manual, update modification times for auto-generated reST files") advanced.add_option("-k", "--keep-going", dest="keep_going", default=False, action="store_true", help="Do not abort on errors but continue as much as possible after an error") From 8fd3b684c270b941e87246c3174953d8d95fddb7 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 15 Mar 2017 04:34:07 +0100 Subject: [PATCH 018/184] Minor fixes in docstrings --- src/sage_setup/docbuild/__init__.py | 6 +++--- src/sage_setup/docbuild/ext/sage_autodoc.py | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index c430efcec27..db24f25cb76 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -110,7 +110,7 @@ def f(self, *args, **kwds): logger.warning("LaTeX file written to {}".format(output_dir)) else: logger.warning( - "Build finished. The built documents can be found in {}". + "Build finished. The built documents can be found in {}". format(output_dir)) f.is_output_format = True @@ -1548,8 +1548,8 @@ def setup_parser(): def setup_logger(verbose=1, color=True): """ - Sets up and returns a Python Logger instance for the Sage - documentation builder. The optional argument sets logger's level + Set up and return a Python Logger instance for the Sage + documentation builder. The optional argument sets logger's level and message format. """ # Set up colors. Adapted from sphinx.cmdline. diff --git a/src/sage_setup/docbuild/ext/sage_autodoc.py b/src/sage_setup/docbuild/ext/sage_autodoc.py index 32b045c6bf8..4eda946422b 100644 --- a/src/sage_setup/docbuild/ext/sage_autodoc.py +++ b/src/sage_setup/docbuild/ext/sage_autodoc.py @@ -235,7 +235,8 @@ def process(app, what_, name, obj, options, lines): def between(marker, what=None, keepempty=False, exclude=False): - """Return a listener that either keeps, or if *exclude* is True excludes, + """ + Return a listener that either keeps, or if *exclude* is True excludes, lines between lines that match the *marker* regular expression. If no line matches, the resulting docstring would be empty, so no change will be made unless *keepempty* is true. @@ -269,7 +270,8 @@ def process(app, what_, name, obj, options, lines): def format_annotation(annotation): - """Return formatted representation of a type annotation. + """ + Return formatted representation of a type annotation. Show qualified names for types and additional details for types from the ``typing`` module. @@ -329,7 +331,8 @@ def format_annotation(annotation): def formatargspec(function, args, varargs=None, varkw=None, defaults=None, kwonlyargs=(), kwonlydefaults={}, annotations={}): - """Return a string representation of an ``inspect.FullArgSpec`` tuple. + """ + Return a string representation of an ``inspect.FullArgSpec`` tuple. An enhanced version of ``inspect.formatargspec()`` that handles typing annotations better. From 15e9ec01ac175b589e6bdb52629ce6bbf797269b Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 15 Mar 2017 11:27:20 +0100 Subject: [PATCH 019/184] Suppress deprecation warnings --- src/sage_setup/docbuild/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index db24f25cb76..f4dc66d2e28 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -14,7 +14,8 @@ from __future__ import print_function from six.moves import range -import logging, optparse, os, shutil, subprocess, sys, re +import optparse, os, shutil, subprocess, sys, re +import logging, warnings import sphinx.cmdline import sphinx.util.console @@ -903,10 +904,13 @@ def get_new_and_updated_modules(self, save=False): yield module continue - # get the timestamp of the rst doc for the module + # get the modification timestamp of the reST doc for the module mtime = all_docs[docname] try: - __import__(module) + with warnings.catch_warnings(): + # primarily intended to ignore deprecation warnings + warnings.simplefilter("ignore") + __import__(module) except ImportError as err: logger.error("Warning: Could not import %s %s", module, err) raise From bbc8e0d6f461f753c224d808898d2ca2afaa7cd1 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 16 Mar 2017 01:28:54 +0100 Subject: [PATCH 020/184] Revert "Minor fixes in docstrings" This reverts commit 8fd3b684c270b941e87246c3174953d8d95fddb7. --- src/sage_setup/docbuild/__init__.py | 6 +++--- src/sage_setup/docbuild/ext/sage_autodoc.py | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index f4dc66d2e28..1d5175059c0 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -111,7 +111,7 @@ def f(self, *args, **kwds): logger.warning("LaTeX file written to {}".format(output_dir)) else: logger.warning( - "Build finished. The built documents can be found in {}". + "Build finished. The built documents can be found in {}". format(output_dir)) f.is_output_format = True @@ -1552,8 +1552,8 @@ def setup_parser(): def setup_logger(verbose=1, color=True): """ - Set up and return a Python Logger instance for the Sage - documentation builder. The optional argument sets logger's level + Sets up and returns a Python Logger instance for the Sage + documentation builder. The optional argument sets logger's level and message format. """ # Set up colors. Adapted from sphinx.cmdline. diff --git a/src/sage_setup/docbuild/ext/sage_autodoc.py b/src/sage_setup/docbuild/ext/sage_autodoc.py index 4eda946422b..32b045c6bf8 100644 --- a/src/sage_setup/docbuild/ext/sage_autodoc.py +++ b/src/sage_setup/docbuild/ext/sage_autodoc.py @@ -235,8 +235,7 @@ def process(app, what_, name, obj, options, lines): def between(marker, what=None, keepempty=False, exclude=False): - """ - Return a listener that either keeps, or if *exclude* is True excludes, + """Return a listener that either keeps, or if *exclude* is True excludes, lines between lines that match the *marker* regular expression. If no line matches, the resulting docstring would be empty, so no change will be made unless *keepempty* is true. @@ -270,8 +269,7 @@ def process(app, what_, name, obj, options, lines): def format_annotation(annotation): - """ - Return formatted representation of a type annotation. + """Return formatted representation of a type annotation. Show qualified names for types and additional details for types from the ``typing`` module. @@ -331,8 +329,7 @@ def format_annotation(annotation): def formatargspec(function, args, varargs=None, varkw=None, defaults=None, kwonlyargs=(), kwonlydefaults={}, annotations={}): - """ - Return a string representation of an ``inspect.FullArgSpec`` tuple. + """Return a string representation of an ``inspect.FullArgSpec`` tuple. An enhanced version of ``inspect.formatargspec()`` that handles typing annotations better. From 1e39367abfd82bc4e41669a80acb8a5d3bfdce21 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 16 Mar 2017 01:33:47 +0100 Subject: [PATCH 021/184] Just one minor fix in a log statement --- src/sage_setup/docbuild/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 1d5175059c0..2e4e10e3205 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -111,7 +111,7 @@ def f(self, *args, **kwds): logger.warning("LaTeX file written to {}".format(output_dir)) else: logger.warning( - "Build finished. The built documents can be found in {}". + "Build finished. The built documents can be found in {}". format(output_dir)) f.is_output_format = True From fb9c5a814ec67b27a1fb481c5400bb3f1140e2d5 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 16 Mar 2017 01:55:23 +0100 Subject: [PATCH 022/184] Change module to module_name for consistency --- src/sage_setup/docbuild/__init__.py | 34 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 2e4e10e3205..a46272d0453 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -709,12 +709,12 @@ def _wrapper(self, build_type, *args, **kwds): if force: # Write reST files for all modules from scratch. self.clean_auto() - for module in self.get_all_included_modules(): - self.write_auto_rest_file(module) + for module_name in self.get_all_included_modules(): + self.write_auto_rest_file(module_name) else: # Write reST files for new and updated modules. - for module in self.get_new_and_updated_modules(): - self.write_auto_rest_file(module) + for module_name in self.get_new_and_updated_modules(): + self.write_auto_rest_file(module_name) # Copy over the custom reST files from _sage _sage = os.path.join(self.dir, '_sage') @@ -896,12 +896,12 @@ def get_new_and_updated_modules(self, save=False): new_modules = [] updated_modules = [] old_modules = [] - for module in self.get_all_included_modules(): - docname = module.replace('.', os.path.sep) + for module_name in self.get_all_included_modules(): + docname = module_name.replace('.', os.path.sep) if docname not in all_docs: - new_modules.append(module) - yield module + new_modules.append(module_name) + yield module_name continue # get the modification timestamp of the reST doc for the module @@ -910,29 +910,29 @@ def get_new_and_updated_modules(self, save=False): with warnings.catch_warnings(): # primarily intended to ignore deprecation warnings warnings.simplefilter("ignore") - __import__(module) + __import__(module_name) except ImportError as err: - logger.error("Warning: Could not import %s %s", module, err) + logger.error("Warning: Could not import %s %s", module_name, err) raise - newtime = os.path.getmtime(sys.modules[module].__file__) + newtime = os.path.getmtime(sys.modules[module_name].__file__) if newtime > mtime: - updated_modules.append(module) - yield module + updated_modules.append(module_name) + yield module_name else: # keep good old module - old_modules.append(module) + old_modules.append(module_name) removed_modules = [] for docname in all_docs.keys(): if docname.startswith('sage' + os.path.sep): - module = docname.replace(os.path.sep, '.') - if module not in old_modules and module not in updated_modules: + module_name = docname.replace(os.path.sep, '.') + if not (module_name in old_modules or module_name in updated_modules): try: os.remove(os.path.join(self.dir, docname) + '.rst') except OSError: # already removed pass logger.debug("Deleted auto-generated reST file %s".format(docname)) - removed_modules.append(module) + removed_modules.append(module_name) logger.info("Found %d new modules", len(new_modules)) logger.info("Found %d updated modules", len(updated_modules)) From 1993e823663a192a30dbc9fb010977baf5959079 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 29 Jul 2016 11:28:04 +0200 Subject: [PATCH 023/184] Improve RealNumber.str() --- src/doc/en/prep/Quickstarts/NumAnalysis.rst | 19 +-- src/sage/interfaces/tides.py | 32 ++-- src/sage/rings/complex_mpc.pyx | 22 +-- src/sage/rings/complex_number.pyx | 52 +++--- src/sage/rings/rational.pyx | 4 +- src/sage/rings/real_mpfi.pyx | 10 +- src/sage/rings/real_mpfr.pyx | 173 ++++++++++++-------- 7 files changed, 179 insertions(+), 133 deletions(-) diff --git a/src/doc/en/prep/Quickstarts/NumAnalysis.rst b/src/doc/en/prep/Quickstarts/NumAnalysis.rst index 69e27e51b9b..b02d23c80b6 100644 --- a/src/doc/en/prep/Quickstarts/NumAnalysis.rst +++ b/src/doc/en/prep/Quickstarts/NumAnalysis.rst @@ -32,19 +32,16 @@ Basic Analysis sage: ring=RealField(3) To print the actual number (without rounding off the last few imprecise -digits to only display correct digits), call the ``.str()`` method with -the option ``truncate=False``. - -:: +digits to only display correct digits), call the ``.str()`` method:: sage: print(ring('1').nextabove()) 1.2 :: - sage: print(ring('1').nextabove().str(truncate=False)) + sage: print(ring('1').nextabove().str()) 1.2 - sage: print(ring('1').nextbelow().str(truncate=False)) + sage: print(ring('1').nextbelow().str()) 0.88 Let's change our precision. @@ -52,9 +49,9 @@ Let's change our precision. :: sage: ring=RealField(20) - sage: print(ring('1').nextabove().str(truncate=False)) + sage: print(ring('1').nextabove().str()) 1.0000019 - sage: print(ring('1').nextbelow().str(truncate=False)) + sage: print(ring('1').nextbelow().str()) 0.99999905 You can also specify the rounding mode. @@ -66,17 +63,17 @@ You can also specify the rounding mode. :: - sage: ring(1/9).str(truncate=False) + sage: ring(1/9).str() '0.11111116' :: - sage: ringup(1/9).str(truncate=False) + sage: ringup(1/9).str() '0.13' :: - sage: ringdown(1/9).str(truncate=False) + sage: ringdown(1/9).str() '0.10' :: diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py index e2f6a3a24a6..07b1db8b457 100644 --- a/src/sage/interfaces/tides.py +++ b/src/sage/interfaces/tides.py @@ -502,14 +502,14 @@ def genfiles_mintides(integrator, driver, f, ics, initial, final, delta, aa = 'XX[{}]'.format(l0.index(str(a))+len(var)) else: consta=True - aa = RR(a).str(truncate=False) + aa = RR(a).str() if str(b) in lv: bb = 'XX[{}]'.format(lv.index(str(b))) elif str(b) in l0: bb = 'XX[{}]'.format(l0.index(str(b))+len(var)) else: constb = True - bb = RR(b).str(truncate=False) + bb = RR(b).str() if consta: oper += '_c' if not oper=='div': @@ -626,12 +626,12 @@ def genfiles_mintides(integrator, driver, f, ics, initial, final, delta, """%(n-1) outfile.write(auxstring) for i in range(len(ics)): - outfile.write('\tv[{}] = {} ; \n'.format(i, RR(ics[i]).str(truncate=False))) - outfile.write('\ttini = {} ;\n'.format(RR(initial).str(truncate=False))) - outfile.write('\ttend = {} ;\n'.format(RR(final).str(truncate=False))) - outfile.write('\tdt = {} ;\n'.format(RR(delta).str(truncate=False))) - outfile.write('\ttolrel = {} ;\n'.format(RR(tolrel).str(truncate=False))) - outfile.write('\ttolabs = {} ;\n'.format(RR(tolabs).str(truncate=False))) + outfile.write('\tv[{}] = {} ; \n'.format(i, RR(ics[i]).str())) + outfile.write('\ttini = {} ;\n'.format(RR(initial).str())) + outfile.write('\ttend = {} ;\n'.format(RR(final).str())) + outfile.write('\tdt = {} ;\n'.format(RR(delta).str())) + outfile.write('\ttolrel = {} ;\n'.format(RR(tolrel).str())) + outfile.write('\ttolabs = {} ;\n'.format(RR(tolabs).str())) outfile.write('\textern char ofname[500];') outfile.write('\tstrcpy(ofname, "'+ output +'");\n') outfile.write('\tminc_tides(v,VARS,p,PARS,tini,tend,dt,tolrel,tolabs);\n') @@ -792,7 +792,7 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, aa = 'par[{}]'.format(lp.index(sa)) else: consta=True - aa = RR(a).str(truncate=False) + aa = RR(a).str() if sb in lv: bb = 'var[{}]'.format(lv.index(sb)) elif sb in l0: @@ -801,7 +801,7 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, bb = 'par[{}]'.format(lp.index(sb)) else: constb=True - bb = RR(b).str(truncate=False) + bb = RR(b).str() if consta: oper += '_c' if not oper=='div': @@ -933,24 +933,24 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, outfile.write('\tfor(i=0; iy._parent).rnd) return y - def str(self, int base=10, int truncate=True): + def str(self, base=10, **kwds): """ Return a string of ``self``. INPUT: - - ``base`` -- base for output + - ``base`` -- (default: 10) base for output - - ``truncate`` -- if ``True``, round off the last digits in printing - to lessen confusing base-2 roundoff issues. + - ``**kwds`` -- other arguments to pass to the ``str()`` + method of the real numbers in the real and imaginary parts. EXAMPLES:: sage: MPC = MPComplexField(64) sage: z = MPC(-4, 3)/7 sage: z.str() - '-0.571428571428571429 + 0.428571428571428571*I' + '-0.571428571428571428564 + 0.428571428571428571436*I' sage: z.str(16) '-0.92492492492492490 + 0.6db6db6db6db6db70*I' sage: z.str(truncate=True) '-0.571428571428571429 + 0.428571428571428571*I' - sage: z.str(2, True) + sage: z.str(2) '-0.1001001001001001001001001001001001001001001001001001001001001001 + 0.01101101101101101101101101101101101101101101101101101101101101110*I' """ s = "" if self.real() != 0: - s = self.real().str(base, truncate=truncate) + s = self.real().str(base, **kwds) if self.imag() != 0: if mpfr_signbit(self.value.im): - s += ' - ' + (-self.imag()).str(base, truncate=truncate) + '*I' + s += ' - ' + (-self.imag()).str(base, **kwds) + '*I' else: if s: s += ' + ' - s += self.imag().str(base, truncate=truncate) + '*I' + s += self.imag().str(base, **kwds) + '*I' if not s: return "0" return s diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index c0b0ed25b72..606559dbd48 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -219,7 +219,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: s1 == CC(gp(s1)) True """ - return self.str(truncate=False) + return self.str() def _maxima_init_(self, I=None): """ @@ -233,7 +233,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: CC(.5 + I)._maxima_init_() '0.50000000000000000 + 1.0000000000000000*%i' """ - return self.str(truncate=False, istr='%i') + return self.str(istr='%i') @property def __array_interface__(self): @@ -365,7 +365,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: a._repr_() '2.00000000000000 + 1.00000000000000*I' """ - return self.str(10) + return self.str(truncate=True) def __hash__(self): """ @@ -458,25 +458,27 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): """ self._multiplicative_order = integer.Integer(n) - def str(self, base=10, truncate=True, istr='I'): + def str(self, base=10, istr='I', **kwds): r""" Return a string representation of ``self``. INPUT: - - ``base`` -- (Default: 10) The base to use for printing + - ``base`` -- (default: 10) base for output - - ``truncate`` -- (Default: ``True``) Whether to print fewer - digits than are available, to mask errors in the last bits. + - ``istr`` -- (default: ``I``) String representation of the complex unit - - ``istr`` -- (Default: ``I``) String representation of the complex unit + - ``**kwds`` -- other arguments to pass to the ``str()`` + method of the real numbers in the real and imaginary parts. EXAMPLES:: sage: a = CC(pi + I*e) - sage: a.str() + sage: a + 3.14159265358979 + 2.71828182845905*I + sage: a.str(truncate=True) '3.14159265358979 + 2.71828182845905*I' - sage: a.str(truncate=False) + sage: a.str() '3.1415926535897931 + 2.7182818284590451*I' sage: a.str(base=2) '11.001001000011111101101010100010001000010110100011000 + 10.101101111110000101010001011000101000101011101101001*I' @@ -489,23 +491,23 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: CC(0) 0.000000000000000 sage: CC.0.str(istr='%i') - '1.00000000000000*%i' + '1.0000000000000000*%i' """ s = "" - if self.real() != 0: - s = self.real().str(base, truncate=truncate) - if self.imag() != 0: - y = self.imag() - if s!="": + if self.real(): + s = self.real().str(base, **kwds) + if self.imag(): + y = self.imag() + if s: if y < 0: - s = s+" - " + s += " - " y = -y else: - s = s+" + " - s = s+"{ystr}*{istr}".format(ystr=y.str(base, truncate=truncate), - istr=istr) - if len(s) == 0: - s = self.real().str(base, truncate=truncate) + s += " + " + ystr = y.str(base, **kwds) + s += ystr + "*" + istr + if not s: + s = self.real().str(base, **kwds) return s def _latex_(self): @@ -539,7 +541,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): '-\\infty' """ import re - s = self.str().replace('*I', 'i').replace('infinity','\\infty') + s = repr(self).replace('*I', 'i').replace('infinity','\\infty') return re.sub(r"e(-?\d+)", r" \\times 10^{\1}", s) def _pari_(self): @@ -853,8 +855,8 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: magma(ComplexField(200)(1/3)) # indirect, optional - magma 0.333333333333333333333333333333333333333333333333333333333333 """ - real_string = self.real().str(truncate=False) - imag_string = self.imag().str(truncate=False) + real_string = self.real().str() + imag_string = self.imag().str() digit_precision_bound = len(real_string) return "%s![%sp%s, %sp%s]" % (self.parent()._magma_init_(magma), real_string, digit_precision_bound, diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 8ce36cb3a2d..beb7c8b06a0 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -538,7 +538,9 @@ cdef class Rational(sage.structure.element.FieldElement): if not base: set_from_Rational(self, x.simplest_rational()) else: - xstr = x.str(base) + # Truncate in base 10 to match repr(x). + # See https://trac.sagemath.org/ticket/21124 + xstr = x.str(base, truncate=(base == 10)) if '.' in xstr: exp = (len(xstr) - (xstr.index('.') +1)) p = base**exp diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index d4969151073..4d4ab4237e5 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -1591,7 +1591,7 @@ cdef class RealIntervalFieldElement(RingElement): Now, consider the precisions needed to represent the endpoints (this is the precision that would be produced by - ``v.lower().str(no_sci=False, truncate=False)``). Our + ``v.lower().str(no_sci=False)``). Our result is no more precise than the less precise endpoint, and is sufficiently imprecise that the error can be represented with the given number of decimal digits. Our result is the most precise @@ -1678,8 +1678,8 @@ cdef class RealIntervalFieldElement(RingElement): style = 'brackets' if style == 'brackets': - t1 = self.lower().str(base=base, no_sci=no_sci, e=e, truncate=False) - t2 = self.upper().str(base=base, no_sci=no_sci, e=e, truncate=False) + t1 = self.lower().str(base=base, no_sci=no_sci, e=e) + t2 = self.upper().str(base=base, no_sci=no_sci, e=e) return "[%s .. %s]"%(t1, t2) @@ -2204,7 +2204,7 @@ cdef class RealIntervalFieldElement(RingElement): EXAMPLES:: sage: R = RealIntervalField(13) - sage: R.pi().lower().str(truncate=False) + sage: R.pi().lower().str() '3.1411' :: @@ -2255,7 +2255,7 @@ cdef class RealIntervalFieldElement(RingElement): EXAMPLES:: sage: R = RealIntervalField(13) - sage: R.pi().upper().str(truncate=False) + sage: R.pi().upper().str() '3.1417' :: diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 0a7de270574..82bf4c883e4 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -1388,7 +1388,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: magma(RealField(1000)(1/3)) # indirect, optional - magma 0.3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 """ - real_string = self.str(truncate=False) + real_string = self.str() digit_precision_upper_bound = len(real_string) return "%s!%sp%s" % (self.parent()._magma_init_(magma), real_string, digit_precision_upper_bound) @@ -1540,7 +1540,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: RR(2.1) # indirect doctest 2.10000000000000 """ - return self.str(10) + return self.str(truncate=True) def _latex_(self): r""" @@ -1553,7 +1553,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: latex(RR(2e100)) # indirect doctest 2.00000000000000 \times 10^{100} """ - s = self.str() + s = repr(self) parts = s.split('e') if len(parts) > 1: # scientific notation @@ -1583,7 +1583,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: s1 == RR(gp(s1)) True """ - return self.str(10, no_sci=True, truncate=False) + return self.str(10, no_sci=True) def _sage_input_(self, sib, coerced): r""" @@ -1673,8 +1673,7 @@ cdef class RealNumber(sage.structure.element.RingElement): # in a number with at least the number of bits we need. will_convert = (coerced == 2 or not coerced) - self_str = self.str(truncate=False, - skip_zeroes=(will_convert or self.prec() <= 53)) + self_str = self.str(skip_zeroes=(will_convert or self.prec() <= 53)) # To use choice 2 or choice 4, we must be able to read # numbers of this precision as a literal. We support this @@ -1702,7 +1701,7 @@ cdef class RealNumber(sage.structure.element.RingElement): s = self_str else: # This is tricky. str() uses mpfr_get_str() with - # reqdigits=0; this guarantees to give enough digits + # digits=0; this guarantees to give enough digits # to recreate the input, if we print and read with # round-to-nearest. However, we are not going to # read with round-to-nearest, so we might need more digits. @@ -1715,7 +1714,7 @@ cdef class RealNumber(sage.structure.element.RingElement): fld = RealField(self.prec() + 1, rnd='RNDU') else: fld = RealField(self.prec() + 1, rnd='RNDD') - s = fld(self).str(truncate=False) + s = fld(self).str() v = sib(self.parent())(sib.float_str(repr(s))) if negative: @@ -1786,65 +1785,80 @@ cdef class RealNumber(sage.structure.element.RingElement): """ return ZZ(0) - def str(self, int base=10, no_sci=None, e=None, int truncate=1, bint skip_zeroes=0): + def str(self, int base=10, size_t digits=0, *, no_sci=None, + e=None, bint truncate=False, bint skip_zeroes=False): """ Return a string representation of ``self``. INPUT: - - ``base`` -- base for output + - ``base`` -- (default: 10) base for output - - ``no_sci`` -- if 2, never print using scientific notation; if 1 - or ``True``, print using scientific notation only for very large or - very small numbers; if 0 or ``False`` always print with scientific - notation; if ``None`` (the default), print how the parent prints. + - ``digits`` -- (default: 0) number of digits to display. When + ``digits`` is zero, choose this automatically. - - ``e`` -- symbol used in scientific notation; defaults to 'e' for - base=10, and '@' otherwise + - ``no_sci`` -- if 2, never print using scientific notation; if + ``True``, use scientific notation only for very large or very + small numbers; if ``False`` always print with scientific + notation; if ``None`` (the default), print how the parent + prints. - - ``truncate`` -- if ``True``, round off the last digits in - printing to lessen confusing base-2 roundoff issues. + - ``e`` -- symbol used in scientific notation; defaults to 'e' for + base=10, and '@' otherwise - - ``skip_zeroes`` -- if ``True``, skip trailing zeroes in mantissa + - ``truncate`` -- (default: ``False``) if ``True``, round off the + last digits in base-10 printing to lessen confusing base-2 + roundoff issues. This flag may not be used in other bases or + when ``digits`` is given. + + - ``skip_zeroes`` -- (default: ``False``) if ``True``, skip + trailing zeroes in mantissa EXAMPLES:: sage: a = 61/3.0; a 20.3333333333333 - sage: a.str(truncate=False) + sage: a.str() '20.333333333333332' + sage: a.str(truncate=True) + '20.3333333333333' sage: a.str(2) '10100.010101010101010101010101010101010101010101010101' sage: a.str(no_sci=False) - '2.03333333333333e1' + '2.0333333333333332e1' sage: a.str(16, no_sci=False) '1.4555555555555@1' + sage: a.str(digits=5) + '20.333' + sage: a.str(2, digits=5) + '10100.' + sage: b = 2.0^99 sage: b.str() - '6.33825300114115e29' + '6.3382530011411470e29' sage: b.str(no_sci=False) - '6.33825300114115e29' + '6.3382530011411470e29' sage: b.str(no_sci=True) - '6.33825300114115e29' + '6.3382530011411470e29' sage: c = 2.0^100 sage: c.str() - '1.26765060022823e30' + '1.2676506002282294e30' sage: c.str(no_sci=False) - '1.26765060022823e30' + '1.2676506002282294e30' sage: c.str(no_sci=True) - '1.26765060022823e30' + '1.2676506002282294e30' sage: c.str(no_sci=2) - '1267650600228230000000000000000.' + '1267650600228229400000000000000.' sage: 0.5^53 1.11022302462516e-16 sage: 0.5^54 5.55111512312578e-17 sage: (0.01).str() - '0.0100000000000000' + '0.010000000000000000' sage: (0.01).str(skip_zeroes=True) '0.01' sage: (-10.042).str() - '-10.0420000000000' + '-10.042000000000000' sage: (-10.042).str(skip_zeroes=True) '-10.042' sage: (389.0).str(skip_zeroes=True) @@ -1858,10 +1872,48 @@ cdef class RealNumber(sage.structure.element.RingElement): 1ekg.00000000 sage: print((65536.0).str(base=62)) H32.0000000 - sage: print((65536.0).str(base=63)) + + String conversion respects rounding:: + + sage: x = -RR.pi() + sage: x.str(digits=1) + '-3.' + sage: y = RealField(53, rnd="RNDD")(x) + sage: y.str(digits=1) + '-4.' + sage: y = RealField(53, rnd="RNDU")(x) + sage: y.str(digits=1) + '-3.' + sage: y = RealField(53, rnd="RNDZ")(x) + sage: y.str(digits=1) + '-3.' + sage: y = RealField(53, rnd="RNDA")(x) + sage: y.str(digits=1) + '-4.' + + TESTS:: + + sage: x = RR.pi() + sage: x.str(base=1) + Traceback (most recent call last): + ... + ValueError: base (=1) must be an integer between 2 and 62 + sage: x.str(base=63) Traceback (most recent call last): ... ValueError: base (=63) must be an integer between 2 and 62 + sage: x.str(digits=-10) + Traceback (most recent call last): + ... + OverflowError: can't convert negative value to size_t + sage: x.str(base=16, truncate=True) + Traceback (most recent call last): + ... + ValueError: truncate is only supported in base 10 + sage: x.str(digits=10, truncate=True) + Traceback (most recent call last): + ... + ValueError: cannot truncate when digits is given """ if base < 2 or base > 62: raise ValueError("base (=%s) must be an integer between 2 and 62" % base) @@ -1882,23 +1934,19 @@ cdef class RealNumber(sage.structure.element.RingElement): else: e = 'e' - cdef char *s - cdef mp_exp_t exponent - - cdef int reqdigits - - reqdigits = 0 - - if base == 10 and truncate: - - # This computes reqdigits == floor(log_{10}(2^(b-1))), + if truncate: + if base != 10: + raise ValueError("truncate is only supported in base 10") + if digits: + raise ValueError("cannot truncate when digits is given") + # This computes digits = floor(log_{10}(2^(b-1))), # which is the number of *decimal* digits that are # "right", given that the last binary bit of the binary # number can be off. That is, if this real is within a # relative error of 2^(-b) of an exact decimal with - # reqdigits digits, that decimal will be returned. + # `digits` digits, that decimal will be returned. # This is equivalent to saying that exact decimals with - # reqdigits digits differ by at least 2*2^(-b) (relative). + # `digits` digits differ by at least 2*2^(-b) (relative). # (Depending on the precision and the exact number involved, # adjacent exact decimals can differ by far more than 2*2^(-b) @@ -1907,15 +1955,17 @@ cdef class RealNumber(sage.structure.element.RingElement): # This avoids the confusion a lot of people have with the last # 1-2 binary digits being wrong due to rounding coming from # representing numbers in binary. - - reqdigits = (((self._parent).__prec - 1) * 0.3010299956) - if reqdigits <= 1: reqdigits = 2 + digits = (((self._parent).__prec - 1) * 0.3010299956) + if digits < 2: + digits = 2 sig_on() - s = mpfr_get_str(0, &exponent, base, reqdigits, + cdef char *s + cdef mp_exp_t exponent + s = mpfr_get_str(NULL, &exponent, base, digits, self.value, (self._parent).rnd) sig_off() - if s == 0: + if s is NULL: raise RuntimeError("unable to convert an mpfr number to a string") t = str(s) mpfr_free_str(s) @@ -1923,18 +1973,13 @@ cdef class RealNumber(sage.structure.element.RingElement): if skip_zeroes: t = _re_skip_zeroes.match(t).group(1) - cdef int digits - digits = len(t) - if t[0] == "-": - digits = digits - 1 - if no_sci is None: no_sci = not (self._parent).sci_not - if no_sci is True and ( abs(exponent-1) >=6 ): + if no_sci is True and abs(exponent-1) >= 6: no_sci = False - if no_sci is False: + if not no_sci: if t[0] == "-": return "-%s.%s%s%s" % (t[1:2], t[2:], e, exponent-1) return "%s.%s%s%s" % (t[0], t[1:], e, exponent-1) @@ -2844,18 +2889,18 @@ cdef class RealNumber(sage.structure.element.RingElement): EXAMPLES:: - sage: (1.0).nexttoward(2).str(truncate=False) + sage: (1.0).nexttoward(2).str() '1.0000000000000002' - sage: (1.0).nexttoward(RR('-infinity')).str(truncate=False) + sage: (1.0).nexttoward(RR('-infinity')).str() '0.99999999999999989' sage: RR(infinity).nexttoward(0) 2.09857871646739e323228496 # 32-bit 5.87565378911159e1388255822130839282 # 64-bit - sage: RR(pi).str(truncate=False) + sage: RR(pi).str() '3.1415926535897931' - sage: RR(pi).nexttoward(22/7).str(truncate=False) + sage: RR(pi).nexttoward(22/7).str() '3.1415926535897936' - sage: RR(pi).nexttoward(21/7).str(truncate=False) + sage: RR(pi).nexttoward(21/7).str() '3.1415926535897927' """ cdef RealNumber other_rn @@ -2885,9 +2930,9 @@ cdef class RealNumber(sage.structure.element.RingElement): 8.50969131174084e-1388255822130839284 # 64-bit sage: RR('+infinity').nextabove() +infinity - sage: RR(-sqrt(2)).str(truncate=False) + sage: RR(-sqrt(2)).str() '-1.4142135623730951' - sage: RR(-sqrt(2)).nextabove().str(truncate=False) + sage: RR(-sqrt(2)).nextabove().str() '-1.4142135623730949' """ @@ -2911,9 +2956,9 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: RR('+infinity').nextbelow() 2.09857871646739e323228496 # 32-bit 5.87565378911159e1388255822130839282 # 64-bit - sage: RR(-sqrt(2)).str(truncate=False) + sage: RR(-sqrt(2)).str() '-1.4142135623730951' - sage: RR(-sqrt(2)).nextbelow().str(truncate=False) + sage: RR(-sqrt(2)).nextbelow().str() '-1.4142135623730954' """ From 8b0f3ae1c60bb97a94cc8dd0538b2bd4db7c0703 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 5 Apr 2017 12:22:34 +0200 Subject: [PATCH 024/184] Remove unused method and argument --- src/sage_setup/docbuild/__init__.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index a46272d0453..a750ad2b32f 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -882,7 +882,7 @@ def get_all_included_modules(self): for module in self.get_modules(filename): yield module - def get_new_and_updated_modules(self, save=False): + def get_new_and_updated_modules(self): """ Return an iterator for all new and updated modules that appear in the toctrees, and remove obsolete old modules. @@ -938,22 +938,6 @@ def get_new_and_updated_modules(self, save=False): logger.info("Found %d updated modules", len(updated_modules)) logger.info("Removed %d obsolete modules", len(removed_modules)) - def get_newly_included_modules(self, save=False): - """ - Returns an iterator for all modules that appear in the - toctrees that don't appear in the cache. - """ - cache = self.get_cache() - new_modules = 0 - for module in self.get_all_included_modules(): - if module not in cache: - cache[module] = True - new_modules += 1 - yield module - logger.info("Found %d newly included modules", new_modules) - if save: - self.save_cache() - def print_new_and_updated_modules(self): """ Print all the modules that appear in the toctrees that From c58d90285342fe430a186343b3ca2d86b905802c Mon Sep 17 00:00:00 2001 From: Karim Van Aelst Date: Fri, 5 May 2017 16:40:25 +0200 Subject: [PATCH 025/184] Add integrated curves on manifolds --- src/doc/en/reference/manifolds/diff_map.rst | 2 + src/sage/manifolds/differentiable/chart.py | 29 + src/sage/manifolds/differentiable/curve.py | 28 +- .../differentiable/integrated_curve.py | 1195 +++++++++++++++++ src/sage/manifolds/differentiable/manifold.py | 175 +++ 5 files changed, 1426 insertions(+), 3 deletions(-) create mode 100644 src/sage/manifolds/differentiable/integrated_curve.py diff --git a/src/doc/en/reference/manifolds/diff_map.rst b/src/doc/en/reference/manifolds/diff_map.rst index 0054c1099ef..385248c0506 100644 --- a/src/doc/en/reference/manifolds/diff_map.rst +++ b/src/doc/en/reference/manifolds/diff_map.rst @@ -9,3 +9,5 @@ Differentiable Maps and Curves sage/manifolds/differentiable/diff_map sage/manifolds/differentiable/curve + + sage/manifolds/differentiable/integrated_curve diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index bf7b8d18c5d..777e79fdfa1 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -548,6 +548,35 @@ def restrict(self, subset, restrictions=None): dom._top_frames.remove(resu._frame) return self._dom_restrict[subset] + def symbolic_velocities(self, left='D', right=None): + r""" + + Returns a list of symbolic variables ready to be used by the user as + the derivatives of the coordinate functions with respect to a curve + parameter (that is the velocities along the curve). + It may actually serve to denote anything else than velocities, with a + name including the coordinate functions. + + """ + + from sage.calculus.var import var + + string_velocities = [] + velocities = [] + + for coord_func in self[:]: + string_velocities += [left + "{}".format(coord_func)] # will raise + #error in case left is not a string + + if right is not None: + string_velocities = [string_vel + right for string_vel in string_velocities] # will + # raise error in case right is not a string + + for string_vel in string_velocities: + velocities += [var(string_vel)] + + return velocities + #***************************************************************************** class RealDiffChart(DiffChart, RealChart): diff --git a/src/sage/manifolds/differentiable/curve.py b/src/sage/manifolds/differentiable/curve.py index 0bc6c4544bb..4e45d203896 100644 --- a/src/sage/manifolds/differentiable/curve.py +++ b/src/sage/manifolds/differentiable/curve.py @@ -763,10 +763,8 @@ def plot(self, chart=None, ambient_coords=None, mapping=None, prange=None, """ from sage.rings.infinity import Infinity from sage.misc.functional import numerical_approx - from sage.plot.graphics import Graphics - from sage.plot.line import line from sage.manifolds.chart import RealChart - from sage.manifolds.utilities import set_axes_labels + # # Get the @options from kwds # @@ -857,9 +855,33 @@ def plot(self, chart=None, ambient_coords=None, mapping=None, prange=None, [numerical_approx( x[j].substitute(parameters) ) for j in ind_pc] ) t += dt + + return self._graphics(plot_curve, ambient_coords, thickness=thickness, + aspect_ratio=aspect_ratio, color= color, + style=style, label_axes=label_axes) + + + + def _graphics(self, plot_curve, ambient_coords, thickness=1, aspect_ratio='automatic', + color='red', style='-', label_axes=True): + r""" + Plots a 2D or 3D curve in a Cartesian graph with axes labeled by the + ambient coordinates. + + TESTS:: + sage: #TO DO + + """ + + from sage.plot.graphics import Graphics + from sage.plot.line import line + from sage.manifolds.utilities import set_axes_labels + + # # The plot # + n_pc = len(ambient_coords) resu = Graphics() resu += line(plot_curve, color=color, linestyle=style, thickness=thickness) diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py new file mode 100644 index 00000000000..394a501938f --- /dev/null +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -0,0 +1,1195 @@ +r""" +Integrated Curves in Manifolds + +Given a differentiable manifold `M`, an *integrated curve* curve in `M` is a +differentiable curve constructed as a numerical solution to a system of +second order differential equations. + +Integrated curves are implemented by :class:`IntegratedCurve`, which the +classes :class:`IntegratedAutoparallelCurve` and :class:`IntegratedGeodesic` +inherit. + +""" + +from sage.symbolic.expression import Expression +from sage.rings.infinity import Infinity +from sage.calculus.desolvers import desolve_system_rk4 +from sage.manifolds.chart import Chart +from sage.manifolds.differentiable.real_line import OpenInterval +from sage.manifolds.differentiable.curve import DifferentiableCurve +from sage.manifolds.differentiable.tangent_vector import TangentVector +from sage.calculus.interpolation import Spline +from sage.misc.decorators import options +from sage.misc.functional import numerical_approx + + + +class IntegratedCurve(DifferentiableCurve): + r""" + Given a chart with coordinates denoted :MATH:`(x_{1}, ..., x_{n})`, + an instance of :class:`IntegratedCurve` is a curve + :MATH:`t \mapsto (x_{1}(t), ..., x_{n}(t))` constructed as a numerical + solution to a system of second order differential equations satisfied by + the coordinate curves :MATH:`t \mapsto x_{i}(t)`. + + INPUT: + + - ``parent`` -- + :class:`~sage.manifolds.differentiable.manifold_homset.DifferentiableCurveSet` + the set of curves `\mathrm{Hom}(I, M)` to which the curve belongs + - ``equations_rhs`` -- list of the right-hand sides of the equations on + the velocities only (the term *velocity* referring to the derivatives + :MATH:`d x_{i} / dt` of the coordinate curves) + - ``velocities`` -- list of the symbolic expressions used in ``equations_rhs`` + to denote the velocities + - ``curve_parameter`` -- symbolic expression used in ``equations_rhs`` + to denote the parameter of the curve (denoted :MATH:`t` in the + descriptions above) + - ``initial_tangent_vector`` -- + :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` + initial tangent vector of the curve + - ``name`` -- (default: ``None``) string; symbol given to the curve + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the + the curve; if none is provided, ``name`` will be used + - ``is_isomorphism`` -- (default: ``False``) determines whether the + constructed object is a diffeomorphism; if set to ``True``, + then `M` must have dimension one + - ``is_identity`` -- (default: ``False``) determines whether the + constructed object is the identity map; if set to ``True``, + then `M` must coincide with the domain of the curve + + EXAMPLE: + + Motion of a charged particle in an axial magnetic field linearly + increasing in time and exponentially decreasing in space: + + .. MATH:: + + \mathbf{B}(t,\mathbf{x}) = \frac{B_{0}t}{T} \exp \left( - \frac{ x_{1}^{2} + x_{2}^{2} }{ L^{2} } \right) \mathbf{e_{3}}. + + Equations of motion are: + + .. MATH:: + + \ddot{x}_{1}(t) &= \frac{qB(t, \mathbf{x}(t))}{m} \dot{x}_{2}(t) \\ + \ddot{x}_{2}(t) &= - \frac{qB(t, \mathbf{x}(t))}{m} \dot{x}_{1}(t) \\ + \ddot{x}_{3}(t) &= 0 + + Start with declaring a chart on a 3-dimensional manifold and the various + symbolic expressions denoting the equations, velocities and parameters:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() ; D + [Dx1, Dx2, Dx3] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + + Set the initial conditions:: + + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + + Declare an integrated curve and display information relative to it:: + + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) ; c + Integrated curve c in the 3-dimensional differentiable manifold M + sage: c.system() + Curve c in the 3-dimensional differentiable manifold M integrated over + the Real interval (0, 5) as a solution to the following system, written + with respect to the Chart (M, (x1, x2, x3)): + + Initial point: Point p on the 3-dimensional differentiable manifold M + with coordinates [0, 0, 0] in Chart (M, (x1, x2, x3)) + Initial tangent vector: Tangent vector at Point p on the 3-dimensional + differentiable manifold M with components [1, 0, 1] in Chart (M, (x1, x2, x3)) + + d(x1)/dt = Dx1 + d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(x2)/dt = Dx2 + d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(x3)/dt = Dx3 + d(Dx3)/dt = 0 + + Generate a solution of the system and an interpolation of this solution:: + + sage: c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + ....: solution_key='carac time 1') + Performing 4th order Runge-Kutta integration by default... + Numerical integration completed. Resulting list of points was associated + with the key 'carac time 1' (if this key already referred to a former + numerical solution, such a solution was erased). + sage: c.interpolate(solution_key='carac time 1', interpolation_key='interp 1') + Performing cubic spline interpolation by default... + Interpolation completed and associated with the key 'interp 1' + (if this key already referred to a former interpolation, + such an interpolation was erased). + + Such an interpolation is required to evaluate the curve and the vector + tangent to the curve for any value of the curve parameter:: + + sage: c(1.9) + Evaluating point coordinates from the interpolation associated with + the key 'interp 1' by default... + [1.3776707219621374, -0.9000776970132945, 1.9] + sage: v = c.tangent_vector_eval_at(4.3) + Evaluating tangent vector components from the interpolation associated + with the key 'interp 1' by default... + sage: v + Tangent vector at Point on the 3-dimensional differentiable manifold M + sage: v.display() + -0.9303968397216424 d/dx1 - 0.3408080563014475 d/dx2 + 1.0000000000000004 d/dx3 + + Plotting a numerical solution (with or without its tangent vector field) + also requires the solution to be interpolated at least once:: + + sage: c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], interpolation_key='interp 1', + ....: thickness=2.5, display_tangent=True, plot_points=200, + ....: plot_points_tangent=10, scale=0.5, color='blue', + ....: color_tangent='red') + sage: c_plot_2d_1.show() + + .. PLOT:: + + M = Manifold(3, 'M') + X = M.chart('x1 x2 x3') + var('x1 x2 x3 t B_0 m q L T') + B = B_0*t/T*exp(-(x1**2 + x2**2)/L**2) + D = X.symbolic_velocities() ; D + eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + p = M.point((0,0,0), name='p') + Tp = M.tangent_space(p) + v = Tp((1,0,1)) + c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + parameters=[B_0, m, q, L, T]) + c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + solution_key='carac time 1') + c.interpolate(solution_key='carac time 1', interpolation_key='interp 1') + c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], interpolation_key='interp 1', + thickness=2.5, display_tangent=True, plot_points=200, + plot_points_tangent=10, scale=0.5, color='blue', + color_tangent='red') + sphinx_plot(c_plot_2d_1) + + An instance of :class:`IntegratedCurve` may store several numerical solutions + and interpolations:: + + sage: c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:100}, + ....: solution_key='carac time 100') + Performing 4th order Runge-Kutta integration by default... + Numerical integration completed. Resulting list of points was associated + with the key 'carac time 100' (if this key already referred to a former + numerical solution, such a solution was erased). + sage: c.interpolate(solution_key='carac time 100', interpolation_key='interp 100') + Performing cubic spline interpolation by default... + Interpolation completed and associated with the key 'interp 100' + (if this key already referred to a former interpolation, such an + interpolation was erased). + sage: c_plot_3d_100 = c.plot(interpolation_key='interp 100', thickness=2.5, + ....: display_tangent=True, plot_points=200, + ....: plot_points_tangent=10, scale=0.5, + ....: color='green', color_tangent='orange') + sage: c_plot_3d_1 = c.plot(interpolation_key='interp 1', thickness=2.5, + ....: display_tangent=True, plot_points=200, + ....: plot_points_tangent=10, scale=0.5, + ....: color='blue', color_tangent='red') + sage: (c_plot_3d_1 + c_plot_3d_100).show() + + .. PLOT:: + + M = Manifold(3, 'M') + X = M.chart('x1 x2 x3') + var('x1 x2 x3 t B_0 m q L T') + B = B_0*t/T*exp(-(x1**2 + x2**2)/L**2) + D = X.symbolic_velocities() ; D + eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + p = M.point((0,0,0), name='p') + Tp = M.tangent_space(p) + v = Tp((1,0,1)) + c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + parameters=[B_0, m, q, L, T]) + c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + solution_key='carac time 1') + c.interpolate(solution_key='carac time 1', interpolation_key='interp 1') + c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:100}, + solution_key='carac time 100') + c.interpolate(solution_key='carac time 100', interpolation_key='interp 100') + c_plot_3d_1 = c.plot(interpolation_key='interp 1', thickness=2.5, + display_tangent=True, plot_points=200, + plot_points_tangent=10, scale=0.5, color='blue', + color_tangent='red') + c_plot_3d_100 = c.plot(interpolation_key='interp 100', thickness=2.5, + display_tangent=True, plot_points=200, + plot_points_tangent=10, scale=0.5, color='green', + color_tangent='orange') + sphinx_plot(c_plot_3d_1 + c_plot_3d_100) + + """ + + def __init__(self, parent, equations_rhs, velocities, curve_parameter, + initial_tangent_vector, chart=None, parameters=None, name=None, + latex_name=None, is_isomorphism=False, is_identity=False): + + r""" + Constructs a numerical curve. + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns + [x1], D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + ValueError: Number of equations should equal codomain dimension. + sage: c = M.integrated_curve(eqns, D + [x1], (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + ValueError: Number of velocities should equal codomain dimension. + sage: c = M.integrated_curve(eqns, D, (t, -oo, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + ValueError: Both boundaries of the interval need to be finite. + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), x1, name='c', + ....: parameters=[B_0, m, q, L, T]) + TypeError: x1 should be a tangent vector. + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + parameters=[m, q, L, T]) + TypeError: B_0 should either be a coordinate function of B_0, or + one the corresponding velocities [Dx1, Dx2, Dx3], or the curve + parameter t, or one of the parameters [m, q, L, T]. + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) ; c + Integrated curve c in the 3-dimensional differentiable manifold M + sage: # TestSuite(c).run() # pickling and category failed + + """ + + # starting with parent class method to initialize the four last arguments + DifferentiableCurve.__init__(self, parent, name=name, latex_name=latex_name, + is_isomorphism=is_isomorphism, + is_identity=is_identity) # (coord_expression=None) + + # checking argument 'parent' + domain = self.domain() + if not isinstance(domain, OpenInterval): + raise TypeError("{} is not a real interval".format(domain)) + else: + t_min = domain.lower_bound() + t_max = domain.upper_bound() + if t_min == -Infinity or t_max == +Infinity: + raise ValueError("Both boundaries of the interval need to be finite.") + + # checking argument 'equations_rhs' + codomain_dimension = self.codomain().dim() + if len(equations_rhs) != codomain_dimension: + raise ValueError("Number of equations should equal codomain dimension.") + for eqn in equations_rhs: + if not isinstance(eqn, Expression): + raise TypeError("{} should be a symbolic expression.".format(eqn)) + # desolve_system_rk4 called in 'solve' method is in charge of raising + # errors about possibly remaining problems in argument equations_rhs + + # checking argument 'velocities' + if len(velocities) != codomain_dimension: + raise ValueError("Number of velocities should equal codomain dimension.") + + # checking argument 'curve_parameter' + if not isinstance(curve_parameter, Expression): + raise TypeError("{} should be a symbolic expression.".format(curve_parameter)) + # desolve_system_rk4 called in 'solve' method is in charge of raising + # errors about possibly remaining problems in argument curve_parameter + + # checking argument 'initial_tangent_vector' + if not isinstance(initial_tangent_vector, TangentVector): + raise TypeError("{} should be a tangent vector.".format(initial_tangent_vector)) + + # setting the chart + if chart is None: + chart = self.codomain().default_chart() + + # checking argument 'parameters' + codomain_dimension = self.codomain().dim() + if parameters is not None: + for param in parameters: + if not isinstance(param, Expression): + raise TypeError("{} should be a symbolic expression.".format(param)) + + # checking that equations_rhs only uses the chart coordinates, the velocities, + # the curve parameter and the parameters if there are + announced_variables = [coord_func for coord_func in chart[:]] + announced_variables += [vel for vel in velocities] + [curve_parameter] + if parameters is not None: + announced_variables += [param for param in parameters] + + equations_rhs_variables = set() + for eqn in equations_rhs: + equations_rhs_variables = equations_rhs_variables.union(eqn.variables()) + + for rhs_variable in equations_rhs_variables: + if rhs_variable not in announced_variables: + str_error = "{} should either be ".format(rhs_variable) + str_error += "a coordinate function of {}, ".format(rhs_variable, chart) + str_error += "or one the corresponding velocities {}, ".format(velocities) + str_error += "or the curve parameter {}".format(curve_parameter) + if parameters is not None: + str_error += ", or one of the parameters {}".format(parameters) + raise TypeError(str_error + ".") + + # defining all attributes + self._equations_rhs = [eqn for eqn in equations_rhs] # converts to list + # since might not already be a list (which is later required) + self._velocities = [vel for vel in velocities] # converts to list since + # might not already be a list (which is later required) + self._curve_parameter = curve_parameter + self._initial_tangent_vector = initial_tangent_vector + self._chart = chart + self._parameters = parameters + self._solutions = {} # dictionary containing all numerically computed + # lists of points of the curve, the keys being chosen by the user when + # calling method 'solve' + self._interpolations = {} # dictionary containing lists of interpolation + # objects, each interpolation object implementing the interpolation of + # one of the numerical coordinate curves, and the keys being chosen by + # the user when calling method 'interpolate' + + + + def _repr_(self): + r""" + Returns a string representation of ``self``. + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: parameters=[B_0, m, q, L, T]) ; c + Integrated curve in the 3-dimensional differentiable manifold M + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c' + ....: parameters=[B_0, m, q, L, T]) ; c + Integrated curve c in the 3-dimensional differentiable manifold M + + """ + + description = "Integrated curve " + if self._name is not None: + description += self._name + " " + description += "in the {}".format(self._codomain) + return description + + + + def system(self): + r""" + Provides a detailed description of the system defining the curve. + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + sage: c.system() + Curve c in the 3-dimensional differentiable manifold M integrated + over the Real interval (0, 5) as a solution to the following system, + written with respect to the Chart (M, (x1, x2, x3)): + + Initial point: Point p on the 3-dimensional differentiable manifold M + with coordinates [0, 0, 0] in Chart (M, (x1, x2, x3)) + Initial tangent vector: Tangent vector at Point p on the 3-dimensional + differentiable manifold M with components [1, 0, 1] in Chart (M, (x1, x2, x3)) + + d(x1)/dt = Dx1 + d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(x2)/dt = Dx2 + d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(x3)/dt = Dx3 + d(Dx3)/dt = 0 + + """ + + initial_tangent_space = self._initial_tangent_vector.parent() + initial_point = initial_tangent_space.base_point() # gets the initial point + # as the base point of the tangent space to which initial tangent vector belongs + initial_point_coordinates = initial_point.coordinates(self._chart) # will + # raise error if coordinates in chart are not known + initial_point_coordinates = [coord for coord in initial_point_coordinates] # converts + # to list since was previously a tuple + + initial_coordinate_basis = self._chart.frame().at(initial_point) + initial_tangent_vector_components = self._initial_tangent_vector[:, initial_coordinate_basis] # will + # raise error if components in coordinate basis are not known + + description = "Curve " + if self._name is not None: + description += self._name + " " + description += "in the {} integrated over the {} ".format(self.codomain(), + self.domain()) + + description += "as a solution to the following system, written with " + description += "respect to the {}:\n\n".format(self._chart) + description += "Initial point: {} with coordinates {} in {}\n".format(initial_point, + initial_point_coordinates, + self._chart) + description += "Initial tangent vector: {} with components {} in {}\n\n".format(self._initial_tangent_vector, + initial_tangent_vector_components, + self._chart) + + for coord_func, velocity, eqn in zip(self._chart[:], self._velocities, self._equations_rhs): + description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) + description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) + print(description) + + return [self._equations_rhs, self._initial_tangent_vector, self._chart] + + + + def solve(self, step=0.1, method=None, solution_key=None, parameters_values=None): + r""" + Integrates the curve over the domain of integration. + + Integration scheme 'rk4' uses Sage solver *desolve_system_rk4*. + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + sage: c.solve(parameters_values={m:1, q:1, L:10, T:1}) + ValueError: Numerical values should be provided for each of the + parameters [B_0, m, q, L, T]. + sage: c.solve(method='my method', parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + ValueError: No available method of integration referred to as 'my method'. + sage: c.solve(parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + Performing 4th order Runge-Kutta integration by default... + Resulting list of points will be associated with the key 'rk4' by + default. + Numerical integration completed. Resulting list of points was + associated with the key 'rk4' (if this key already referred to a + former numerical solution, such a solution was erased). + + """ + + if method is None: + method = 'rk4' + print('Performing 4th order Runge-Kutta integration by default...') + + if solution_key is None: + solution_key = method + print("Resulting list of points will be associated with the key '{}' by default.".format(solution_key)) + + if method == 'rk4': + if self._parameters is not None: + if parameters_values is None or len(parameters_values) != len(self._parameters): + raise ValueError("Numerical values should be provided for each of the parameters {}.".format(self._parameters)) + eqns_rhs = [eqn.substitute(parameters_values) for eqn in self._equations_rhs] # will + # raise error if any element of parameters_values is not numerical + else: + eqns_rhs = self._equations_rhs + + coordinate_functions_list = [coord_func for coord_func in self._chart[:]] + + t_min = numerical_approx(self.domain().lower_bound()) + t_max = numerical_approx(self.domain().upper_bound()) + + initial_tangent_space = self._initial_tangent_vector.parent() + initial_point = initial_tangent_space.base_point() # gets the initial + # point as the base point of the tangent space to which the initial tangent vector belongs + initial_point_coordinates = initial_point.coordinates(self._chart) # will + # raise error if coordinates in chart cannot get known + initial_point_coordinates = [numerical_approx(coord) for coord + in initial_point_coordinates] # converts + # to list since was previously a tuple (so it can be added to [t_min] below), + # and gets numerical value in case some coordinates contained expressions such as pi + + initial_coordinate_basis = self._chart.frame().at(initial_point) + initial_tangent_vector_components = self._initial_tangent_vector[:, initial_coordinate_basis] # will + # raise error if components in coordinate basis cannot get known + initial_tangent_vector_components = [numerical_approx(comp) for comp + in initial_tangent_vector_components] # should + # already be a list, but the components need to be completely numerical as well + + sol = desolve_system_rk4(self._velocities + eqns_rhs, + coordinate_functions_list + self._velocities, + ivar = self._curve_parameter, + ics=[t_min] + initial_point_coordinates + initial_tangent_vector_components, + end_points=[t_min, t_max], step=step) + + dim = self.codomain().dim() + self._solutions[solution_key] = [point[0:dim+1] for point in sol] + else: + raise ValueError("No available method of integration referred to as '{}'.".format(method)) + + print("Numerical integration completed. " + + "Resulting list of points was associated with the key '{}' ".format(solution_key) + + "(if this key already referred to a former numerical solution, " + + "such a solution was erased).") + + return self._solutions[solution_key] + + + + def solution(self, solution_key=None): + r""" + Returns the list of points associated with the given key. + + TESTS:: + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + sage: c.solve(parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + Performing 4th order Runge-Kutta integration by default... + Resulting list of points will be associated with the key 'rk4' by + default. + Numerical integration completed. Resulting list of points was + associated with the key 'rk4' (if this key already referred to a + former numerical solution, such a solution was erased). + sage: c.solution(solution_key='my solution') + ValueError: No existing key 'my solution' referring to any numerical + solution. + sage: c.solution() + Returning the numerical solution associated with the key 'rk4' by + default... + + """ + + if solution_key is None: + if 'rk4' in self._solutions.keys(): + solution_key = 'rk4' + else: + solution_key = self._solutions.keys()[0] # will raise error if + # self._solutions empty + print("Returning the numerical solution associated with the key '{}' by default...".format(solution_key)) + elif solution_key not in self._solutions.keys(): + raise ValueError("No existing key '{}' referring to any numerical solution.".format(solution_key)) + + return self._solutions[solution_key] + + + + def interpolate(self, solution_key=None, method=None, interpolation_key=None): + r""" + Interpolates the chosen numerical solution using the given interpolation + method. + + Interpolation scheme 'cubic spline' uses *GSL* via Sage class + :class:`~sage.calculus.interpolation.Spline`. + + TESTS:: + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + sage: c.solve(method='rk4', solution_key='sol1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + Numerical integration completed. Resulting list of points was associated + with the key 'sol1' (if this key already referred to a former + numerical solution, such a solution was erased). + sage: c.interpolate(solution_key='my solution') + ValueError: No existing key 'my solution' referring to any numerical + solution. + sage: c.interpolate(solution_key='sol1', method='my method') + ValueError: No available method of interpolation referred to as 'my method'. + sage: c.interpolate(solution_key='sol1') + Performing cubic spline interpolation by default... + Resulting interpolation will be associated with the key + 'cubic spline-interp-sol1' by default. + Interpolation completed and associated with the key + 'cubic spline-interp-sol1' (if this key already referred to a + former interpolation, such an interpolation was erased). + sage: c.interpolate(method='cubic spline', solution_key='sol1', + interpolation_key='interp1') + Interpolation completed and associated with the key 'interp1' (if + this key already referred to a former interpolation, such an + interpolation was erased). + + """ + + if solution_key is None: + if 'rk4' in self._solutions.keys(): + solution_key = 'rk4' + else: + solution_key = self._solutions.keys()[0] # will raise error if + # self._solutions empty + print("Interpolating the numerical solution associated with the key '{}' by default...".format(solution_key)) + elif solution_key not in self._solutions.keys(): + raise ValueError("No existing key '{}' referring to any numerical solution.".format(solution_key)) + + if method is None: + method = 'cubic spline' + print("Performing cubic spline interpolation by default...") + + if interpolation_key is None: + interpolation_key = "{}-interp-{}".format(method, solution_key) + print("Resulting interpolation will be associated with the key '{}' by default.".format(interpolation_key)) + + if method=='cubic spline': + self._interpolations[interpolation_key] = [] + dim = self.codomain().dim() + for i in range(dim): + coordinate_curve = [] + for point in self._solutions[solution_key]: + coordinate_curve += [[point[0], point[i+1]]] + self._interpolations[interpolation_key] += [Spline(coordinate_curve)] + else: + raise ValueError("No available method of interpolation referred to as '" + + method + "'.") + + print("Interpolation completed and associated with the key '{}' ".format(interpolation_key) + + "(if this key already referred to a former interpolation, " + + "such an interpolation was erased).") + + return self._interpolations[interpolation_key] + + + + def interpolation(self, interpolation_key=None): + r""" + Returns the interpolation object associated with the given key. + + TESTS:: + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + sage: c.solve(method='rk4', solution_key='sol1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + Numerical integration completed. Resulting list of points was associated + with the key 'sol1' (if this key already referred to a former + numerical solution, such a solution was erased). + sage: c.interpolate(method='cubic spline', solution_key='sol1', + interpolation_key='interp1') + Interpolation completed and associated with the key 'interp1' (if + this key already referred to a former interpolation, such an + interpolation was erased). + sage: c.interpolation(interpolation_key='my interp') + ValueError: No existing key 'my interp' referring to any interpolation. + sage: c.interpolation() + Returning the interpolation associated with the key 'interp1' by + default... + + """ + + if interpolation_key==None: + if 'cubic spline' in self._interpolations.keys(): + interpolation_key = 'cubic spline' + else: + interpolation_key = self._interpolations.keys()[0] # will + # raise error if self._interpolations empty + print("Returning the interpolation associated with the key '{}' ".format(interpolation_key) + + "by default...") + elif interpolation_key not in self._interpolations.keys(): + raise ValueError("No existing key '{}' referring to any interpolation.".format(interpolation_key)) + + return self._interpolations[interpolation_key] + + + + def __call__(self, curve_parameter_value, interpolation_key=None): + r""" + Returns the image of the curve for the given value of the curve + parameter, using the chosen interpolation. + + TESTS:: + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + sage: c.solve(method='rk4', solution_key='sol1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + Numerical integration completed. Resulting list of points was associated + with the key 'sol1' (if this key already referred to a former + numerical solution, such a solution was erased). + sage: c.interpolate(method='cubic spline', solution_key='sol1', + interpolation_key='interp1') + Interpolation completed and associated with the key 'interp1' (if + this key already referred to a former interpolation, such an + interpolation was erased). + sage: c(1.1, interpolation_key='my interp') + ValueError: No existing key 'my interp' referring to any + interpolation. + sage: c(1.1) + Evaluating point coordinates from the interpolation associated + with the key 'interp1' by default... + [1.060743431308544, -0.2153838226258469, 1.1] + + """ + + if interpolation_key==None: + if 'cubic spline' in self._interpolations.keys(): + interpolation_key = 'cubic spline' + else: + interpolation_key = self._interpolations.keys()[0] # will + # raise error if self._interpolations empty + print("Evaluating point coordinates from the interpolation " + + "associated with the key '{}' ".format(interpolation_key) + + "by default...") + elif interpolation_key not in self._interpolations.keys(): + raise ValueError("No existing key '{}' referring to any interpolation.".format(interpolation_key)) + + interpolation = self._interpolations[interpolation_key] + + if isinstance(interpolation[0], Spline): #partial test, in case + # future interpolation objects do not contain lists of instances of + # the Spline class + interpolated_coordinates = [coordinate_curve_spline(curve_parameter_value) + for coordinate_curve_spline in interpolation] + return interpolated_coordinates + + raise TypeError("Unexpected type of interpolation object.") + + + + def tangent_vector_eval_at(self, curve_parameter_value, interpolation_key=None): + r""" + Returns the vector tangent to the curve at the given curve parameter + with components evaluated from the given interpolation. + + TESTS:: + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t B_0 m q L T') + (t, B_0, m, q, L, T) + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', + ....: parameters=[B_0, m, q, L, T]) + sage: c.solve(method='rk4', solution_key='sol1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + Numerical integration completed. Resulting list of points was associated + with the key 'sol1' (if this key already referred to a former + numerical solution, such a solution was erased). + sage: c.interpolate(method='cubic spline', solution_key='sol1', + interpolation_key='interp1') + Interpolation completed and associated with the key 'interp1' (if + this key already referred to a former interpolation, such an + interpolation was erased). + + """ + + if interpolation_key==None: + if 'cubic spline' in self._interpolations.keys(): + interpolation_key = 'cubic spline' + else: + interpolation_key = self._interpolations.keys()[0] # will + # raise error if self._interpolations empty + print("Evaluating tangent vector components from the interpolation " + + "associated with the key '{}' ".format(interpolation_key) + + "by default...") + elif interpolation_key not in self._interpolations.keys(): + raise ValueError("No existing key '{}' referring to any interpolation.".format(interpolation_key)) + + interpolation = self._interpolations[interpolation_key] + + if isinstance(interpolation[0], Spline): #partial test, in case + # future interpolation objects do not contain lists of instances of + # the Spline class + interpolated_coordinates = [coordinate_curve_spline(curve_parameter_value) + for coordinate_curve_spline in interpolation] + M = self.codomain() + p = M.point(interpolated_coordinates, chart=self._chart) + Tp = M.tangent_space(p) + + evaluated_tangent_vector_components = [coordinate_curve_spline.derivative(curve_parameter_value, order=1) + for coordinate_curve_spline in interpolation] + basis = self._chart.frame().at(p) + v = Tp(evaluated_tangent_vector_components, basis=basis) + return v + + raise TypeError("Unexpected type of interpolation object.") + + + + @options(thickness=1, plot_points=75, plot_points_tangent=10, aspect_ratio='automatic', scale=1) + def plot(self, display_tangent=False, color_tangent='blue', + interpolation_key=None, ambient_coords=None, prange=None, + include_end_point=(True, True), end_point_offset=(0.001, 0.001), + color='red', style='-', label_axes=True, **kwds): + r""" + Plots the 2D or 3D projection of the curve onto the space of the chosen + two or three ambient coordinates, based on the interpolation of a + numerical solution previously computed." + + TESTS:: + sage: #TO DO + + """ + + # + # Get the @options from kwds + # + thickness = kwds.pop('thickness') + plot_points = kwds.pop('plot_points') + aspect_ratio = kwds.pop('aspect_ratio') + + # + # Coordinates of the chart w.r.t. which the curve is plotted + # + if ambient_coords is None: + ambient_coords = self._chart[:] # all chart coordinates are used + n_pc = len(ambient_coords) + if n_pc != 2 and n_pc !=3: + raise ValueError("the number of coordinates involved in the " + + "plot must be either 2 or 3, not {}".format(n_pc)) + # indices of plot coordinates + ind_pc = [self._chart[:].index(pc) for pc in ambient_coords] + + # + # Parameter range for the plot + # + param_min = numerical_approx(self.domain().lower_bound()) + param_max = numerical_approx(self.domain().upper_bound()) + + if prange is None: + prange = (param_min, param_max) + elif not isinstance(prange, (tuple, list)): + raise TypeError("{} is neither a tuple nor a list".format(prange)) + elif len(prange) != 2: + raise ValueError("the argument prange must be a tuple/list " + + "of 2 elements") + + tmin = numerical_approx(prange[0]) + tmax = numerical_approx(prange[1]) + + if tmin < param_min or tmin > param_max or tmax < param_min or tmax > param_max: + raise ValueError("Parameter range should be a subinterval of the curve domain ({}).".format(self.domain())) + + if not include_end_point[0]: + tmin = tmin + end_point_offset[0] + + if not include_end_point[1]: + tmax = tmax - end_point_offset[1] + + tmin = numerical_approx(tmin) + tmax = numerical_approx(tmax) + + # + # List of points for the plot curve + # + if interpolation_key==None: + if 'cubic spline' in self._interpolations.keys(): + interpolation_key = 'cubic spline' + else: + interpolation_key = self._interpolations.keys()[0] # will raise error if + # self._interpolations empty + print("Plotting from the interpolation associated " + + "with the key '{}' ".format(interpolation_key) + + "by default...") + elif interpolation_key not in self._interpolations.keys(): + raise ValueError("No existing key '{}' referring to any interpolation.".format(interpolation_key)) + + interpolation = self._interpolations[interpolation_key] + + if isinstance(interpolation[0], Spline): #partial test, in case + # future interpolation objects do not contain lists of instances of + # the Spline class + plot_curve = [] + dt = (tmax - tmin) / (plot_points - 1) + t = tmin + + + for i in range(plot_points): + plot_curve.append( [interpolation[j](t) for j in ind_pc] ) + t += dt + + if display_tangent: + from sage.plot.graphics import Graphics + from sage.plot.arrow import arrow2d + from sage.plot.plot3d.shapes import arrow3d + + scale = kwds.pop('scale') + plot_points_tangent = kwds.pop('plot_points_tangent', None) + if plot_points_tangent is None: + plot_points_tangent = plot_points + print("Plotting as many tangent vectors as points.") + + plot_vectors = Graphics() + dt = (tmax - tmin) / (plot_points_tangent - 1) + t = tmin + + for i in range(plot_points_tangent): + # interpolated ambient coordinates: + xp = [interpolation[j](t) for j in ind_pc] + + # tangent vector ambiant components evaluated from the interpolation: + vcomp = [coordinate_curve_spline.derivative(t, order=1) + for coordinate_curve_spline in interpolation] + + coord_tail = xp + coord_head = [xp[i] + scale*vcomp[i] for i in ind_pc] + + if coord_head != coord_tail: + if n_pc == 2: + plot_vectors += arrow2d(tailpoint=coord_tail, + headpoint=coord_head, + color=color_tangent) + else: + plot_vectors += arrow3d(coord_tail, coord_head, + color=color_tangent) + t += dt + return plot_vectors + DifferentiableCurve._graphics(self, plot_curve, + ambient_coords, thickness=thickness, + aspect_ratio=aspect_ratio, color=color, + style=style, label_axes=label_axes) + + + return DifferentiableCurve._graphics(self, plot_curve, ambient_coords, thickness=thickness, + aspect_ratio=aspect_ratio, color=color, + style=style, label_axes=label_axes) + + raise TypeError("Unexpected type of interpolation object.") + + + +class IntegratedAutoparallelCurve(IntegratedCurve): + r""" + Constructs a numerical autoparallel curve. + + INPUT: + TO DO + + """ + + def __init__(self, parent, affine_connection, curve_parameter, initial_tangent_vector, + chart=None, parameters=None, name=None, latex_name=None, + is_isomorphism=False, is_identity=False): + + r"""Construct the autoparallel curve with respect to the given affine connection with the given initial tangent vector.""" + + from sage.symbolic.ring import SR + + dim = parent.codomain().dim() + equations_rhs = [] + + # setting the chart to gain access to the coordinate functions + if chart is None: + chart = parent.codomain().default_chart() + + coordinate_functions = chart[:] + velocities = chart.symbolic_velocities() + gamma = affine_connection.coef() + + for alpha in range(dim): + rhs = SR(0) + for mu in range(dim): + for nu in range(dim): + rhs -= gamma[alpha, mu, nu].expr()*velocities[mu]*velocities[nu] + equations_rhs += [rhs.simplify_full()] + + IntegratedCurve.__init__(self, parent, equations_rhs, velocities, curve_parameter, + initial_tangent_vector, chart=chart, parameters=parameters, + name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, + is_identity=is_identity) + + self._affine_connection = affine_connection + + + def _repr_(self): + r""" + Returns a string representation of ``self``. + + TESTS:: + sage: #TO DO + + """ + + description = "Integrated autoparallel curve " + if self._name is not None: + description += self._name + " " + description += "in the {}".format(self._codomain) + return description + + + + def system(self): + r""" + Returns the system defining the autoparallel curve : chart, equations and initial conditions + + TESTS:: + sage: #TO DO + + """ + + initial_tangent_space = self._initial_tangent_vector.parent() + initial_point = initial_tangent_space.base_point() # gets the initial + # point as the base point of the tangent space which initial tangent vector belongs to + initial_point_coordinates = initial_point.coordinates(self._chart) # will + # raise error if coordinates in chart are not known + initial_point_coordinates = [coord for coord in initial_point_coordinates] # converts + # to list since was previously a tuple + + initial_coordinate_basis = self._chart.frame().at(initial_point) + initial_tangent_vector_components = self._initial_tangent_vector[:, initial_coordinate_basis] # will + # raise error if components in coordinate basis are not known + + description = "Autoparallel curve " + if self._name is not None: + description += self._name + " " + description += "in the {} equipped with the {}, and integrated over the {} ".format(self.codomain(), self._affine_connection, self.domain()) + description += "as a solution to the following equations, written with respect to the {}:\n\n".format(self._chart) + + description += "Initial point: {} with coordinates {} in {}\n".format(initial_point, + initial_point_coordinates, self._chart) + description += "Initial tangent vector: {} with components {} in {}\n\n".format(self._initial_tangent_vector, + initial_tangent_vector_components, self._chart) + + for coord_func, velocity, eqn in zip(self._chart[:], self._velocities, self._equations_rhs): + description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) + description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) + print(description) + + return [self._equations_rhs, self._initial_tangent_vector, self._chart] + + + +class IntegratedGeodesic(IntegratedAutoparallelCurve): + r""" + Constructs a numerical geodesic on the manifold. + + INPUT: + TO DO + + """ + + def __init__(self, parent, metric, curve_parameter, initial_tangent_vector, + chart=None, parameters=None, name=None, latex_name=None, + is_isomorphism=False, is_identity=False): + + r"""Construct the geodesic curve with respect to the given metric with the given initial tangent vector.""" + + + IntegratedAutoparallelCurve.__init__(self, parent, metric.connection(), curve_parameter, + initial_tangent_vector, chart=chart, parameters=parameters, + name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, + is_identity=is_identity) + + self._metric = metric + + + + def _repr_(self): + r""" + Returns a string representation of ``self``. + + TESTS:: + sage: #TO DO + + """ + + description = "Integrated geodesic " + if self._name is not None: + description += self._name + " " + description += "in the {}".format(self._codomain) + return description + + + + def system(self): + r""" + Returns the system defining the geodesic : chart, equations and initial conditions + + TESTS:: + sage: #TO DO + + """ + + initial_tangent_space = self._initial_tangent_vector.parent() + initial_point = initial_tangent_space.base_point() # gets the initial + # point as the base point of the tangent space which initial tangent vector belongs to + initial_point_coordinates = initial_point.coordinates(self._chart) # will + # raise error if coordinates in chart are not known + initial_point_coordinates = [coord for coord in initial_point_coordinates] # converts + # to list since was previously a tuple + + initial_coordinate_basis = self._chart.frame().at(initial_point) + initial_tangent_vector_components = self._initial_tangent_vector[:, initial_coordinate_basis] # will + # raise error if components in coordinate basis are not known + + description = "Geodesic " + if self._name is not None: + description += self._name + " " + description += "in the {} equipped with the {}, and integrated over the {} ".format(self.codomain(), self._metric, self.domain()) + description += "as a solution to the following geodesic equations, written with respect to the {}:\n\n".format(self._chart) + + description += "Initial point: {} with coordinates {} in {}\n".format(initial_point, + initial_point_coordinates, self._chart) + description += "Initial tangent vector: {} with components {} in {}\n\n".format(self._initial_tangent_vector, + initial_tangent_vector_components, self._chart) + + for coord_func, velocity, eqn in zip(self._chart[:], self._velocities, self._equations_rhs): + description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) + description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) + print(description) + + return [self._equations_rhs, self._initial_tangent_vector, self._chart] diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 609e28006c7..fda5836a718 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -2570,6 +2570,181 @@ def curve(self, coord_expression, param, chart=None, coord_expression = {chart: (coord_expression,)} return curve_set(coord_expression, name=name, latex_name=latex_name) + def integrated_curve(self, equations_rhs, velocities, curve_param, + initial_tangent_vector, chart=None, parameters=None, + name=None, latex_name=None, is_isomorphism=False, + is_identity=False): + + r""" + Constructs a numerical curve defined by a system of second order differential + equations in the coordinate functions. + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + for details. + + INPUT: TO COMPLETE ! ! ! ! ! + + - `` + - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, where + + * ``t`` is the curve parameter used in ``coord_expression``; + * ``t_min`` is its minimal (finite) value; + * ``t_max`` its maximal (finite) value; + + - ``name`` -- (default: ``None``) string; symbol given to the curve + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote + the curve; if none is provided, ``name`` will be used + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + + EXAMPLES: + + #TO DO + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + for more examples, including plots. + + """ + from sage.manifolds.differentiable.real_line import RealLine + from sage.manifolds.differentiable.integrated_curve import IntegratedCurve + if len(curve_param) != 3: + raise ValueError("the argument 'curve_param' must be of the form " + + "(t, t_min, t_max)") + t = curve_param[0] + t_min = curve_param[1] + t_max = curve_param[2] + real_field = RealLine(names=(repr(t),)) + interval = real_field.open_interval(t_min, t_max) + curve_set = Hom(interval, self) + return IntegratedCurve(curve_set, equations_rhs, velocities, t, + initial_tangent_vector, chart=chart, parameters=parameters, + name=name, latex_name=latex_name, + is_isomorphism=is_isomorphism, + is_identity=is_identity) #use someone's element_constructor ? + + + + def integrated_autoparallel_curve(self, affine_connection, curve_param, initial_tangent_vector, chart=None, + name=None, latex_name=None, is_isomorphism=False, + is_identity=False): + + r""" + + Constructs an numerical autoparallel curve. + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.curve.IntegratedCurve` + for details. + + INPUT: TO COMPLETE ! ! ! ! + + - `` + - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, where + + * ``t`` is the curve parameter used in ``coord_expression``; + * ``t_min`` is its minimal (finite) value; + * ``t_max`` its maximal (finite) value; + + - ``name`` -- (default: ``None``) string; symbol given to the curve + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote + the curve; if none is provided, ``name`` will be used + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + + EXAMPLES: + + #TO DO + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + for more examples, including plots. + + """ + + from sage.manifolds.differentiable.real_line import RealLine + from sage.manifolds.differentiable.integrated_curve import IntegratedAutoparallelCurve + if len(curve_param) != 3: + raise ValueError("the argument 'curve_param' must be of the form " + + "(t, t_min, t_max)") + t = curve_param[0] + t_min = curve_param[1] + t_max = curve_param[2] + real_field = RealLine(names=(repr(t),)) + interval = real_field.open_interval(t_min, t_max) + curve_set = Hom(interval, self) + return IntegratedAutoparallelCurve(curve_set, affine_connection, t, initial_tangent_vector, chart=chart, + name=name, latex_name=latex_name, + is_isomorphism=is_isomorphism, + is_identity=is_identity) #use someone's element_constructor ? + + + + def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, chart=None, + name=None, latex_name=None, is_isomorphism=False, + is_identity=False): + + r""" + + Constructs a numerical geodesic. + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.curve.IntegratedAutoparallelCurve` + for details. + + INPUT: TO COMPLETE ! ! ! ! + + - `` + - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, where + + * ``t`` is the curve parameter used in ``coord_expression``; + * ``t_min`` is its minimal (finite) value; + * ``t_max`` its maximal (finite) value; + + - ``name`` -- (default: ``None``) string; symbol given to the curve + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote + the curve; if none is provided, ``name`` will be used + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + + EXAMPLES: + + #TO DO + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + for more examples, including plots. + + """ + from sage.manifolds.differentiable.real_line import RealLine + from sage.manifolds.differentiable.integrated_curve import IntegratedGeodesic + if len(curve_param) != 3: + raise ValueError("the argument 'curve_param' must be of the form " + + "(t, t_min, t_max)") + t = curve_param[0] + t_min = curve_param[1] + t_max = curve_param[2] + real_field = RealLine(names=(repr(t),)) + interval = real_field.open_interval(t_min, t_max) + curve_set = Hom(interval, self) + return IntegratedGeodesic(curve_set, metric, t, initial_tangent_vector, chart=chart, + name=name, latex_name=latex_name, + is_isomorphism=is_isomorphism, + is_identity=is_identity) #use someone's element_constructor ? + def affine_connection(self, name, latex_name=None): r""" Define an affine connection on the manifold. From c994a452524712c019fb297642d424c98a0a4c97 Mon Sep 17 00:00:00 2001 From: Zachary Gershkoff Date: Thu, 18 May 2017 18:59:15 -0500 Subject: [PATCH 026/184] Corrected merge conflict output --- src/doc/en/developer/git_trac.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/developer/git_trac.rst b/src/doc/en/developer/git_trac.rst index bd30c04870d..d18160a39de 100644 --- a/src/doc/en/developer/git_trac.rst +++ b/src/doc/en/developer/git_trac.rst @@ -486,7 +486,7 @@ The file now looks like this:: <<<<<<< HEAD if i > 1: return fibonacci(i-1) * fibonacci(i-2) - return i + return [0, 1][i] ======= return fibonacci(i-1) + fibonacci(i-2) >>>>>>> 41675dfaedbfb89dcff0a47e520be4aa2b6c5d1b From 2328a7f7a61314952a72fd35a269770b5e2bb114 Mon Sep 17 00:00:00 2001 From: Karim Van Aelst Date: Wed, 31 May 2017 14:54:28 +0200 Subject: [PATCH 027/184] bugsremoved, documentation completed, mapping implemented --- src/sage/manifolds/differentiable/chart.py | 90 +- src/sage/manifolds/differentiable/curve.py | 26 +- .../differentiable/integrated_curve.py | 2847 ++++++++++++----- src/sage/manifolds/differentiable/manifold.py | 346 +- 4 files changed, 2443 insertions(+), 866 deletions(-) diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 777e79fdfa1..fb6a2c10651 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -550,32 +550,76 @@ def restrict(self, subset, restrictions=None): def symbolic_velocities(self, left='D', right=None): r""" - - Returns a list of symbolic variables ready to be used by the user as - the derivatives of the coordinate functions with respect to a curve - parameter (that is the velocities along the curve). - It may actually serve to denote anything else than velocities, with a - name including the coordinate functions. - + Returns a tuple of symbolic variables ready to be used by the + user as the derivatives of the coordinate functions with respect + to a curve parameter (i.e. the velocities along the curve). + It may actually serve to denote anything else than velocities, + with a name including the coordinate functions. + The choice of strings provided as 'left' and 'right' arguments + is not entirely free since it must comply with Python + prescriptions. + + INPUT: + + - ``left`` -- (default: ``D``) string to concatenate to the left + of each coordinate functions of the chart + - ``right`` -- (default: ``None``) string to concatenate to the + right of each coordinate functions of the chart + + OUTPUT: + + - a list of symbolic expressions with the desired names + + EXAMPLE: + + Symbolic derivatives of the Cartesian coordinates of the + 3-dimensional Euclidean space:: + + sage: R3 = Manifold(3, 'R3', start_index=1) + sage: cart. = R3.chart() + sage: R3 = Manifold(3, 'R3', start_index=1) + sage: cart. = R3.chart() + sage: Deriv1 = cart.symbolic_velocities(); Deriv1 + [DX, DY, DZ] + sage: Deriv2 = cart.symbolic_velocities(left='d', right="_dt") + sage: Deriv2 + [dX_dt, dY_dt, dZ_dt] + + TESTS:: + + sage: R3 = Manifold(3, 'R3', start_index=1) + sage: cart. = R3.chart() + sage: D = cart.symbolic_velocities(); D + [DX, DY, DZ] + sage: D = cart.symbolic_velocities(left='d', right="/dt"); D + Traceback (most recent call last): + ... + ValueError: The name "dX/dt" is not a valid Python + identifier. + sage: D = cart.symbolic_velocities(left='d', right="_dt"); D + [dX_dt, dY_dt, dZ_dt] + sage: D = cart.symbolic_velocities(left='', right="'"); D + Traceback (most recent call last): + ... + ValueError: The name "X'" is not a valid Python + identifier. + sage: D = cart.symbolic_velocities(left='', right="_dot"); D + [X_dot, Y_dot, Z_dot] + """ - - from sage.calculus.var import var - - string_velocities = [] - velocities = [] - - for coord_func in self[:]: - string_velocities += [left + "{}".format(coord_func)] # will raise - #error in case left is not a string - + + from sage.symbolic.ring import var + + string_velocities = [left + format(coord_func) + for coord_func in self[:]] # will raise + # error in case left is not a string + if right is not None: - string_velocities = [string_vel + right for string_vel in string_velocities] # will - # raise error in case right is not a string + string_velocities = [string_vel + right for string_vel + in string_velocities] # will raise + # error in case right is not a string - for string_vel in string_velocities: - velocities += [var(string_vel)] - - return velocities + return list(var(string_velocities)) #***************************************************************************** diff --git a/src/sage/manifolds/differentiable/curve.py b/src/sage/manifolds/differentiable/curve.py index 4e45d203896..e6504c4e06b 100644 --- a/src/sage/manifolds/differentiable/curve.py +++ b/src/sage/manifolds/differentiable/curve.py @@ -63,7 +63,7 @@ class DifferentiableCurve(DiffMap): the values being lists or tuples of `n` symbolic expressions of `t`, where `n` is the dimension of `M` - ``name`` -- (default: ``None``) string; symbol given to the curve - - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - ``is_isomorphism`` -- (default: ``False``) determines whether the constructed object is a diffeomorphism; if set to ``True``, @@ -856,20 +856,26 @@ def plot(self, chart=None, ambient_coords=None, mapping=None, prange=None, for j in ind_pc] ) t += dt - return self._graphics(plot_curve, ambient_coords, thickness=thickness, + return self._graphics(plot_curve, ambient_coords, + thickness=thickness, aspect_ratio=aspect_ratio, color= color, - style=style, label_axes=label_axes) + style=style, label_axes=label_axes) - def _graphics(self, plot_curve, ambient_coords, thickness=1, aspect_ratio='automatic', - color='red', style='-', label_axes=True): + def _graphics(self, plot_curve, ambient_coords, thickness=1, + aspect_ratio='automatic', color='red', style='-', + label_axes=True): r""" - Plots a 2D or 3D curve in a Cartesian graph with axes labeled by the - ambient coordinates. - - TESTS:: - sage: #TO DO + Plots a 2D or 3D curve in a Cartesian graph with axes labeled by + the ambient coordinates; it is invoked by the methods + :meth:`plot` of + :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve`, + and its subclasses + (:class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve`, + :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve`, + and + :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic`). """ diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index 394a501938f..630073f79f9 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -1,16 +1,29 @@ r""" Integrated Curves in Manifolds -Given a differentiable manifold `M`, an *integrated curve* curve in `M` is a -differentiable curve constructed as a numerical solution to a system of -second order differential equations. +Given a differentiable manifold `M`, an *integrated curve* curve in `M` +is a differentiable curve constructed as a numerical solution to a +system of second order differential equations. Integrated curves are implemented by :class:`IntegratedCurve`, which the -classes :class:`IntegratedAutoparallelCurve` and :class:`IntegratedGeodesic` -inherit. +classes :class:`IntegratedAutoparallelCurve` and +:class:`IntegratedGeodesic` inherit. + +AUTHORS: + +- Karim Van Aelst (2017): initial version """ +#*********************************************************************** +# Copyright (C) 2017 Karim Van Aelst +# +# 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. +# http://www.gnu.org/licenses/ +#*********************************************************************** + from sage.symbolic.expression import Expression from sage.rings.infinity import Infinity from sage.calculus.desolvers import desolve_system_rk4 @@ -22,35 +35,40 @@ from sage.misc.decorators import options from sage.misc.functional import numerical_approx - - class IntegratedCurve(DifferentiableCurve): - r""" + r""" Given a chart with coordinates denoted :MATH:`(x_{1}, ..., x_{n})`, an instance of :class:`IntegratedCurve` is a curve - :MATH:`t \mapsto (x_{1}(t), ..., x_{n}(t))` constructed as a numerical - solution to a system of second order differential equations satisfied by - the coordinate curves :MATH:`t \mapsto x_{i}(t)`. - + :MATH:`t \mapsto (x_{1}(t), ..., x_{n}(t))` constructed as a + numerical solution to a system of second order differential + equations satisfied by the coordinate curves + :MATH:`t \mapsto x_{i}(t)`. + INPUT: - ``parent`` -- :class:`~sage.manifolds.differentiable.manifold_homset.DifferentiableCurveSet` the set of curves `\mathrm{Hom}(I, M)` to which the curve belongs - - ``equations_rhs`` -- list of the right-hand sides of the equations on - the velocities only (the term *velocity* referring to the derivatives - :MATH:`d x_{i} / dt` of the coordinate curves) - - ``velocities`` -- list of the symbolic expressions used in ``equations_rhs`` - to denote the velocities - - ``curve_parameter`` -- symbolic expression used in ``equations_rhs`` - to denote the parameter of the curve (denoted :MATH:`t` in the - descriptions above) + - ``equations_rhs`` -- list of the right-hand sides of the equations + on the velocities only (the term *velocity* referring to the + derivatives :MATH:`d x_{i} / dt` of the coordinate curves) + - ``velocities`` -- list of the symbolic expressions used in + ``equations_rhs`` to denote the velocities + - ``curve_parameter`` -- symbolic expression used in + ``equations_rhs`` to denote the parameter of the curve (denoted + :MATH:`t` in the descriptions above) - ``initial_tangent_vector`` -- :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` initial tangent vector of the curve + - ``chart`` -- (default: ``None``) chart on the manifold in + which the equations are given; if ``None`` the default chart + of the manifold is assumed + - ``parameters`` -- list of the symbolic expressions used in + ``equations_rhs`` and ``initial_tangent_vector`` other than the + coordinates, the velocities and the curve parameter - ``name`` -- (default: ``None``) string; symbol given to the curve - - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the - the curve; if none is provided, ``name`` will be used + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the curve; if none is provided, ``name`` will be used - ``is_isomorphism`` -- (default: ``False``) determines whether the constructed object is a diffeomorphism; if set to ``True``, then `M` must have dimension one @@ -59,225 +77,267 @@ class IntegratedCurve(DifferentiableCurve): then `M` must coincide with the domain of the curve EXAMPLE: - + Motion of a charged particle in an axial magnetic field linearly increasing in time and exponentially decreasing in space: - + .. MATH:: - \mathbf{B}(t,\mathbf{x}) = \frac{B_{0}t}{T} \exp \left( - \frac{ x_{1}^{2} + x_{2}^{2} }{ L^{2} } \right) \mathbf{e_{3}}. - + \mathbf{B}(t,\mathbf{x}) = \frac{B_{0}t}{T} \exp \left( + -\frac{ x_{1}^{2} + x_{2}^{2} }{ L^{2} } \right) \mathbf{e_{3}}. + Equations of motion are: - + .. MATH:: - - \ddot{x}_{1}(t) &= \frac{qB(t, \mathbf{x}(t))}{m} \dot{x}_{2}(t) \\ - \ddot{x}_{2}(t) &= - \frac{qB(t, \mathbf{x}(t))}{m} \dot{x}_{1}(t) \\ + + \ddot{x}_{1}(t) &= \frac{qB(t,\mathbf{x}(t))}{m} + \dot{x}_{2}(t) \\ + \ddot{x}_{2}(t) &= - \frac{qB(t, \mathbf{x}(t))}{m} + \dot{x}_{1}(t) \\ \ddot{x}_{3}(t) &= 0 - - Start with declaring a chart on a 3-dimensional manifold and the various - symbolic expressions denoting the equations, velocities and parameters:: + + Start with declaring a chart on a 3-dimensional manifold and the + symbolic expressions denoting the velocities and the various + parameters:: sage: M = Manifold(3, 'M') sage: X. = M.chart() sage: var('t B_0 m q L T') (t, B_0, m, q, L, T) sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) - sage: D = X.symbolic_velocities() ; D + sage: D = X.symbolic_velocities(); D [Dx1, Dx2, Dx3] sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] - + Set the initial conditions:: - + sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - + Declare an integrated curve and display information relative to it:: - + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) ; c - Integrated curve c in the 3-dimensional differentiable manifold M - sage: c.system() - Curve c in the 3-dimensional differentiable manifold M integrated over - the Real interval (0, 5) as a solution to the following system, written - with respect to the Chart (M, (x1, x2, x3)): - - Initial point: Point p on the 3-dimensional differentiable manifold M - with coordinates [0, 0, 0] in Chart (M, (x1, x2, x3)) - Initial tangent vector: Tangent vector at Point p on the 3-dimensional - differentiable manifold M with components [1, 0, 1] in Chart (M, (x1, x2, x3)) - - d(x1)/dt = Dx1 - d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) - d(x2)/dt = Dx2 - d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) - d(x3)/dt = Dx3 - d(Dx3)/dt = 0 - - Generate a solution of the system and an interpolation of this solution:: - - sage: c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + ....: parameters=[B_0, m, q, L, T]); c + Integrated curve c in the 3-dimensional differentiable + manifold M + sage: sys = c.system() + Curve c in the 3-dimensional differentiable manifold M + integrated over the Real interval (0, 5) as a solution to the + following system, written w.r.t. + Chart (M, (x1, x2, x3)): + + Initial point: Point p on the 3-dimensional differentiable + manifold M with coordinates [0, 0, 0] w.r.t. + Chart (M, (x1, x2, x3)) + Initial tangent vector: Tangent vector at Point p on + the 3-dimensional differentiable manifold M with + components [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) + + d(x1)/dt = Dx1 + d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(x2)/dt = Dx2 + d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(x3)/dt = Dx3 + d(Dx3)/dt = 0 + + + Generate a solution of the system and an interpolation of this + solution:: + + sage: sol = c.solve(step=0.2, + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, ....: solution_key='carac time 1') Performing 4th order Runge-Kutta integration by default... - Numerical integration completed. Resulting list of points was associated - with the key 'carac time 1' (if this key already referred to a former - numerical solution, such a solution was erased). - sage: c.interpolate(solution_key='carac time 1', interpolation_key='interp 1') + Numerical integration completed. Resulting list of points was + associated with the key 'carac time 1' (if this key already + referred to a former numerical solution, such a solution was + erased). + sage: interp = c.interpolate(solution_key='carac time 1', + ....: interpolation_key='interp 1') Performing cubic spline interpolation by default... - Interpolation completed and associated with the key 'interp 1' + Interpolation completed and associated with the key 'interp 1' (if this key already referred to a former interpolation, such an interpolation was erased). - - Such an interpolation is required to evaluate the curve and the vector - tangent to the curve for any value of the curve parameter:: + + Such an interpolation is required to evaluate the curve and the + vector tangent to the curve for any value of the curve parameter:: sage: c(1.9) - Evaluating point coordinates from the interpolation associated with - the key 'interp 1' by default... - [1.3776707219621374, -0.9000776970132945, 1.9] - sage: v = c.tangent_vector_eval_at(4.3) - Evaluating tangent vector components from the interpolation associated + Evaluating point coordinates from the interpolation associated with the key 'interp 1' by default... + [1.3776707219621374, -0.9000776970132945, 1.9] + sage: v = c.tangent_vector_eval_at(4.3) + Evaluating tangent vector components from the interpolation + associated with the key 'interp 1' by default... sage: v - Tangent vector at Point on the 3-dimensional differentiable manifold M + Tangent vector at Point on the 3-dimensional differentiable + manifold M sage: v.display() - -0.9303968397216424 d/dx1 - 0.3408080563014475 d/dx2 + 1.0000000000000004 d/dx3 - - Plotting a numerical solution (with or without its tangent vector field) - also requires the solution to be interpolated at least once:: - - sage: c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], interpolation_key='interp 1', - ....: thickness=2.5, display_tangent=True, plot_points=200, - ....: plot_points_tangent=10, scale=0.5, color='blue', - ....: color_tangent='red') + -0.9303968397216424 d/dx1 - 0.3408080563014475 d/dx2 + + 1.0000000000000004 d/dx3 + + Plotting a numerical solution (with or without its tangent vector + field) also requires the solution to be interpolated at least once:: + + sage: c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], + ....: interpolation_key='interp 1', thickness=2.5, + ....: display_tangent=True, plot_points=200, + ....: plot_points_tangent=10, scale=0.5, + ....: color='blue', color_tangent='red') + A tiny final offset equal to the value of 'end_point_offset[1]' + (= 0.001) was introduced in order to safely compute the last + tangent vector from the interpolation. sage: c_plot_2d_1.show() .. PLOT:: - + M = Manifold(3, 'M') X = M.chart('x1 x2 x3') var('x1 x2 x3 t B_0 m q L T') B = B_0*t/T*exp(-(x1**2 + x2**2)/L**2) - D = X.symbolic_velocities() ; D + D = X.symbolic_velocities() eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] p = M.point((0,0,0), name='p') Tp = M.tangent_space(p) v = Tp((1,0,1)) c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', parameters=[B_0, m, q, L, T]) - c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + c.solve(step=0.2, + parameters_values={B_0:1, m:1, q:1, L:10, T:1}, solution_key='carac time 1') - c.interpolate(solution_key='carac time 1', interpolation_key='interp 1') - c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], interpolation_key='interp 1', - thickness=2.5, display_tangent=True, plot_points=200, - plot_points_tangent=10, scale=0.5, color='blue', - color_tangent='red') + c.interpolate(solution_key='carac time 1', + interpolation_key='interp 1') + c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], + interpolation_key='interp 1', thickness=2.5, + display_tangent=True, plot_points=200, + plot_points_tangent=10, scale=0.5, color='blue', + color_tangent='red') sphinx_plot(c_plot_2d_1) - - An instance of :class:`IntegratedCurve` may store several numerical solutions - and interpolations:: - sage: c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:100}, - ....: solution_key='carac time 100') - Performing 4th order Runge-Kutta integration by default... - Numerical integration completed. Resulting list of points was associated - with the key 'carac time 100' (if this key already referred to a former - numerical solution, such a solution was erased). - sage: c.interpolate(solution_key='carac time 100', interpolation_key='interp 100') - Performing cubic spline interpolation by default... - Interpolation completed and associated with the key 'interp 100' - (if this key already referred to a former interpolation, such an - interpolation was erased). - sage: c_plot_3d_100 = c.plot(interpolation_key='interp 100', thickness=2.5, - ....: display_tangent=True, plot_points=200, - ....: plot_points_tangent=10, scale=0.5, - ....: color='green', color_tangent='orange') - sage: c_plot_3d_1 = c.plot(interpolation_key='interp 1', thickness=2.5, - ....: display_tangent=True, plot_points=200, - ....: plot_points_tangent=10, scale=0.5, - ....: color='blue', color_tangent='red') - sage: (c_plot_3d_1 + c_plot_3d_100).show() + An instance of :class:`IntegratedCurve` may store several numerical + solutions and interpolations:: + + sage: sol = c.solve(step=0.2, + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:100}, + ....: solution_key='carac time 100', verbose=False) + sage: interp = c.interpolate(solution_key='carac time 100', + ....: interpolation_key='interp 100', verbose=False) + sage: c_plot_3d_100 = c.plot(interpolation_key='interp 100', + ....: thickness=2.5, display_tangent=True, + ....: plot_points=200, plot_points_tangent=10, + ....: scale=0.5, color='green', + ....: color_tangent='orange', verbose=False) + sage: c_plot_3d_1 = c.plot(interpolation_key='interp 1', + ....: thickness=2.5, display_tangent=True, + ....: plot_points=200, plot_points_tangent=10, + ....: scale=0.5, color='blue', + ....: color_tangent='red', verbose=False) + sage: viewer3D = 'threejs' + sage: graph = c_plot_3d_1 + c_plot_3d_100 + sage: graph.show(viewer = viewer3D) .. PLOT:: - + M = Manifold(3, 'M') X = M.chart('x1 x2 x3') var('x1 x2 x3 t B_0 m q L T') B = B_0*t/T*exp(-(x1**2 + x2**2)/L**2) - D = X.symbolic_velocities() ; D + D = X.symbolic_velocities() eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] p = M.point((0,0,0), name='p') Tp = M.tangent_space(p) v = Tp((1,0,1)) c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', parameters=[B_0, m, q, L, T]) - c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + c.solve(step=0.2, + parameters_values={B_0:1, m:1, q:1, L:10, T:1}, solution_key='carac time 1') - c.interpolate(solution_key='carac time 1', interpolation_key='interp 1') - c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:100}, + c.interpolate(solution_key='carac time 1', + interpolation_key='interp 1') + c.solve(step=0.2, + parameters_values={B_0:1, m:1, q:1, L:10, T:100}, solution_key='carac time 100') - c.interpolate(solution_key='carac time 100', interpolation_key='interp 100') - c_plot_3d_1 = c.plot(interpolation_key='interp 1', thickness=2.5, - display_tangent=True, plot_points=200, - plot_points_tangent=10, scale=0.5, color='blue', - color_tangent='red') - c_plot_3d_100 = c.plot(interpolation_key='interp 100', thickness=2.5, - display_tangent=True, plot_points=200, - plot_points_tangent=10, scale=0.5, color='green', - color_tangent='orange') - sphinx_plot(c_plot_3d_1 + c_plot_3d_100) - + c.interpolate(solution_key='carac time 100', + interpolation_key='interp 100') + c_plot_3d_1 = c.plot(interpolation_key='interp 1', + thickness=2.5, display_tangent=True, + plot_points=200, plot_points_tangent=10, + scale=0.5, color='blue', color_tangent='red', + verbose=False) + c_plot_3d_100 = c.plot(interpolation_key='interp 100', + thickness=2.5, display_tangent=True, + plot_points=200, plot_points_tangent=10, + scale=0.5, color='green', + color_tangent='orange', verbose=False) + graph = c_plot_3d_1 + c_plot_3d_100 + sphinx_plot(graph) + """ - - def __init__(self, parent, equations_rhs, velocities, curve_parameter, - initial_tangent_vector, chart=None, parameters=None, name=None, - latex_name=None, is_isomorphism=False, is_identity=False): - + + def __init__(self, parent, equations_rhs, velocities, + curve_parameter, initial_tangent_vector, chart=None, + parameters=None, name=None, latex_name=None, + is_isomorphism=False, is_identity=False): r""" Constructs a numerical curve. - + TESTS:: - + sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns + [x1], D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - ValueError: Number of equations should equal codomain dimension. - sage: c = M.integrated_curve(eqns, D + [x1], (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - ValueError: Number of velocities should equal codomain dimension. - sage: c = M.integrated_curve(eqns, D, (t, -oo, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - ValueError: Both boundaries of the interval need to be finite. - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), x1, name='c', - ....: parameters=[B_0, m, q, L, T]) + sage: c = M.integrated_curve(eqns + [x1], D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + Traceback (most recent call last): + ... + ValueError: Number of equations should equal codomain + dimension. + sage: c = M.integrated_curve(eqns, D + [x1],(t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + Traceback (most recent call last): + ... + ValueError: Number of velocities should equal codomain + dimension. + sage: c = M.integrated_curve(eqns, D, (t, -oo, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + Traceback (most recent call last): + ... + ValueError: Both boundaries of the interval need to be + finite. + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), x1, + ....: name='c', parameters=[B_0, m, q, L, T]) + Traceback (most recent call last): + ... TypeError: x1 should be a tangent vector. - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - parameters=[m, q, L, T]) - TypeError: B_0 should either be a coordinate function of B_0, or - one the corresponding velocities [Dx1, Dx2, Dx3], or the curve - parameter t, or one of the parameters [m, q, L, T]. - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) ; c - Integrated curve c in the 3-dimensional differentiable manifold M + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[m, q, L, T]) + Traceback (most recent call last): + ... + TypeError: B_0 should either be a coordinate function of + Chart (M, (x1, x2, x3)), or one of the corresponding + velocities [Dx1, Dx2, Dx3], or the curve parameter t, or + one of the parameters [m, q, L, T]. + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]); c + Integrated curve c in the 3-dimensional differentiable + manifold M sage: # TestSuite(c).run() # pickling and category failed - """ - - # starting with parent class method to initialize the four last arguments - DifferentiableCurve.__init__(self, parent, name=name, latex_name=latex_name, - is_isomorphism=is_isomorphism, - is_identity=is_identity) # (coord_expression=None) - + """ + + # starting with parent class method to initialize the four last + # arguments + DifferentiableCurve.__init__(self, parent, name=name, + latex_name=latex_name, is_isomorphism=is_isomorphism, + is_identity=is_identity) # (coord_expression=None) + # checking argument 'parent' domain = self.domain() if not isinstance(domain, OpenInterval): @@ -286,93 +346,106 @@ def __init__(self, parent, equations_rhs, velocities, curve_parameter, t_min = domain.lower_bound() t_max = domain.upper_bound() if t_min == -Infinity or t_max == +Infinity: - raise ValueError("Both boundaries of the interval need to be finite.") + raise ValueError("Both boundaries of the interval " + + "need to be finite.") - # checking argument 'equations_rhs' + # checking argument 'equations_rhs' codomain_dimension = self.codomain().dim() if len(equations_rhs) != codomain_dimension: - raise ValueError("Number of equations should equal codomain dimension.") + raise ValueError("Number of equations should equal " + + "codomain dimension.") for eqn in equations_rhs: if not isinstance(eqn, Expression): - raise TypeError("{} should be a symbolic expression.".format(eqn)) - # desolve_system_rk4 called in 'solve' method is in charge of raising - # errors about possibly remaining problems in argument equations_rhs + raise TypeError("{} should be ".format(eqn) + + "a symbolic expression.") + # desolve_system_rk4 called in 'solve' method is in charge of + # raising errors about possibly remaining problems in argument + # equations_rhs - # checking argument 'velocities' + # checking argument 'velocities' if len(velocities) != codomain_dimension: - raise ValueError("Number of velocities should equal codomain dimension.") + raise ValueError("Number of velocities should equal " + + "codomain dimension.") # checking argument 'curve_parameter' if not isinstance(curve_parameter, Expression): - raise TypeError("{} should be a symbolic expression.".format(curve_parameter)) - # desolve_system_rk4 called in 'solve' method is in charge of raising - # errors about possibly remaining problems in argument curve_parameter - + raise TypeError("{} should be ".format(curve_parameter) + + "a symbolic expression.") + # desolve_system_rk4 called in 'solve' method is in charge of + # raising errors about possibly remaining problems in argument + # curve_parameter + # checking argument 'initial_tangent_vector' if not isinstance(initial_tangent_vector, TangentVector): - raise TypeError("{} should be a tangent vector.".format(initial_tangent_vector)) + raise TypeError("{} ".format(initial_tangent_vector) + + "should be a tangent vector.") # setting the chart if chart is None: chart = self.codomain().default_chart() - - # checking argument 'parameters' + + # checking argument 'parameters' codomain_dimension = self.codomain().dim() if parameters is not None: for param in parameters: if not isinstance(param, Expression): - raise TypeError("{} should be a symbolic expression.".format(param)) - - # checking that equations_rhs only uses the chart coordinates, the velocities, - # the curve parameter and the parameters if there are - announced_variables = [coord_func for coord_func in chart[:]] - announced_variables += [vel for vel in velocities] + [curve_parameter] + raise TypeError("{} should be ".format(param) + + "a symbolic expression.") + + # checking that equations_rhs only uses the chart coordinates, + # the velocities, the curve parameter (and the parameters if + # there are) + announced_variables = [coord_func for coord_func in chart[:]] + announced_variables += [vel for vel in velocities] + announced_variables += [curve_parameter] if parameters is not None: announced_variables += [param for param in parameters] equations_rhs_variables = set() for eqn in equations_rhs: - equations_rhs_variables = equations_rhs_variables.union(eqn.variables()) + equations_rhs_variables=equations_rhs_variables.union(eqn.variables()) for rhs_variable in equations_rhs_variables: if rhs_variable not in announced_variables: str_error = "{} should either be ".format(rhs_variable) - str_error += "a coordinate function of {}, ".format(rhs_variable, chart) - str_error += "or one the corresponding velocities {}, ".format(velocities) - str_error += "or the curve parameter {}".format(curve_parameter) + str_error += "a coordinate function of " + str_error += "{}, ".format(chart) + str_error += "or one of the corresponding velocities " + str_error += "{}, ".format(velocities) + str_error += "or the curve parameter " + str_error += "{}".format(curve_parameter) if parameters is not None: - str_error += ", or one of the parameters {}".format(parameters) + str_error += ", or one of the parameters " + str_error += "{}".format(parameters) raise TypeError(str_error + ".") - + # defining all attributes - self._equations_rhs = [eqn for eqn in equations_rhs] # converts to list + self._equations_rhs = list(equations_rhs) # converts to list + # since might not already be a list (which is later required) + self._velocities = list(velocities) # converts to list # since might not already be a list (which is later required) - self._velocities = [vel for vel in velocities] # converts to list since - # might not already be a list (which is later required) self._curve_parameter = curve_parameter self._initial_tangent_vector = initial_tangent_vector self._chart = chart - self._parameters = parameters - self._solutions = {} # dictionary containing all numerically computed - # lists of points of the curve, the keys being chosen by the user when - # calling method 'solve' - self._interpolations = {} # dictionary containing lists of interpolation - # objects, each interpolation object implementing the interpolation of - # one of the numerical coordinate curves, and the keys being chosen by - # the user when calling method 'interpolate' - - + self._parameters = parameters + self._solutions = {} # dictionary containing all numerically + # computed lists of points of the curve, the keys being chosen + # by the user when calling method 'solve' + self._interpolations = {} # dictionary containing lists of + # interpolation objects, each interpolation object implementing + # the interpolation of one of the numerical coordinate curves, + # and the keys being chosen by the user when calling + # method 'interpolate' def _repr_(self): - r""" + r""" Returns a string representation of ``self``. TESTS:: - + sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] @@ -380,12 +453,14 @@ def _repr_(self): sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: parameters=[B_0, m, q, L, T]) ; c - Integrated curve in the 3-dimensional differentiable manifold M - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c' - ....: parameters=[B_0, m, q, L, T]) ; c - Integrated curve c in the 3-dimensional differentiable manifold M - + ....: parameters=[B_0, m, q, L, T]); c + Integrated curve in the 3-dimensional differentiable + manifold M + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]); c + Integrated curve c in the 3-dimensional differentiable + manifold M + """ description = "Integrated curve " @@ -394,286 +469,463 @@ def _repr_(self): description += "in the {}".format(self._codomain) return description + def system(self, verbose=True): + r""" + Provides a detailed description of the system defining the curve + and returns the system defining it: chart, equations and initial + conditions. + INPUT: + + - ``verbose`` -- (default: ``True``) prints a detailed + description of the curve + + OUTPUT: + + - list containing the attributes :attr:`equations_rhs`, + :attr:`initial_tangent_vector` and :attr:`chart` - def system(self): - r""" - Provides a detailed description of the system defining the curve. - TESTS:: - + sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - sage: c.system() - Curve c in the 3-dimensional differentiable manifold M integrated - over the Real interval (0, 5) as a solution to the following system, - written with respect to the Chart (M, (x1, x2, x3)): - - Initial point: Point p on the 3-dimensional differentiable manifold M - with coordinates [0, 0, 0] in Chart (M, (x1, x2, x3)) - Initial tangent vector: Tangent vector at Point p on the 3-dimensional - differentiable manifold M with components [1, 0, 1] in Chart (M, (x1, x2, x3)) - - d(x1)/dt = Dx1 - d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) - d(x2)/dt = Dx2 - d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) - d(x3)/dt = Dx3 - d(Dx3)/dt = 0 - + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + sage: sys = c.system() + Curve c in the 3-dimensional differentiable manifold M + integrated over the Real interval (0, 5) as a solution to + the following system, written w.r.t. + Chart (M, (x1, x2, x3)): + + Initial point: Point p on the 3-dimensional differentiable + manifold M with coordinates [0, 0, 0] w.r.t. + Chart (M, (x1, x2, x3)) + Initial tangent vector: Tangent vector at Point p on the + 3-dimensional differentiable manifold M with + components [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) + + d(x1)/dt = Dx1 + d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(x2)/dt = Dx2 + d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(x3)/dt = Dx3 + d(Dx3)/dt = 0 + + sage: sys_mute = c.system(verbose=False) + sage: sys_mute == sys + True + """ - - initial_tangent_space = self._initial_tangent_vector.parent() - initial_point = initial_tangent_space.base_point() # gets the initial point - # as the base point of the tangent space to which initial tangent vector belongs - initial_point_coordinates = initial_point.coordinates(self._chart) # will - # raise error if coordinates in chart are not known - initial_point_coordinates = [coord for coord in initial_point_coordinates] # converts - # to list since was previously a tuple - - initial_coordinate_basis = self._chart.frame().at(initial_point) - initial_tangent_vector_components = self._initial_tangent_vector[:, initial_coordinate_basis] # will - # raise error if components in coordinate basis are not known - - description = "Curve " - if self._name is not None: - description += self._name + " " - description += "in the {} integrated over the {} ".format(self.codomain(), - self.domain()) - - description += "as a solution to the following system, written with " - description += "respect to the {}:\n\n".format(self._chart) - description += "Initial point: {} with coordinates {} in {}\n".format(initial_point, - initial_point_coordinates, - self._chart) - description += "Initial tangent vector: {} with components {} in {}\n\n".format(self._initial_tangent_vector, - initial_tangent_vector_components, - self._chart) - - for coord_func, velocity, eqn in zip(self._chart[:], self._velocities, self._equations_rhs): - description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) - description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) - print(description) - - return [self._equations_rhs, self._initial_tangent_vector, self._chart] - - - - def solve(self, step=0.1, method=None, solution_key=None, parameters_values=None): + + v0 = self._initial_tangent_vector + chart = self._chart + + if verbose: + initial_tgt_space = v0.parent() + initial_pt = initial_tgt_space.base_point() # retrieves + # the initial point as the base point of the tangent space + # to which initial tangent vector belongs + initial_pt_coords = list(initial_pt.coordinates(chart)) + # previous line converts to list since would otherwise be a + # tuple ; will raise error if coordinates in chart are not + # known + + initial_coord_basis = chart.frame().at(initial_pt) + initial_tgt_vec_comps = v0[:, initial_coord_basis] # will + # raise error if components in coordinate basis are not + # known + + description = "Curve " + if self._name is not None: + description += self._name + " " + description += "in the {} ".format(self.codomain()) + description += "integrated over the " + description += "{} ".format(self.domain()) + description += "as a solution to the following system, " + description += "written w.r.t. " + description += "{}:\n\n".format(chart) + + description += "Initial point: {} ".format(initial_pt) + description += "with coordinates " + description += "{} ".format(initial_pt_coords) + description += "w.r.t. {}\n".format(chart) + + description += "Initial tangent vector: {} ".format(v0) + description += "with components " + description +="{}".format(initial_tgt_vec_comps) + description += " w.r.t. {}\n\n".format(chart) + + zip_sys = zip(chart[:],self._velocities,self._equations_rhs) + for coord_func, velocity, eqn in zip_sys: + description += "d({})/d{} = {}\n".format(coord_func, + self._curve_parameter, + velocity) + description += "d({})/d{} = {}\n".format(velocity, + self._curve_parameter, + eqn) + print(description) + + return [self._equations_rhs, v0, chart] + + def solve(self, step=0.1, method=None, solution_key=None, + parameters_values=None, verbose=True): r""" - Integrates the curve over the domain of integration. - - Integration scheme 'rk4' uses Sage solver *desolve_system_rk4*. + Integrates the curve numerically over the domain of integration. + + INPUT: + + - ``step`` -- (default: ``0.1``) step of integration + - ``method`` -- (default: ``None``) numerical scheme to use for + the integration of the curve; algorithms available are + + * 'rk4', which uses Sage solver ``desolve_system_rk4`` + + - ``solution_key`` -- (default: ``None``) key which the + resulting numerical solution will be associated to ; a default + value is given if none is provided + - ``parameters_values`` -- (default: ``None``) list of numerical + values of the parameters present in the system defining the + curve, to be substituted in the equations before integration + - ``verbose`` -- (default: ``True``) prints information about + the computation in progress + + OUTPUT: + + - list of the numerical points of the solution computed TESTS:: - + sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - sage: c.solve(parameters_values={m:1, q:1, L:10, T:1}) - ValueError: Numerical values should be provided for each of the - parameters [B_0, m, q, L, T]. - sage: c.solve(method='my method', parameters_values={B_0:1, m:1, q:1, L:10, T:1}) - ValueError: No available method of integration referred to as 'my method'. - sage: c.solve(parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + sage: sol = c.solve(parameters_values={m:1, q:1, L:10, T:1}) + Traceback (most recent call last): + ... + ValueError: Numerical values should be provided for each of + the parameters [B_0, m, q, L, T]. + sage: sol = c.solve(method='my method', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + Traceback (most recent call last): + ... + ValueError: No available method of integration referred to + as 'my method'. + sage: sol = c.solve( + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) Performing 4th order Runge-Kutta integration by default... - Resulting list of points will be associated with the key 'rk4' by - default. - Numerical integration completed. Resulting list of points was - associated with the key 'rk4' (if this key already referred to a - former numerical solution, such a solution was erased). + Resulting list of points will be associated with the key + 'rk4' by default. + Numerical integration completed. Resulting list of points + was associated with the key 'rk4' (if this key already + referred to a former numerical solution, such a solution + was erased). + sage: sol_mute = c.solve(verbose=False, + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + sage: sol_mute == sol + True """ if method is None: method = 'rk4' - print('Performing 4th order Runge-Kutta integration by default...') - + if verbose: + print("Performing 4th order Runge-Kutta integration " + + "by default...") + if solution_key is None: solution_key = method - print("Resulting list of points will be associated with the key '{}' by default.".format(solution_key)) - + if verbose: + print("Resulting list of points will be associated " + + "with the key '{}' ".format(solution_key) + + "by default.") + if method == 'rk4': + t_min = self.domain().lower_bound() + t_max = self.domain().upper_bound() + + eqns_rhs = self._equations_rhs + + v0 = self._initial_tangent_vector + chart = self._chart + + initial_tgt_space = v0.parent() + initial_pt = initial_tgt_space.base_point() # retrieves + # the initial point as the base point of the tangent space + # to which initial tangent vector belongs + initial_pt_coords = list(initial_pt.coordinates(chart)) + # previous line converts to list since would otherwise be a + # tuple (yet will need to be added to [t_min] later); will + # raise error if coordinates in chart cannot be obtained + + + initial_coord_basis = chart.frame().at(initial_pt) + initial_tgt_vec_comps = list(v0[:,initial_coord_basis])#idem + if self._parameters is not None: - if parameters_values is None or len(parameters_values) != len(self._parameters): - raise ValueError("Numerical values should be provided for each of the parameters {}.".format(self._parameters)) - eqns_rhs = [eqn.substitute(parameters_values) for eqn in self._equations_rhs] # will - # raise error if any element of parameters_values is not numerical - else: - eqns_rhs = self._equations_rhs - - coordinate_functions_list = [coord_func for coord_func in self._chart[:]] - - t_min = numerical_approx(self.domain().lower_bound()) - t_max = numerical_approx(self.domain().upper_bound()) - - initial_tangent_space = self._initial_tangent_vector.parent() - initial_point = initial_tangent_space.base_point() # gets the initial - # point as the base point of the tangent space to which the initial tangent vector belongs - initial_point_coordinates = initial_point.coordinates(self._chart) # will - # raise error if coordinates in chart cannot get known - initial_point_coordinates = [numerical_approx(coord) for coord - in initial_point_coordinates] # converts - # to list since was previously a tuple (so it can be added to [t_min] below), - # and gets numerical value in case some coordinates contained expressions such as pi - - initial_coordinate_basis = self._chart.frame().at(initial_point) - initial_tangent_vector_components = self._initial_tangent_vector[:, initial_coordinate_basis] # will - # raise error if components in coordinate basis cannot get known - initial_tangent_vector_components = [numerical_approx(comp) for comp - in initial_tangent_vector_components] # should - # already be a list, but the components need to be completely numerical as well - + if parameters_values is None or len(parameters_values)!=len(self._parameters): + raise ValueError("Numerical values should be " + + "provided for each of the " + + "parameters " + "{}.".format(self._parameters)) + for key in parameters_values.keys(): + parameters_values[key]=numerical_approx(parameters_values[key])#gets + # numerical values in case some parameters values + # contain expressions such as pi; will raise error if + # any element of parameters_values is not numerical + + if isinstance(t_min, Expression): + t_min=t_min.substitute(parameters_values) + if t_min==-Infinity or t_min==+Infinity: + raise ValueError("Both boundaries of the " + + "interval need to be finite.") + + if isinstance(t_max, Expression): + t_max=t_max.substitute(parameters_values) + if t_max==-Infinity or t_max==+Infinity: + raise ValueError("Both boundaries of the " + + "interval need to be finite.") + + eqns_rhs=[eqn.substitute(parameters_values) for eqn + in eqns_rhs] + + + + for i in range(len(initial_pt_coords)): + if isinstance(initial_pt_coords[i],Expression): + AUX = initial_pt_coords[i] + AUX = AUX.substitute(parameters_values) + initial_pt_coords[i] = AUX + # 'AUX' only used for the lines of + # source code to be shorter + + + for i in range(len(initial_tgt_vec_comps)): + if isinstance(initial_tgt_vec_comps[i],Expression): + AUX = initial_tgt_vec_comps[i] + AUX = AUX.substitute(parameters_values) + initial_tgt_vec_comps[i] = AUX + # 'AUX' only used for the lines of + # source code to be shorter + + t_min = numerical_approx(t_min) + t_max = numerical_approx(t_max) + initial_pt_coords = [numerical_approx(coord) for coord + in initial_pt_coords] + initial_tgt_vec_comps = [numerical_approx(comp) for comp + in initial_tgt_vec_comps] + # the last two instructions retrieve numerical values even + # if no parameters had to be substituted, in case some + # coordinates or components contain expressions such as pi, + # or are not RealNumber, since variable 'ics' of + # 'desolve_system_rk4' used below needs to be a list of + # RealNumber + + ics = [t_min] + initial_pt_coords + initial_tgt_vec_comps + sol = desolve_system_rk4(self._velocities + eqns_rhs, - coordinate_functions_list + self._velocities, + list(chart[:]) + self._velocities, ivar = self._curve_parameter, - ics=[t_min] + initial_point_coordinates + initial_tangent_vector_components, - end_points=[t_min, t_max], step=step) - + ics = ics, + end_points=[t_min, t_max], + step=step) + dim = self.codomain().dim() - self._solutions[solution_key] = [point[0:dim+1] for point in sol] + self._solutions[solution_key] = [point[0:dim+1] for point + in sol] else: - raise ValueError("No available method of integration referred to as '{}'.".format(method)) + raise ValueError("No available method of integration " + + "referred to as '{}'.".format(method)) + + if verbose: + print("Numerical integration completed. " + + "Resulting list of points was associated with the key " + + "'{}' ".format(solution_key) + + "(if this key already referred to a former numerical " + + "solution, such a solution was erased).") - print("Numerical integration completed. " + - "Resulting list of points was associated with the key '{}' ".format(solution_key) + - "(if this key already referred to a former numerical solution, " + - "such a solution was erased).") - return self._solutions[solution_key] + def solution(self, solution_key=None, verbose=True): + r""" + Returns the solution (list of points) associated with the given + key. + + INPUT: + - ``solution_key`` -- (default: ``None``) key which the + requested numerical solution is associated to ; a default + value is chosen if none is provided + - ``verbose`` -- (default: ``True``) prints information about + the solution returned + + OUTPUT: + + - list of the numerical points of the solution requested + + TESTS:: - def solution(self, solution_key=None): - r""" - Returns the list of points associated with the given key. - - TESTS:: sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - sage: c.solve(parameters_values={B_0:1, m:1, q:1, L:10, T:1}) - Performing 4th order Runge-Kutta integration by default... - Resulting list of points will be associated with the key 'rk4' by - default. - Numerical integration completed. Resulting list of points was - associated with the key 'rk4' (if this key already referred to a - former numerical solution, such a solution was erased). - sage: c.solution(solution_key='my solution') - ValueError: No existing key 'my solution' referring to any numerical - solution. - sage: c.solution() - Returning the numerical solution associated with the key 'rk4' by - default... + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + sage: sol = c.solve(verbose=False, + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + ....: solution_key='sol_T1') + sage: sol_bis = c.solution() + Returning the numerical solution associated with the key + 'sol_T1' by default... + sage: sol_bis == sol + True + sage: sol_ter = c.solution(solution_key='sol_T1') + sage: sol_ter == sol + True + sage: sol_mute = c.solution(verbose=False) + sage: sol_mute == sol + True """ - + if solution_key is None: if 'rk4' in self._solutions.keys(): solution_key = 'rk4' else: - solution_key = self._solutions.keys()[0] # will raise error if - # self._solutions empty - print("Returning the numerical solution associated with the key '{}' by default...".format(solution_key)) + solution_key = self._solutions.keys()[0] # will raise + # error if self._solutions empty + if verbose: + print("Returning the numerical solution associated " + + "with the key '{}' ".format(solution_key) + + "by default...") elif solution_key not in self._solutions.keys(): - raise ValueError("No existing key '{}' referring to any numerical solution.".format(solution_key)) - + raise ValueError("No existing key " + + "'{}' ".format(solution_key) + + "referring to any numerical solution.") + return self._solutions[solution_key] + def interpolate(self, solution_key=None, method=None, + interpolation_key=None, verbose=True): + r""" + Interpolates the chosen numerical solution using the given + interpolation method. + + INPUT: + + - ``solution_key`` -- (default: ``None``) key which the + numerical solution to interpolate is associated to ; a default + value is chosen if none is provided + - ``method`` -- (default: ``None``) interpolation scheme to use; + algorithms available are + + * 'cubic spline', which uses ``GSL`` via Sage class + :class:`~sage.calculus.interpolation.Spline` + + - ``interpolation_key`` -- (default: ``None``) key which the + resulting interpolation will be associated to ; a default + value is given if none is provided + - ``verbose`` -- (default: ``True``) prints information about + the interpolation in progress + + OUTPUT: + - built interpolation object + + TESTS:: - def interpolate(self, solution_key=None, method=None, interpolation_key=None): - r""" - Interpolates the chosen numerical solution using the given interpolation - method. - - Interpolation scheme 'cubic spline' uses *GSL* via Sage class - :class:`~sage.calculus.interpolation.Spline`. - - TESTS:: sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - sage: c.solve(method='rk4', solution_key='sol1', - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) - Numerical integration completed. Resulting list of points was associated - with the key 'sol1' (if this key already referred to a former - numerical solution, such a solution was erased). - sage: c.interpolate(solution_key='my solution') - ValueError: No existing key 'my solution' referring to any numerical - solution. - sage: c.interpolate(solution_key='sol1', method='my method') - ValueError: No available method of interpolation referred to as 'my method'. - sage: c.interpolate(solution_key='sol1') + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + sage: sol = c.solve(method='rk4', solution_key='sol_T1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + ....: verbose=False) + sage: interp = c.interpolate(solution_key='my solution') + Traceback (most recent call last): + ... + ValueError: No existing key 'my solution' referring to any + numerical solution. + sage: interp = c.interpolate(solution_key='sol_T1', + ....: method='my method') + Traceback (most recent call last): + ... + ValueError: No available method of interpolation referred to + as 'my method'. + sage: interp = c.interpolate(method='cubic spline', + ....: solution_key='sol_T1', + ....: interpolation_key='interp_T1') + Interpolation completed and associated with the key + 'interp_T1' (if this key already referred to a former + interpolation, such an interpolation was erased). + sage: interp = c.interpolate() + Interpolating the numerical solution associated with the + key 'sol_T1' by default... Performing cubic spline interpolation by default... - Resulting interpolation will be associated with the key - 'cubic spline-interp-sol1' by default. - Interpolation completed and associated with the key - 'cubic spline-interp-sol1' (if this key already referred to a - former interpolation, such an interpolation was erased). - sage: c.interpolate(method='cubic spline', solution_key='sol1', - interpolation_key='interp1') - Interpolation completed and associated with the key 'interp1' (if - this key already referred to a former interpolation, such an - interpolation was erased). - + Resulting interpolation will be associated with the key + 'cubic spline-interp-sol_T1' by default. + Interpolation completed and associated with the key + 'cubic spline-interp-sol_T1' (if this key already referred + to a former interpolation, such an interpolation was + erased). + """ - + if solution_key is None: if 'rk4' in self._solutions.keys(): solution_key = 'rk4' else: - solution_key = self._solutions.keys()[0] # will raise error if - # self._solutions empty - print("Interpolating the numerical solution associated with the key '{}' by default...".format(solution_key)) + solution_key = self._solutions.keys()[0] # will raise + # error if self._solutions empty + if verbose: + print("Interpolating the numerical solution " + + "associated with the key " + + "'{}' ".format(solution_key) + + "by default...") elif solution_key not in self._solutions.keys(): - raise ValueError("No existing key '{}' referring to any numerical solution.".format(solution_key)) - + raise ValueError("No existing key " + + "'{}' ".format(solution_key) + + "referring to any numerical solution.") + if method is None: method = 'cubic spline' - print("Performing cubic spline interpolation by default...") + if verbose: + print("Performing cubic spline interpolation by " + "default...") if interpolation_key is None: - interpolation_key = "{}-interp-{}".format(method, solution_key) - print("Resulting interpolation will be associated with the key '{}' by default.".format(interpolation_key)) - + interpolation_key = "{}-interp-".format(method) + interpolation_key += "{}".format(solution_key) + if verbose: + print("Resulting interpolation will be associated " + + "with the key '{}' ".format(interpolation_key) + + "by default.") + if method=='cubic spline': self._interpolations[interpolation_key] = [] dim = self.codomain().dim() @@ -681,51 +933,67 @@ def interpolate(self, solution_key=None, method=None, interpolation_key=None): coordinate_curve = [] for point in self._solutions[solution_key]: coordinate_curve += [[point[0], point[i+1]]] - self._interpolations[interpolation_key] += [Spline(coordinate_curve)] + self._interpolations[interpolation_key]+=[Spline(coordinate_curve)] else: - raise ValueError("No available method of interpolation referred to as '" + - method + "'.") + raise ValueError("No available method of interpolation " + + "referred to as '{}'.".format(method)) - print("Interpolation completed and associated with the key '{}' ".format(interpolation_key) + - "(if this key already referred to a former interpolation, " + - "such an interpolation was erased).") - - return self._interpolations[interpolation_key] + if verbose: + print("Interpolation completed and associated with the " + + "key '{}' ".format(interpolation_key) + + "(if this key already referred to a former " + + "interpolation, such an interpolation was erased).") + return self._interpolations[interpolation_key] - - def interpolation(self, interpolation_key=None): + def interpolation(self, interpolation_key=None, verbose=True): r""" Returns the interpolation object associated with the given key. - - TESTS:: + + INPUT: + + - ``interpolation_key`` -- (default: ``None``) key which the + requested interpolation is associated to ; a default + value is chosen if none is provided + - ``verbose`` -- (default: ``True``) prints information about + the interpolation object returned + + OUTPUT: + + - requested interpolation object + + TESTS:: + sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - sage: c.solve(method='rk4', solution_key='sol1', - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) - Numerical integration completed. Resulting list of points was associated - with the key 'sol1' (if this key already referred to a former - numerical solution, such a solution was erased). - sage: c.interpolate(method='cubic spline', solution_key='sol1', - interpolation_key='interp1') - Interpolation completed and associated with the key 'interp1' (if - this key already referred to a former interpolation, such an - interpolation was erased). + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + sage: sol = c.solve(method='rk4', solution_key='sol_T1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + ....: verbose=False) + sage: interp = c.interpolate(method='cubic spline', + ....: solution_key='sol_T1', verbose=False, + ....: interpolation_key='interp_T1') sage: c.interpolation(interpolation_key='my interp') - ValueError: No existing key 'my interp' referring to any interpolation. - sage: c.interpolation() - Returning the interpolation associated with the key 'interp1' by - default... + Traceback (most recent call last): + ... + ValueError: No existing key 'my interp' referring to any + interpolation. + sage: default_interp = c.interpolation() + Returning the interpolation associated with the key + 'interp_T1' by default... + sage: default_interp == interp + True + sage: interp_mute = c.interpolation(verbose=False) + sage: interp_mute == interp + True """ @@ -733,334 +1001,1065 @@ def interpolation(self, interpolation_key=None): if 'cubic spline' in self._interpolations.keys(): interpolation_key = 'cubic spline' else: - interpolation_key = self._interpolations.keys()[0] # will + interpolation_key = self._interpolations.keys()[0]#will # raise error if self._interpolations empty - print("Returning the interpolation associated with the key '{}' ".format(interpolation_key) - + "by default...") + if verbose: + print("Returning the interpolation associated with " + + "the key '{}' ".format(interpolation_key) + + "by default...") elif interpolation_key not in self._interpolations.keys(): - raise ValueError("No existing key '{}' referring to any interpolation.".format(interpolation_key)) - - return self._interpolations[interpolation_key] - + raise ValueError("No existing key " + + "'{}' ".format(interpolation_key) + + "referring to any interpolation.") + return self._interpolations[interpolation_key] - def __call__(self, curve_parameter_value, interpolation_key=None): + def __call__(self, t, interpolation_key=None, + verbose=True): r""" Returns the image of the curve for the given value of the curve - parameter, using the chosen interpolation. - - TESTS:: + parameter, using the chosen interpolation. + + INPUT: + + - ``t'' -- curve parameter value at which the curve is evaluated + - ``interpolation_key`` -- (default: ``None``) key which the + interpolation requested to compute the point is associated to ; + a default value is chosen if none is provided + - ``verbose`` -- (default: ``True``) prints information about + the interpolation used + + OUTPUT: + + - list of the numerical coordinates of the point + + + TESTS:: + sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - sage: c.solve(method='rk4', solution_key='sol1', - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) - Numerical integration completed. Resulting list of points was associated - with the key 'sol1' (if this key already referred to a former - numerical solution, such a solution was erased). - sage: c.interpolate(method='cubic spline', solution_key='sol1', - interpolation_key='interp1') - Interpolation completed and associated with the key 'interp1' (if - this key already referred to a former interpolation, such an - interpolation was erased). + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + sage: sol = c.solve(method='rk4', solution_key='sol_T1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + ....: verbose=False) + sage: interp = c.interpolate(method='cubic spline', + ....: solution_key='sol_T1', verbose=False, + ....: interpolation_key='interp_T1') sage: c(1.1, interpolation_key='my interp') + Traceback (most recent call last): + ... ValueError: No existing key 'my interp' referring to any interpolation. sage: c(1.1) - Evaluating point coordinates from the interpolation associated - with the key 'interp1' by default... - [1.060743431308544, -0.2153838226258469, 1.1] - + Evaluating point coordinates from the interpolation + associated with the key 'interp_T1' by default... + [1.060743431308544, -0.2153838226258469, 1.1] + sage: pt = c(1.1, verbose=False); pt + [1.060743431308544, -0.2153838226258469, 1.1] + """ - + if interpolation_key==None: if 'cubic spline' in self._interpolations.keys(): interpolation_key = 'cubic spline' else: - interpolation_key = self._interpolations.keys()[0] # will + interpolation_key = self._interpolations.keys()[0]#will # raise error if self._interpolations empty - print("Evaluating point coordinates from the interpolation " + - "associated with the key '{}' ".format(interpolation_key) + - "by default...") + if verbose: + print("Evaluating point coordinates from the " + + "interpolation associated with the key " + + "'{}' by default...".format(interpolation_key)) elif interpolation_key not in self._interpolations.keys(): - raise ValueError("No existing key '{}' referring to any interpolation.".format(interpolation_key)) - + raise ValueError("No existing key " + + "'{}' ".format(interpolation_key) + + "referring to any interpolation.") + interpolation = self._interpolations[interpolation_key] - + if isinstance(interpolation[0], Spline): #partial test, in case - # future interpolation objects do not contain lists of instances of - # the Spline class - interpolated_coordinates = [coordinate_curve_spline(curve_parameter_value) - for coordinate_curve_spline in interpolation] + # future interpolation objects do not contain lists of instances + # of the Spline class + interpolated_coordinates=[coordinate_curve_spline(t) + for coordinate_curve_spline in interpolation] return interpolated_coordinates raise TypeError("Unexpected type of interpolation object.") - - - - def tangent_vector_eval_at(self, curve_parameter_value, interpolation_key=None): - r""" - Returns the vector tangent to the curve at the given curve parameter - with components evaluated from the given interpolation. - - TESTS:: + + def tangent_vector_eval_at(self, t, + interpolation_key=None, verbose=True): + r""" + Returns the vector tangent to the curve at the given curve + parameter with components evaluated from the given + interpolation. + + INPUT: + + - ``t`` -- curve parameter value at which the tangent vector is + evaluated + - ``interpolation_key`` -- (default: ``None``) key which the + interpolation requested to compute the tangent vector is + associated to ; a default value is chosen if none is provided + - ``verbose`` -- (default: ``True``) prints information about + the interpolation used + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` + tangent vector with numerical components + + TESTS:: + sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t B_0 m q L T') - (t, B_0, m, q, L, T) + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]) - sage: c.solve(method='rk4', solution_key='sol1', - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) - Numerical integration completed. Resulting list of points was associated - with the key 'sol1' (if this key already referred to a former - numerical solution, such a solution was erased). - sage: c.interpolate(method='cubic spline', solution_key='sol1', - interpolation_key='interp1') - Interpolation completed and associated with the key 'interp1' (if - this key already referred to a former interpolation, such an - interpolation was erased). - + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, + ....: name='c', parameters=[B_0, m, q, L, T]) + sage: sol = c.solve(method='rk4', solution_key='sol_T1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + ....: verbose=False) + sage: interp = c.interpolate(method='cubic spline', + ....: solution_key='sol_T1', verbose=False, + ....: interpolation_key='interp_T1') + sage: tg_vec = c.tangent_vector_eval_at(1.22, + ....: interpolation_key='my interp') + Traceback (most recent call last): + ... + ValueError: No existing key 'my interp' referring to any + interpolation. + sage: tg_vec = c.tangent_vector_eval_at(1.22); tg_vec + Evaluating tangent vector components from the interpolation + associated with the key 'interp_T1' by default... + Tangent vector at Point on the 3-dimensional differentiable + manifold M + sage: tg_vec[:] + [0.7392716344834512, -0.6734470583131389, + 0.9999999999999999] + sage: tg_vec_mute = c.tangent_vector_eval_at(1.22, + ....: verbose=False, + ....: interpolation_key='interp_T1') + sage: tg_vec_mute == tg_vec + True + """ if interpolation_key==None: if 'cubic spline' in self._interpolations.keys(): interpolation_key = 'cubic spline' else: - interpolation_key = self._interpolations.keys()[0] # will + interpolation_key = self._interpolations.keys()[0]#will # raise error if self._interpolations empty - print("Evaluating tangent vector components from the interpolation " + - "associated with the key '{}' ".format(interpolation_key) + - "by default...") + if verbose: + print("Evaluating tangent vector components from the " + + "interpolation associated with the key " + + "'{}' by default...".format(interpolation_key)) elif interpolation_key not in self._interpolations.keys(): - raise ValueError("No existing key '{}' referring to any interpolation.".format(interpolation_key)) - + raise ValueError("No existing key " + + "'{}' ".format(interpolation_key) + + "referring to any interpolation.") + interpolation = self._interpolations[interpolation_key] - + if isinstance(interpolation[0], Spline): #partial test, in case - # future interpolation objects do not contain lists of instances of - # the Spline class - interpolated_coordinates = [coordinate_curve_spline(curve_parameter_value) - for coordinate_curve_spline in interpolation] + # future interpolation objects do not contain lists of instances + # of the Spline class + interpolated_coordinates=[coordinate_curve_spline(t) + for coordinate_curve_spline in interpolation] M = self.codomain() - p = M.point(interpolated_coordinates, chart=self._chart) + p = M.point(interpolated_coordinates, chart=self._chart, + name=None) Tp = M.tangent_space(p) - - evaluated_tangent_vector_components = [coordinate_curve_spline.derivative(curve_parameter_value, order=1) - for coordinate_curve_spline in interpolation] + + evaluated_tgt_vec_comp=[coordinate_curve_spline.derivative(t) + for coordinate_curve_spline in interpolation] # by + # default, order=1 in method 'derivative' of a class Spline basis = self._chart.frame().at(p) - v = Tp(evaluated_tangent_vector_components, basis=basis) + v = Tp(evaluated_tgt_vec_comp, basis=basis) return v raise TypeError("Unexpected type of interpolation object.") + @options(thickness=1, width_tangent=1, plot_points=75, + aspect_ratio='automatic', plot_points_tangent=10, scale=1) + def plot(self, chart=None, ambient_coords=None, mapping=None, + prange=None, interpolation_key=None, + include_end_point=(True, True), + end_point_offset=(0.001, 0.001), verbose=True, color='red', + style='-', label_axes=True, display_tangent=False, + color_tangent='blue', **kwds): + r""" + Plots the 2D or 3D projection of the curve onto the space of the + chosen two or three ambient coordinates, based on the + interpolation of a numerical solution previously computed. + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve.plot` + for complete information about the input. + + ADDITIONAL INPUT: + + - ``interpolation_key`` -- (default: ``None``) key associated to + the interpolation object used for the plot ; a default value + is chosen if none is provided + - ``verbose`` -- (default: ``True``) prints information about + the interpolation object used and the plotting in progress + + EXAMPLE: + + Trajectory of a particle of unit mass and unit charge in an + unit, axial, uniform, stationnary magnetic field:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t') + t + sage: D = X.symbolic_velocities() + sage: eqns = [D[1], -D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 6), v,name='c') + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c_plot_2d = c.plot(ambient_coords=[x1, x2], + ....: thickness=2.5, + ....: display_tangent=True, plot_points=200, + ....: plot_points_tangent=10, scale=0.5, + ....: color='blue', color_tangent='red') + Plotting from the interpolation associated with the key + 'cubic spline-interp-rk4' by default... + A tiny final offset equal to the value of + 'end_point_offset[1]' (= 0.001) was introduced in order to + safely compute the last tangent vector from the + interpolation. + sage: c_plot_2d.show() + + .. PLOT:: + + M = Manifold(3, 'M') + X = M.chart('x1 x2 x3') + var('x1 x2 x3 t') + D = X.symbolic_velocities() + eqns = [D[1], -D[0], SR(0)] + p = M.point((0,0,0), name='p') + Tp = M.tangent_space(p) + v = Tp((1,0,1)) + c = M.integrated_curve(eqns, D, (t, 0, 6), v, name='c') + c.solve(verbose=False) + c.interpolate(verbose=False) + c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], + thickness=2.5, + display_tangent=True, plot_points=200, + plot_points_tangent=10, scale=0.5, + color='blue', color_tangent='red') + sphinx_plot(c_plot_2d_1) - @options(thickness=1, plot_points=75, plot_points_tangent=10, aspect_ratio='automatic', scale=1) - def plot(self, display_tangent=False, color_tangent='blue', - interpolation_key=None, ambient_coords=None, prange=None, - include_end_point=(True, True), end_point_offset=(0.001, 0.001), - color='red', style='-', label_axes=True, **kwds): - r""" - Plots the 2D or 3D projection of the curve onto the space of the chosen - two or three ambient coordinates, based on the interpolation of a - numerical solution previously computed." - - TESTS:: - sage: #TO DO - """ + from sage.manifolds.chart import RealChart + # # Get the @options from kwds # thickness = kwds.pop('thickness') plot_points = kwds.pop('plot_points') aspect_ratio = kwds.pop('aspect_ratio') - + # - # Coordinates of the chart w.r.t. which the curve is plotted + # Interpolation to use + # + if interpolation_key==None: + if 'cubic spline' in self._interpolations.keys(): + interpolation_key = 'cubic spline' + else: + interpolation_key = self._interpolations.keys()[0]#will + # raise error if self._interpolations empty + if verbose: + print("Plotting from the interpolation associated " + + "with the key '{}' ".format(interpolation_key) + + "by default...") + elif interpolation_key not in self._interpolations.keys(): + raise ValueError("No existing key " + + "'{}' ".format(interpolation_key) + + "referring to any interpolation.") + + interpolation = self._interpolations[interpolation_key] + + # + # The mapping, if present, and the chart w.r.t. which the curve + # is plotted + # + if mapping is None: + i0 = self.codomain().start_index() + if chart is None: + chart = self._chart + else: + if not isinstance(chart, RealChart): + raise TypeError("{} is not a real " + + "chart".format(chart)) + mapping = self.codomain().identity_map() + else: + i0 = mapping.codomain().start_index() + if chart is None: + chart = mapping.codomain().default_chart() + elif not isinstance(chart, RealChart): + raise TypeError("{} is not a real chart".format(chart)) + + # + # Coordinates of the above chart w.r.t. which the curve is + # plotted # if ambient_coords is None: - ambient_coords = self._chart[:] # all chart coordinates are used + ambient_coords = chart[:] # all chart coordinates are used n_pc = len(ambient_coords) if n_pc != 2 and n_pc !=3: - raise ValueError("the number of coordinates involved in the " + - "plot must be either 2 or 3, not {}".format(n_pc)) + raise ValueError("the number of coordinates involved in " + + "the plot must be either 2 or 3, " + + "not {}".format(n_pc)) + + # From now on, 'pc' will denote coordinates in terms of which + # the curve is plotted (i.e. the "ambient coordinates"), while + # 'coord' will denote coordinates on self.domain(); of course, + # when, for instance, the mapping is the identity map, these may + # be the same. + # indices of plot coordinates - ind_pc = [self._chart[:].index(pc) for pc in ambient_coords] - + ind_pc = [chart[:].index(pc)+i0 for pc in ambient_coords] # will + # raise an error if ambient_coords are not associated with chart + + # + # Maximal parameter range for the plot of the chosen + # interpolation # - # Parameter range for the plot - # - param_min = numerical_approx(self.domain().lower_bound()) - param_max = numerical_approx(self.domain().upper_bound()) - + param_min = interpolation[0][0][0] + param_max = interpolation[0][-1][0] + if prange is None: prange = (param_min, param_max) elif not isinstance(prange, (tuple, list)): - raise TypeError("{} is neither a tuple nor a list".format(prange)) + raise TypeError("{} is neither ".format(prange) + + "a tuple nor a list") elif len(prange) != 2: - raise ValueError("the argument prange must be a tuple/list " + - "of 2 elements") + raise ValueError("the argument prange must be a " + + "tuple/list of 2 elements") + else: + p = prange #'p' declared only for the line below to be shorter + if p[0]param_max or p[1]param_max: + raise ValueError("Parameter range should be a " + + "subinterval of the curve domain " + + "({}).".format(self.domain())) tmin = numerical_approx(prange[0]) tmax = numerical_approx(prange[1]) - if tmin < param_min or tmin > param_max or tmax < param_min or tmax > param_max: - raise ValueError("Parameter range should be a subinterval of the curve domain ({}).".format(self.domain())) - if not include_end_point[0]: - tmin = tmin + end_point_offset[0] + tmin += numerical_approx(end_point_offset[0]) if not include_end_point[1]: - tmax = tmax - end_point_offset[1] - - tmin = numerical_approx(tmin) - tmax = numerical_approx(tmax) + tmax -= numerical_approx(end_point_offset[1]) + + if mapping is None: + if isinstance(interpolation[0], Spline): #partial test, + # in case future interpolation objects do not contain lists + # of instances of the Spline class + + # + # List of points for the plot curve + # + plot_curve = [] + dt = (tmax - tmin) / (plot_points - 1) + t = tmin - # - # List of points for the plot curve - # - if interpolation_key==None: - if 'cubic spline' in self._interpolations.keys(): - interpolation_key = 'cubic spline' + for k in range(plot_points): + plot_curve.append([interpolation[j-i0](t) for j in ind_pc]) + t += dt + + if display_tangent: + from sage.plot.graphics import Graphics + from sage.plot.arrow import arrow2d + from sage.plot.plot3d.shapes import arrow3d + + scale = kwds.pop('scale') + plot_points_tangent=kwds.pop('plot_points_tangent') + width_tangent = kwds.pop('width_tangent') + + if not tmax < param_max: + tmax=numerical_approx(tmax- end_point_offset[1]) + if verbose: + print("A tiny final offset equal to " + + "the value of " + + "'end_point_offset[1]' " + + "(= {}) ".format(end_point_offset[1])+ + "was introduced " + + "in order to safely compute the " + + "last tangent vector from the " + + "interpolation.") + + plot_vectors = Graphics() + dt = (tmax - tmin) / (plot_points_tangent - 1) + t = tmin + + for k in range(plot_points_tangent): + # interpolated ambient coordinates: + xp = [interpolation[j-i0](t) for j in ind_pc] + + # tangent vector ambiant components evaluated + # from the interpolation: + vec = [coordinate_curve_spline.derivative(t) + for coordinate_curve_spline in interpolation] + + coord_tail = xp + coord_head = [xp[j] + scale*vec[j] + for j in range(len(xp))] + + if coord_head != coord_tail: + if n_pc == 2: + plot_vectors+=arrow2d(tailpoint=coord_tail, + headpoint=coord_head, + color=color_tangent, + width=width_tangent) + else: + plot_vectors+=arrow3d(coord_tail, + coord_head, + color=color_tangent, + width=width_tangent) + t += dt + + return plot_vectors+DifferentiableCurve._graphics(self, + plot_curve, ambient_coords, + thickness=thickness, + aspect_ratio=aspect_ratio, + color=color, + style=style, + label_axes=label_axes) + + return DifferentiableCurve._graphics(self, plot_curve, + ambient_coords, thickness=thickness, + aspect_ratio=aspect_ratio, color=color, + style=style, label_axes=label_axes) + + raise TypeError("Unexpected type of interpolation object.") + else: + # + # The coordinate expressions of the mapping and the + # coordinates involved + # + for chart_pair in mapping._coord_expression.keys(): + subs=(chart_pair[0]._subcharts,chart_pair[1]._subcharts) + # 'subs' declared only for the line below to be shorter + if self._chart in subs[0] and chart in subs[1]: + transf = {} + required_coords = set() + for pc in ambient_coords: + j = chart[:].index(pc) + AUX = mapping._coord_expression[chart_pair] + # 'AUX' used only for the lines of source code + # to be shorter + transf[pc] = AUX.expr()[j] + AUX2 = transf[pc].variables() # idem + required_coords=required_coords.union(AUX2) + break else: - interpolation_key = self._interpolations.keys()[0] # will raise error if - # self._interpolations empty - print("Plotting from the interpolation associated " + - "with the key '{}' ".format(interpolation_key) + - "by default...") - elif interpolation_key not in self._interpolations.keys(): - raise ValueError("No existing key '{}' referring to any interpolation.".format(interpolation_key)) - - interpolation = self._interpolations[interpolation_key] - - if isinstance(interpolation[0], Spline): #partial test, in case - # future interpolation objects do not contain lists of instances of - # the Spline class - plot_curve = [] - dt = (tmax - tmin) / (plot_points - 1) - t = tmin - - - for i in range(plot_points): - plot_curve.append( [interpolation[j](t) for j in ind_pc] ) - t += dt - - if display_tangent: - from sage.plot.graphics import Graphics - from sage.plot.arrow import arrow2d - from sage.plot.plot3d.shapes import arrow3d - - scale = kwds.pop('scale') - plot_points_tangent = kwds.pop('plot_points_tangent', None) - if plot_points_tangent is None: - plot_points_tangent = plot_points - print("Plotting as many tangent vectors as points.") - - plot_vectors = Graphics() - dt = (tmax - tmin) / (plot_points_tangent - 1) + raise ValueError("No expression has been found for " + + "{} in terms of {}".format(self,chart)) + + if isinstance(interpolation[0], Spline): # partial test, in + # case future interpolation objects do not contain lists of + # instances of the Spline class + + # + # List of points for the plot curve + # + plot_curve = [] + dt = (tmax - tmin) / (plot_points - 1) t = tmin - - for i in range(plot_points_tangent): - # interpolated ambient coordinates: - xp = [interpolation[j](t) for j in ind_pc] - - # tangent vector ambiant components evaluated from the interpolation: - vcomp = [coordinate_curve_spline.derivative(t, order=1) - for coordinate_curve_spline in interpolation] - - coord_tail = xp - coord_head = [xp[i] + scale*vcomp[i] for i in ind_pc] - - if coord_head != coord_tail: - if n_pc == 2: - plot_vectors += arrow2d(tailpoint=coord_tail, - headpoint=coord_head, - color=color_tangent) - else: - plot_vectors += arrow3d(coord_tail, coord_head, - color=color_tangent) + required_coords_values = {} + + for k in range(plot_points): + for coord in required_coords: + i = self._chart[:].index(coord) + required_coords_values[coord]=interpolation[i](t) + + xp = [] + for j in ind_pc: + pc = chart[j] + AUX = transf[pc] + AUX = AUX.substitute(required_coords_values) + # 'AUX' only used for the lines of source code + # to be shorter + xp+=[numerical_approx(AUX)] + + plot_curve.append(xp) t += dt - return plot_vectors + DifferentiableCurve._graphics(self, plot_curve, - ambient_coords, thickness=thickness, - aspect_ratio=aspect_ratio, color=color, - style=style, label_axes=label_axes) - - - return DifferentiableCurve._graphics(self, plot_curve, ambient_coords, thickness=thickness, - aspect_ratio=aspect_ratio, color=color, - style=style, label_axes=label_axes) - - raise TypeError("Unexpected type of interpolation object.") - + if display_tangent: + from sage.plot.graphics import Graphics + from sage.plot.arrow import arrow2d + from sage.plot.plot3d.shapes import arrow3d + + scale = kwds.pop('scale') + plot_points_tangent=kwds.pop('plot_points_tangent') + width_tangent = kwds.pop('width_tangent') + + if not tmax < param_max: + tmax=numerical_approx(tmax- end_point_offset[1]) + if verbose: + print("A tiny final offset equal to " + + "the value of " + + "'end_point_offset[1]' " + + "(= {}) ".format(end_point_offset[1])+ + "was introduced " + + "in order to safely compute the " + + "last tangent vector from the " + + "interpolation.") + + plot_vectors = Graphics() + dt = (tmax - tmin) / (plot_points_tangent - 1) + t = tmin + Dcoord_Dt = {} + + Dpc_Dcoord = {} + for pc in ambient_coords: + Dpc_Dcoord[pc] = {} + for coord in transf[pc].variables(): + Dpc_Dcoord[pc][coord]=transf[pc].derivative(coord) + + for k in range(plot_points_tangent): + for coord in required_coords: + i = self._chart[:].index(coord) + AUX = interpolation[i] # 'AUX' only used + # for the lines below to be shorter + required_coords_values[coord] = AUX(t) + Dcoord_Dt[coord] = AUX.derivative(t) + + xp = [] + pushed_vec = [] + for j in ind_pc: + pc = chart[j] + AUX = transf[pc] + AUX = AUX.substitute(required_coords_values) + # 'AUX' only used for the lines of code to + # be shorter + xp+=[numerical_approx(AUX)] + + pushed_comp = 0 + for coord in transf[pc].variables(): + D = Dpc_Dcoord[pc][coord] + D = D.substitute(required_coords_values) + D=numerical_approx(D) + pushed_comp += Dcoord_Dt[coord] * D + + pushed_vec += [pushed_comp] + + coord_tail = xp + coord_head =[xp[j] + scale*pushed_vec[j] + for j in range(len(xp))] + + if coord_head != coord_tail: + if n_pc == 2: + plot_vectors+=arrow2d(tailpoint=coord_tail, + headpoint=coord_head, + color=color_tangent, + width=width_tangent) + else: + plot_vectors+=arrow3d(coord_tail, + coord_head, + color=color_tangent, + width=width_tangent) + t += dt + + return plot_vectors+DifferentiableCurve._graphics(self, + plot_curve, ambient_coords, + thickness=thickness, + aspect_ratio=aspect_ratio, + color=color, + style=style, + label_axes=label_axes) + + return DifferentiableCurve._graphics(self, plot_curve, + ambient_coords, thickness=thickness, + aspect_ratio=aspect_ratio, color=color, + style=style, label_axes=label_axes) + + raise TypeError("Unexpected type of interpolation object.") class IntegratedAutoparallelCurve(IntegratedCurve): - r""" - Constructs a numerical autoparallel curve. - + r""" + Constructs a numerical autoparallel curve on the manifold with + respect to a given affine connection. + INPUT: - TO DO - + + - ``parent`` -- + :class:`~sage.manifolds.differentiable.manifold_homset.DifferentiableCurveSet` + the set of curves `\mathrm{Hom}(I, M)` to which the curve belongs + - ``affine_connection`` -- + :class:`~sage.manifolds.differentiable.affine_connection.AffineConnection` + affine connection with respect to which the curve is + autoparallel + - ``curve_parameter`` -- symbolic expression to be used as the + parameter of the curve + - ``initial_tangent_vector`` -- + :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` + initial tangent vector of the curve + - ``chart`` -- (default: ``None``) chart on the manifold in + which the equations are given; if ``None`` the default chart + of the manifold is assumed + - ``parameters`` -- list of the symbolic expressions used in the + coefficients of ``affine_connection`` other than the + coordinates associated with the chart + - ``name`` -- (default: ``None``) string; symbol given to the curve + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the curve; if none is provided, ``name`` will be used + - ``is_isomorphism`` -- (default: ``False``) determines whether the + constructed object is a diffeomorphism; if set to ``True``, + then `M` must have dimension one + - ``is_identity`` -- (default: ``False``) determines whether the + constructed object is the identity map; if set to ``True``, + then `M` must coincide with the domain of the curve + + EXAMPLE: + + Autoparallel curves associated with the Mercator projection of the + unit 2-sphere :MATH:`\mathbb{S}^{2}`. + + .. SEEALSO:: + + https://idontgetoutmuch.wordpress.com/2016/11/24/mercator-a-connection-with-torsion/ + for more details about Mercator projection + + On the Mercator projection, the lines of longitude all appear + vertical and then all parallel w.r.t each others. + Likewise, all the lines of latitude appear horizontal and parallel + w.r.t each others. + These curves may be recovered as autoparallel curves of a certain + connection :MATH:`\nabla` to be made explicit. + + Start with declaring the standard polar coordinates + :MATH:`(\theta, \phi)` on :MATH:`\mathbb{S}^{2}` and the + corresponding coordinate frame :MATH:`(e_{\theta}, e_{\phi})`:: + + sage: S2 = Manifold(2, 'S^2', start_index=1) + sage: polar.=S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + sage: epolar = polar.frame() + + Normalizing :MATH:`e_{\phi}` provides an orthonormal basis:: + + sage: ch_basis = S2.automorphism_field() + sage: ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) + sage: epolar_ON=S2.default_frame().new_frame(ch_basis,'epolar_ON') + + Denote :MATH:`(\hat{e}_{\theta}, \hat{e}_{\phi})` such an + orthonormal frame field. + In any point, the vector field :MATH:`\hat{e}_{\theta}` is + normalized and tangent to the line of longitude through the point. + Likewise, :MATH:`\hat{e}_{\phi}` is normalized and tangent to the + line of latitude. + + Now, set an affine connection with respect to which such fields are + parallely transported in all directions, that is: + :MATH:`\nabla \hat{e}_{\theta} = \nabla \hat{e}_{\phi} = 0`. + This is equivalent to setting all the connection coefficients to + zero w.r.t. this frame:: + + sage: nab = S2.affine_connection('nab') + sage: nab.set_coef(epolar_ON)[:] + [[[0, 0], [0, 0]], [[0, 0], [0, 0]]] + + This connection is such that two vectors are parallel if their + angles to a given meridian are the same. + Check that this connection is compatible with the Euclidean + metric tensor :MATH:`g` induced on :MATH:`\mathbb{S}^{2}`:: + + sage: g = S2.metric('g') + sage: g[1,1], g[2,2] = 1, (sin(th))^2 + sage: nab(g)[:] + [[[0, 0], [0, 0]], [[0, 0], [0, 0]]] + + Yet, this connection is not the Levi-Civita connection, which + implies that it has non-vanishing torsion:: + + sage: nab.torsion()[:] + [[[0, 0], [0, 0]], [[0, cos(th)/sin(th)], [-cos(th)/sin(th), 0]]] + + Set generic initial conditions for the autoparallel curves to + compute:: + + sage: [th0, ph0, v_th0, v_ph0] = var('th0 ph0 v_th0 v_ph0') + sage: p = S2.point((th0, ph0), name='p') + sage: Tp = S2.tangent_space(p) + sage: v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) + + Declare the corresponding integrated autoparallel curve and display + the differential system it satisfies:: + + sage: [t, tmin, tmax] = var('t tmin tmax') + sage: c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), + ....: v, chart=polar, + ....: parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0],name='c') + sage: sys = c.system() + Autoparallel curve c in the 2-dimensional differentiable + manifold S^2 equipped with Affine connection nab on the + 2-dimensional differentiable manifold S^2, and integrated over + the Real interval (tmin, tmax) as a solution to the following + equations, written w.r.t. Chart (S^2, (th, ph)): + + Initial point: Point p on the 2-dimensional differentiable + manifold S^2 with coordinates [th0, ph0] w.r.t. + Chart (S^2, (th, ph)) + Initial tangent vector: Tangent vector at Point p on the + 2-dimensional differentiable manifold S^2 with + components [v_th0, v_ph0/sin(th0)] w.r.t. Chart (S^2, (th, ph)) + + d(th)/dt = Dth + d(Dth)/dt = 0 + d(ph)/dt = Dph + d(Dph)/dt = -Dph*Dth*cos(th)/sin(th) + + + Set a dictionnary providing the parameter range and the initial + conditions for a line of latitude and a line of longitude:: + + sage: dict_params={'latit':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:0,v_ph0:1}, + ....: 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}} + + Declare the Mercator coordinates :MATH:`(\xi, \zeta)` and the + corresponding coordinate change from the polar coordinates:: + + sage: mercator.=S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta') + sage: polar.transition_map(mercator, (log(tan(th/2)), ph)) + Change of coordinates from Chart (S^2, (th, ph)) to Chart + (S^2, (xi, ze)) + + Ask for the identity map in terms of these charts in order to add + this coordinate change to its dictionnary of expressions. This is + required to plot the curve w.r.t the Mercator chart:: + + sage: identity = S2.identity_map() + sage: identity.coord_functions(polar, mercator) + Coordinate functions (log(sin(1/2*th)/cos(1/2*th)), ph) on the + Chart (S^2, (th, ph)) + + Solve, interpolate and prepare the plot for the solutions + corresponding to the two initial conditions previously set:: + + sage: graph2D_mercator = Graphics() + sage: for key in dict_params.keys(): + ....: sol = c.solve(solution_key='sol-'+key, + ....: parameters_values=dict_params[key], verbose=False) + ....: interp = c.interpolate(solution_key='sol-'+key, + ....: interpolation_key='interp-'+key, verbose=False) + ....: graph2D_mercator+=c.plot(interpolation_key='interp-'+key, + ....: chart=mercator, thickness=2, + ....: verbose=False) + + Prepare a grid of Mercator coordinates lines, and plot the curves + over it:: + + sage: graph2D_mercator_coords=mercator.plot(chart=mercator, + ....: number_values=8,color='yellow') + sage: (graph2D_mercator + graph2D_mercator_coords).show() + + .. PLOT:: + + S2 = Manifold(2, 'S^2', start_index=1) + polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + [th, ph] = var('th ph') + epolar = polar.frame() + ch_basis = S2.automorphism_field() + ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) + epolar_ON = S2.default_frame().new_frame(ch_basis, 'e') + nab = S2.affine_connection('nab') + nab.set_coef(epolar_ON)[:] + [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') + p = S2.point((th0, ph0), name='p') + Tp = S2.tangent_space(p) + v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) + c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, + chart=polar,parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], + name='c') + dict_params={'latit':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:0,v_ph0:1}, + 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}} + mercator = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta') + [xi,ze] = var('xi ze') + polar.transition_map(mercator, (log(tan(th/2)), ph)) + identity = S2.identity_map() + identity.coord_functions(polar, mercator) + graph2D_mercator = Graphics() + for key in dict_params.keys(): + c.solve(solution_key='sol-'+key, + parameters_values=dict_params[key], verbose=False) + c.interpolate(solution_key='sol-'+key, + interpolation_key='interp-'+key, verbose=False) + graph2D_mercator += c.plot(interpolation_key='interp-'+key, + chart=mercator, thickness=2, verbose=False) + graph2D_mercator_coords = mercator.plot(chart=mercator, + number_values=8, color='yellow') + sphinx_plot(graph2D_mercator + graph2D_mercator_coords) + + The resulting curves are horizontal and vertical as expected. + It is easier to check that these are latitude and longitude lines + respectively when plotting them on :MATH:`\mathbb{S}^{2}`. + To do so, use :MATH:`\mathbb{R}^{3}` as the codomain of the standard + map embedding :MATH:`(\mathbb{S}^{2}, (\theta, \phi))` in the + 3-dimensional Euclidean space:: + + sage: R3 = Manifold(3, 'R3', start_index=1) + sage: cart. = R3.chart() + sage: euclid_embedding = S2.diff_map(R3, + ....: {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) + + Plot the resulting curves on the grid of polar coordinates lines on + :MATH:`\mathbb{S}^{2}`:: + + sage: graph3D_embedded_curves = Graphics() + sage: for key in dict_params.keys(): + ....: graph3D_embedded_curves+=c.plot(interpolation_key='interp-'+key, + ....: mapping=euclid_embedding, thickness=5, + ....: display_tangent=True, scale=0.4, width_tangent=0.5, + ....: verbose=False) + sage: graph3D_embedded_polar_coords = polar.plot(chart=cart, + ....: mapping=euclid_embedding, + ....: number_values=15, color='yellow') + sage: graph=graph3D_embedded_curves+graph3D_embedded_polar_coords + sage: viewer3D = 'threejs' + sage: graph.show(viewer=viewer3D) + + .. PLOT:: + + S2 = Manifold(2, 'S^2', start_index=1) + polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + [th, ph] = var('th ph') + epolar = polar.frame() + ch_basis = S2.automorphism_field() + ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) + epolar_ON = S2.default_frame().new_frame(ch_basis, 'e') + nab = S2.affine_connection('nab') + nab.set_coef(epolar_ON)[:] + [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') + p = S2.point((th0, ph0), name='p') + Tp = S2.tangent_space(p) + v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) + c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, + chart=polar,parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], + name='c') + dict_params={'latit':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:0,v_ph0:1}, + 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}} + R3 = Manifold(3, 'R3', start_index=1) + cart = R3.chart('X Y Z') + [X,Y,Z] = var('X Y Z') + euclid_embedding = S2.diff_map(R3, + {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) + graph3D_embedded_curves = Graphics() + for key in dict_params.keys(): + c.solve(solution_key='sol-'+key, + parameters_values=dict_params[key], verbose=False) + c.interpolate(solution_key='sol-'+key, + interpolation_key='interp-'+key, verbose=False) + graph3D_embedded_curves+=c.plot(interpolation_key='interp-'+key, + mapping=euclid_embedding, thickness=5, + display_tangent=True, scale=0.4, width_tangent=0.5, + verbose=False) + graph3D_embedded_polar_coords = polar.plot(chart=cart, + mapping=euclid_embedding, + number_values=15, color='yellow') + graph = graph3D_embedded_curves+graph3D_embedded_polar_coords + sphinx_plot(graph) + + Finally, one may plot a general autoparallel curve w.r.t + :MATH:`\nabla` that is neither a line of latitude or longitude. + The vectors tangent to such a curve make an angle different from 0 + or :MATH:`\pi/2` with the lines of latitude and longitude. + Then, compute a curve such that both components of its initial + tangent vectors are non zero:: + + sage: sol = c.solve(solution_key='sol-angle', + ....: parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, + ....: verbose=False) + sage: interp = c.interpolate(solution_key='sol-angle', + ....: interpolation_key='interp-angle', verbose=False) + + Plot the resulting curve in the Mercator plane. + This generates a straight line, as expected:: + + sage: graph2D_mercator_angle_curve=c.plot(interpolation_key='interp-angle', + ....: chart=mercator, thickness=1, display_tangent=True, + ....: scale=0.2, width_tangent=0.2, verbose=False) + sage: graph2D_mercator_angle_curve.show() + + .. PLOT:: + + S2 = Manifold(2, 'S^2', start_index=1) + polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + [th, ph] = var('th ph') + epolar = polar.frame() + ch_basis = S2.automorphism_field() + ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) + epolar_ON = S2.default_frame().new_frame(ch_basis, 'e') + nab = S2.affine_connection('nab') + nab.set_coef(epolar_ON)[:] + [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') + p = S2.point((th0, ph0), name='p') + Tp = S2.tangent_space(p) + v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) + c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, + chart=polar,parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], + name='c') + mercator = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta') + [xi,ze] = var('xi ze') + polar.transition_map(mercator, (log(tan(th/2)), ph)) + identity = S2.identity_map() + identity.coord_functions(polar, mercator) + sol = c.solve(solution_key='sol-angle', + parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, + verbose=False) + interp = c.interpolate(solution_key='sol-angle', + interpolation_key='interp-angle', verbose=False) + graph2D_mercator_angle_curve=c.plot(interpolation_key='interp-angle', + chart=mercator, thickness=1, display_tangent=True, + scale=0.2, width_tangent=0.2, verbose=False) + sphinx_plot(graph2D_mercator_angle_curve) + + One may eventually plot such a curve on :MATH:`\mathbb{S}^{2}`:: + + sage: graph3D_embedded_angle_curve=c.plot(interpolation_key='interp-angle', + ....: mapping=euclid_embedding, thickness=5, + ....: display_tangent=True, scale=0.1, width_tangent=0.5, + ....: verbose=False) + sage: graph=graph3D_embedded_angle_curve+graph3D_embedded_polar_coords + sage: graph.show(viewer=viewer3D) + + .. PLOT:: + + S2 = Manifold(2, 'S^2', start_index=1) + polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + [th, ph] = var('th ph') + epolar = polar.frame() + ch_basis = S2.automorphism_field() + ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) + epolar_ON = S2.default_frame().new_frame(ch_basis, 'e') + nab = S2.affine_connection('nab') + nab.set_coef(epolar_ON)[:] + [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') + p = S2.point((th0, ph0), name='p') + Tp = S2.tangent_space(p) + v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) + c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, + chart=polar,parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], + name='c') + R3 = Manifold(3, 'R3', start_index=1) + cart = R3.chart('X Y Z') + [X,Y,Z] = var('X Y Z') + euclid_embedding = S2.diff_map(R3, + {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) + sol = c.solve(solution_key='sol-angle', + parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, + verbose=False) + interp = c.interpolate(solution_key='sol-angle', + interpolation_key='interp-angle', verbose=False) + graph3D_embedded_angle_curve=c.plot(interpolation_key='interp-angle', + mapping=euclid_embedding, thickness=5, display_tangent=True, + scale=0.1, width_tangent=0.5, verbose=False) + graph3D_embedded_polar_coords = polar.plot(chart=cart, + mapping=euclid_embedding, number_values=15, color='yellow') + graph=graph3D_embedded_angle_curve+graph3D_embedded_polar_coords + sphinx_plot(graph) + """ - def __init__(self, parent, affine_connection, curve_parameter, initial_tangent_vector, - chart=None, parameters=None, name=None, latex_name=None, - is_isomorphism=False, is_identity=False): - - r"""Construct the autoparallel curve with respect to the given affine connection with the given initial tangent vector.""" - + def __init__(self, parent, affine_connection, curve_parameter, + initial_tangent_vector, chart=None, parameters=None, + name=None, latex_name=None, is_isomorphism=False, + is_identity=False): + r"""Constructs an autoparallel curve with respect to the given + affine connection with the given initial tangent vector. + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: [t, A, B] = var('t A B') + sage: nab = M.affine_connection('nabla', r'\nabla') + sage: nab[X.frame(),0,0,1],nab[X.frame(),2,1,2]=A*x1^2,B*x2*x3 + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, + ....: name='c', parameters=[A, B]); c + Integrated autoparallel curve c in the 3-dimensional + differentiable manifold M + + """ + from sage.symbolic.ring import SR dim = parent.codomain().dim() + i0 = parent.codomain().start_index() equations_rhs = [] - + # setting the chart to gain access to the coordinate functions if chart is None: chart = parent.codomain().default_chart() - + coordinate_functions = chart[:] velocities = chart.symbolic_velocities() gamma = affine_connection.coef() - + for alpha in range(dim): rhs = SR(0) for mu in range(dim): - for nu in range(dim): - rhs -= gamma[alpha, mu, nu].expr()*velocities[mu]*velocities[nu] + for nu in range(dim): + AUX = velocities[mu] * velocities[nu] + rhs-= gamma[alpha+i0, mu+i0, nu+i0].expr() * AUX + # 'AUX' only used for the line above to be shorter equations_rhs += [rhs.simplify_full()] - - IntegratedCurve.__init__(self, parent, equations_rhs, velocities, curve_parameter, - initial_tangent_vector, chart=chart, parameters=parameters, - name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, - is_identity=is_identity) - - self._affine_connection = affine_connection + IntegratedCurve.__init__(self, parent, equations_rhs, + velocities, curve_parameter, + initial_tangent_vector, chart=chart, + parameters=parameters, + name=name, latex_name=latex_name, + is_isomorphism=is_isomorphism, + is_identity=is_identity) + + self._affine_connection = affine_connection def _repr_(self): - r""" + r""" Returns a string representation of ``self``. - TESTS:: - sage: #TO DO + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: [t, A, B] = var('t A B') + sage: nab = M.affine_connection('nabla', r'\nabla') + sage: nab[X.frame(),0,0,1],nab[X.frame(),2,1,2]=A*x1^2,B*x2*x3 + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, + ....: parameters=[A, B]); c + Integrated autoparallel curve in the 3-dimensional + differentiable manifold M + sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, + ....: name='c', parameters=[A, B]); c + Integrated autoparallel curve c in the 3-dimensional + differentiable manifold M """ @@ -1068,82 +2067,342 @@ def _repr_(self): if self._name is not None: description += self._name + " " description += "in the {}".format(self._codomain) - return description - + return description - - def system(self): + def system(self, verbose=True): r""" - Returns the system defining the autoparallel curve : chart, equations and initial conditions - - TESTS:: - sage: #TO DO - - """ - - initial_tangent_space = self._initial_tangent_vector.parent() - initial_point = initial_tangent_space.base_point() # gets the initial - # point as the base point of the tangent space which initial tangent vector belongs to - initial_point_coordinates = initial_point.coordinates(self._chart) # will - # raise error if coordinates in chart are not known - initial_point_coordinates = [coord for coord in initial_point_coordinates] # converts - # to list since was previously a tuple - - initial_coordinate_basis = self._chart.frame().at(initial_point) - initial_tangent_vector_components = self._initial_tangent_vector[:, initial_coordinate_basis] # will - # raise error if components in coordinate basis are not known - - description = "Autoparallel curve " - if self._name is not None: - description += self._name + " " - description += "in the {} equipped with the {}, and integrated over the {} ".format(self.codomain(), self._affine_connection, self.domain()) - description += "as a solution to the following equations, written with respect to the {}:\n\n".format(self._chart) - - description += "Initial point: {} with coordinates {} in {}\n".format(initial_point, - initial_point_coordinates, self._chart) - description += "Initial tangent vector: {} with components {} in {}\n\n".format(self._initial_tangent_vector, - initial_tangent_vector_components, self._chart) - - for coord_func, velocity, eqn in zip(self._chart[:], self._velocities, self._equations_rhs): - description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) - description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) - print(description) - - return [self._equations_rhs, self._initial_tangent_vector, self._chart] + Provides a detailed description of the system defining the + autoparallel curve and returns the system defining it: chart, + equations and initial conditions. + INPUT: + - ``verbose`` -- (default: ``True``) prints a detailed + description of the curve + + OUTPUT: + + - list containing the attributes :attr:`equations_rhs`, + :attr:`initial_tangent_vector` and :attr:`chart` + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: [t, A, B] = var('t A B') + sage: nab = M.affine_connection('nabla', r'\nabla') + sage: nab[X.frame(),0,0,1],nab[X.frame(),2,1,2]=A*x1^2,B*x2*x3 + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, + ....: parameters=[A, B]) + sage: sys = c.system() + Autoparallel curve in the 3-dimensional differentiable + manifold M equipped with Affine connection nabla on the + 3-dimensional differentiable manifold M, and integrated + over the Real interval (0, 5) as a solution to the + following equations, written w.r.t. + Chart (M, (x1, x2, x3)): + + Initial point: Point p on the 3-dimensional differentiable + manifold M with coordinates [0, 0, 0] w.r.t. + Chart (M, (x1, x2, x3)) + Initial tangent vector: Tangent vector at Point p on the + 3-dimensional differentiable manifold M with + components [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) + + d(x1)/dt = Dx1 + d(Dx1)/dt = -A*Dx1*Dx2*x1^2 + d(x2)/dt = Dx2 + d(Dx2)/dt = 0 + d(x3)/dt = Dx3 + d(Dx3)/dt = -B*Dx2*Dx3*x2*x3 + + sage: sys_bis = c.system(verbose=False) + sage: sys_bis == sys + True + + """ + + v0 = self._initial_tangent_vector + chart = self._chart + + if verbose: + initial_tgt_space = v0.parent() + initial_pt = initial_tgt_space.base_point() # retrieves + # the initial point as the base point of the tangent space + # to which initial tangent vector belongs + initial_pt_coords = list(initial_pt.coordinates(chart)) + # previous line converts to list since would otherwise be a + # tuple ; will raise error if coordinates in chart are not + # known + + initial_coord_basis = chart.frame().at(initial_pt) + initial_tgt_vec_comps = v0[:,initial_coord_basis] # will + # raise error if components in coordinate basis are not + # known + + description = "Autoparallel curve " + if self._name is not None: + description += self._name + " " + description += "in the {} ".format(self.codomain()) + description += "equipped with " + description += "{}, ".format(self._affine_connection) + description += "and integrated over the " + description += "{} ".format(self.domain()) + description += "as a solution to the following equations, " + description += "written w.r.t. " + description += "{}:\n\n".format(chart) + + description += "Initial point: {} ".format(initial_pt) + description += "with coordinates " + description += "{} ".format(initial_pt_coords) + description += "w.r.t. {}\n".format(chart) + + description += "Initial tangent vector: {} ".format(v0) + description += "with components " + description +="{}".format(initial_tgt_vec_comps) + description += " w.r.t. {}\n\n".format(chart) + + zip_sys = zip(chart[:],self._velocities,self._equations_rhs) + for coord_func, velocity, eqn in zip_sys: + description += "d({})/d{} = {}\n".format(coord_func, + self._curve_parameter, + velocity) + description += "d({})/d{} = {}\n".format(velocity, + self._curve_parameter, + eqn) + print(description) + + return [self._equations_rhs, v0, chart] class IntegratedGeodesic(IntegratedAutoparallelCurve): - r""" - Constructs a numerical geodesic on the manifold. - + r""" + Constructs a numerical geodesic on the manifold with respect to a + given metric. + INPUT: - TO DO - + + - ``parent`` -- + :class:`~sage.manifolds.differentiable.manifold_homset.DifferentiableCurveSet` + the set of curves `\mathrm{Hom}(I, M)` to which the curve belongs + - ``metric`` -- + :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric` + metric with respect to which the curve is a geodesic + - ``curve_parameter`` -- symbolic expression to be used as the + parameter of the curve; + - ``initial_tangent_vector`` -- + :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` + initial tangent vector of the curve + - ``chart`` -- (default: ``None``) chart on the manifold in + which the equations are given; if ``None`` the default chart + of the manifold is assumed + - ``parameters`` -- list of the symbolic expressions used in the + coefficients of ``metric`` other than the coordinates + associated with the chart + - ``name`` -- (default: ``None``) string; symbol given to the curve + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote + the curve; if none is provided, ``name`` will be used + - ``is_isomorphism`` -- (default: ``False``) determines whether the + constructed object is a diffeomorphism; if set to ``True``, + then `M` must have dimension one + - ``is_identity`` -- (default: ``False``) determines whether the + constructed object is the identity map; if set to ``True``, + then `M` must coincide with the domain of the curve + + EXAMPLE: + + Geodesics of the unit 2-sphere :MATH:`\mathbb{S}^{2}`. + Start with declaring the standard polar coordinates + :MATH:`(\theta, \phi)` on :MATH:`\mathbb{S}^{2}` and the + corresponding coordinate frame :MATH:`(e_{\theta}, e_{\phi})`:: + + sage: S2 = Manifold(2, 'S^2', start_index=1) + sage: polar.=S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + sage: epolar = polar.frame() + + Set the Euclidean metric tensor :MATH:`g` induced on + :MATH:`\mathbb{S}^{2}`:: + + sage: g = S2.metric('g') + sage: g[1,1], g[2,2] = 1, (sin(th))^2 + + Set generic initial conditions for the geodesics to compute:: + + sage: [th0, ph0, v_th0, v_ph0] = var('th0 ph0 v_th0 v_ph0') + sage: p = S2.point((th0, ph0), name='p') + sage: Tp = S2.tangent_space(p) + sage: v = Tp((v_th0, v_ph0), basis=epolar.at(p)) + + Declare the corresponding integrated geodesic and display the + differential system it satisfies:: + + sage: [t, tmin, tmax] = var('t tmin tmax') + sage: c = S2.integrated_geodesic(g, (t, tmin, tmax), v, + ....: chart=polar, + ....: parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0],name='c') + sage: sys = c.system() + Geodesic c in the 2-dimensional differentiable manifold S^2 + equipped with Riemannian metric g on the 2-dimensional + differentiable manifold S^2, and integrated over the Real + interval (tmin, tmax) as a solution to the following geodesic + equations, written w.r.t. Chart (S^2, (th, ph)): + + Initial point: Point p on the 2-dimensional differentiable + manifold S^2 with coordinates [th0, ph0] w.r.t. + Chart (S^2, (th, ph)) + Initial tangent vector: Tangent vector at Point p on the + 2-dimensional differentiable manifold S^2 with + components [v_th0, v_ph0] w.r.t. Chart (S^2, (th, ph)) + + d(th)/dt = Dth + d(Dth)/dt = Dph^2*cos(th)*sin(th) + d(ph)/dt = Dph + d(Dph)/dt = -2*Dph*Dth*cos(th)/sin(th) + + + Set a dictionnary providing the parameter range and the initial + conditions for various geodesics:: + + sage: dict_params={'equat':{tmin:0,tmax:3,th0:pi/2,ph0:0.1,v_th0:0,v_ph0:1}, + ....: 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}, + ....: 'angle':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:1}} + + Use :MATH:`\mathbb{R}^{3}` as the codomain of the standard map + embedding :MATH:`(\mathbb{S}^{2}, (\theta, \phi))` in the + 3-dimensional Euclidean space:: + + sage: R3 = Manifold(3, 'R3', start_index=1) + sage: cart. = R3.chart() + sage: euclid_embedding = S2.diff_map(R3, + ....: {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) + + Solve, interpolate and prepare the plot for the solutions + corresponding to the three initial conditions previously set:: + + sage: graph3D_embedded_geods = Graphics() + sage: for key in dict_params.keys(): + ....: sol = c.solve(solution_key='sol-'+key, + ....: parameters_values=dict_params[key], verbose=False) + ....: interp = c.interpolate(solution_key='sol-'+key, + ....: interpolation_key='interp-'+key, verbose=False) + ....: graph3D_embedded_geods+=c.plot(interpolation_key='interp-'+key, + ....: mapping=euclid_embedding, thickness=5, + ....: display_tangent=True, scale=0.3, + ....: width_tangent=0.5, verbose=False) + + Plot the resulting geodesics on the grid of polar coordinates lines + on :MATH:`\mathbb{S}^{2}` and check that these are great circles:: + + sage: graph3D_embedded_polar_coords = polar.plot(chart=cart, + ....: mapping=euclid_embedding, + ....: number_values=15, color='yellow') + sage: graph=graph3D_embedded_geods+graph3D_embedded_polar_coords + sage: viewer3D = 'threejs' + sage: graph.show(viewer=viewer3D) + + .. PLOT:: + + S2 = Manifold(2, 'S^2', start_index=1) + polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + [th, ph] = var('th ph') + epolar = polar.frame() + g = S2.metric('g') + g[1,1], g[2,2] = 1, (sin(th))**2 + [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') + p = S2.point((th0, ph0), name='p') + Tp = S2.tangent_space(p) + v = Tp((v_th0, v_ph0), basis=epolar.at(p)) + c = S2.integrated_geodesic(g, (t, tmin, tmax), v, chart=polar, + parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], name='c') + dict_params={'equat':{tmin:0,tmax:3,th0:pi/2,ph0:0.1,v_th0:0,v_ph0:1}, + 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}, + 'angle':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:1}} + R3 = Manifold(3, 'R3', start_index=1) + cart = R3.chart('X Y Z') + [X,Y,Z] = var('X Y Z') + euclid_embedding = S2.diff_map(R3, + {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) + graph3D_embedded_geods = Graphics() + for key in dict_params.keys(): + sol = c.solve(solution_key='sol-'+key, + parameters_values=dict_params[key], verbose=False) + interp = c.interpolate(solution_key='sol-'+key, + interpolation_key='interp-'+key, verbose=False) + graph3D_embedded_geods+=c.plot(interpolation_key='interp-'+key, + mapping=euclid_embedding, thickness=5, + display_tangent=True, scale=0.3, + width_tangent=0.5, verbose=False) + graph3D_embedded_polar_coords = polar.plot(chart=cart, + mapping=euclid_embedding, + number_values=15, color='yellow') + graph = graph3D_embedded_geods + graph3D_embedded_polar_coords + sphinx_plot(graph) + """ - def __init__(self, parent, metric, curve_parameter, initial_tangent_vector, - chart=None, parameters=None, name=None, latex_name=None, - is_isomorphism=False, is_identity=False): - - r"""Construct the geodesic curve with respect to the given metric with the given initial tangent vector.""" - - - IntegratedAutoparallelCurve.__init__(self, parent, metric.connection(), curve_parameter, - initial_tangent_vector, chart=chart, parameters=parameters, - name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, - is_identity=is_identity) - - self._metric = metric + def __init__(self, parent, metric, curve_parameter, + initial_tangent_vector, chart=None, parameters=None, + name=None, latex_name=None, is_isomorphism=False, + is_identity=False): + r"""Constructs a geodesic curve with respect to the given metric + with the given initial tangent vector. + TESTS:: + + sage: S2 = Manifold(2, 'S^2') + sage: X. = S2.chart() + sage: [t, A] = var('t A') + sage: g = S2.metric('g') + sage: g[0,0] = A + sage: g[1,0] = 0 + sage: g[1,1] = A*sin(theta)^2 + sage: p = S2.point((pi/2,0), name='p') + sage: Tp = S2.tangent_space(p) + sage: v = Tp((1/sqrt(2),1/sqrt(2))) + sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', + ....: parameters=[A]); c + Integrated geodesic c in the 2-dimensional differentiable + manifold S^2 + + """ + + IntegratedAutoparallelCurve.__init__(self, parent, + metric.connection(), curve_parameter, + initial_tangent_vector, chart=chart, + parameters=parameters, name=name, + latex_name=latex_name, + is_isomorphism=is_isomorphism, + is_identity=is_identity) + + self._metric = metric def _repr_(self): - r""" + r""" Returns a string representation of ``self``. - TESTS:: - sage: #TO DO + TESTS:: + + sage: S2 = Manifold(2, 'S^2') + sage: X. = S2.chart() + sage: [t, A] = var('t A') + sage: g = S2.metric('g') + sage: g[0,0] = A + sage: g[1,0] = 0 + sage: g[1,1] = A*sin(theta)^2 + sage: p = S2.point((pi/2,0), name='p') + sage: Tp = S2.tangent_space(p) + sage: v = Tp((1/sqrt(2),1/sqrt(2))) + sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, + ....: parameters=[A]); c + Integrated geodesic in the 2-dimensional differentiable + manifold S^2 + sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', + ....: parameters=[A]); c + Integrated geodesic c in the 2-dimensional differentiable + manifold S^2 """ @@ -1151,45 +2410,111 @@ def _repr_(self): if self._name is not None: description += self._name + " " description += "in the {}".format(self._codomain) - return description - + return description - - def system(self): + def system(self, verbose=True): r""" - Returns the system defining the geodesic : chart, equations and initial conditions - - TESTS:: - sage: #TO DO - + Returns the system defining the geodesic : chart, equations and + initial conditions + + INPUT: + + - ``verbose`` -- (default: ``True``) prints a detailed + description of the curve + + OUTPUT: + + - list containing the attributes :attr:`equations_rhs`, + :attr:`initial_tangent_vector` and :attr:`chart` + + TESTS:: + + sage: S2 = Manifold(2, 'S^2') + sage: X. = S2.chart() + sage: [t, A] = var('t A') + sage: g = S2.metric('g') + sage: g[0,0] = A + sage: g[1,0] = 0 + sage: g[1,1] = A*sin(theta)^2 + sage: p = S2.point((pi/2,0), name='p') + sage: Tp = S2.tangent_space(p) + sage: v = Tp((1/sqrt(2),1/sqrt(2))) + sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', + ....: parameters=[A]) + sage: sys = c.system() + Geodesic c in the 2-dimensional differentiable manifold S^2 + equipped with Riemannian metric g on the 2-dimensional + differentiable manifold S^2, and integrated over the Real + interval (0, pi) as a solution to the following geodesic + equations, written w.r.t. Chart (S^2, (theta, phi)): + + Initial point: Point p on the 2-dimensional differentiable + manifold S^2 with coordinates [1/2*pi, 0] w.r.t. + Chart (S^2, (theta, phi)) + Initial tangent vector: Tangent vector at Point p on the + 2-dimensional differentiable manifold S^2 with + components [1/2*sqrt(2), 1/2*sqrt(2)] w.r.t. + Chart (S^2, (theta, phi)) + + d(theta)/dt = Dtheta + d(Dtheta)/dt = Dphi^2*cos(theta)*sin(theta) + d(phi)/dt = Dphi + d(Dphi)/dt = -2*Dphi*Dtheta*cos(theta)/sin(theta) + + sage: sys_bis = c.system(verbose=False) + sage: sys_bis == sys + True + """ - - initial_tangent_space = self._initial_tangent_vector.parent() - initial_point = initial_tangent_space.base_point() # gets the initial - # point as the base point of the tangent space which initial tangent vector belongs to - initial_point_coordinates = initial_point.coordinates(self._chart) # will - # raise error if coordinates in chart are not known - initial_point_coordinates = [coord for coord in initial_point_coordinates] # converts - # to list since was previously a tuple - - initial_coordinate_basis = self._chart.frame().at(initial_point) - initial_tangent_vector_components = self._initial_tangent_vector[:, initial_coordinate_basis] # will - # raise error if components in coordinate basis are not known - - description = "Geodesic " - if self._name is not None: - description += self._name + " " - description += "in the {} equipped with the {}, and integrated over the {} ".format(self.codomain(), self._metric, self.domain()) - description += "as a solution to the following geodesic equations, written with respect to the {}:\n\n".format(self._chart) - - description += "Initial point: {} with coordinates {} in {}\n".format(initial_point, - initial_point_coordinates, self._chart) - description += "Initial tangent vector: {} with components {} in {}\n\n".format(self._initial_tangent_vector, - initial_tangent_vector_components, self._chart) - - for coord_func, velocity, eqn in zip(self._chart[:], self._velocities, self._equations_rhs): - description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) - description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) - print(description) - - return [self._equations_rhs, self._initial_tangent_vector, self._chart] + + v0 = self._initial_tangent_vector + chart = self._chart + + if verbose: + initial_tgt_space = v0.parent() + initial_pt = initial_tgt_space.base_point()#retrieves + # the initial point as the base point of the tangent space + # to which initial tangent vector belongs + initial_pt_coords = list(initial_pt.coordinates(chart)) + # previous line converts to list since would otherwise be a + # tuple ; will raise error if coordinates in chart are not + # known + + initial_coord_basis = chart.frame().at(initial_pt) + initial_tgt_vec_comps=v0[:,initial_coord_basis]#will + # raise error if components in coordinate basis are not + # known + + description = "Geodesic " + if self._name is not None: + description += self._name + " " + description += "in the {} ".format(self.codomain()) + description += "equipped with " + description += "{}, ".format(self._metric) + description += "and integrated over the " + description += "{} ".format(self.domain()) + description += "as a solution to the following " + description += "geodesic equations, written w.r.t. " + description += "{}:\n\n".format(chart) + + description += "Initial point: {} ".format(initial_pt) + description += "with coordinates " + description += "{} ".format(initial_pt_coords) + description += "w.r.t. {}\n".format(chart) + + description += "Initial tangent vector: {} ".format(v0) + description += "with components " + description +="{}".format(initial_tgt_vec_comps) + description += " w.r.t. {}\n\n".format(chart) + + zip_sys = zip(chart[:],self._velocities,self._equations_rhs) + for coord_func, velocity, eqn in zip_sys: + description += "d({})/d{} = {}\n".format(coord_func, + self._curve_parameter, + velocity) + description += "d({})/d{} = {}\n".format(velocity, + self._curve_parameter, + eqn) + print(description) + + return [self._equations_rhs, v0, chart] diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index fda5836a718..eb2cc4bb94e 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -2574,43 +2574,106 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, initial_tangent_vector, chart=None, parameters=None, name=None, latex_name=None, is_isomorphism=False, is_identity=False): - r""" - Constructs a numerical curve defined by a system of second order differential - equations in the coordinate functions. + Constructs a numerical curve defined by a system of second order + differential equations in the coordinate functions. .. SEEALSO:: - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` for details. - INPUT: TO COMPLETE ! ! ! ! ! + INPUT: - - `` - - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, where + - ``equations_rhs`` -- list of the right-hand sides of the + equations on the velocities only + - ``velocities`` -- list of the symbolic expressions used in + ``equations_rhs`` to denote the velocities + - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, + where - * ``t`` is the curve parameter used in ``coord_expression``; + * ``t`` is the symbolic expression used in``equations_rhs`` to + denote the parameter of the curve ; * ``t_min`` is its minimal (finite) value; * ``t_max`` its maximal (finite) value; + - ``initial_tangent_vector`` -- + :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` + initial tangent vector of the curve + - ``chart`` -- (default: ``None``) chart on the manifold in + which the equations are given ; if ``None`` the default chart + of the manifold is assumed + - ``parameters`` -- list of the symbolic expressions used in + ``equations_rhs`` other than the coordinates, the velocities + and the curve parameter - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used + - ``is_isomorphism`` -- (default: ``False``) determines whether the + constructed object is a diffeomorphism; if set to ``True``, + then `M` must have dimension one + - ``is_identity`` -- (default: ``False``) determines whether the + constructed object is the identity map; if set to ``True``, + then `M` must coincide with the domain of the curve OUTPUT: - - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` - EXAMPLES: + EXAMPLE: - #TO DO + Trajectory of a particle of unit mass and unit charge in an + unit, axial, uniform, stationnary magnetic field:: - .. SEEALSO:: - - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` - for more examples, including plots. + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: var('t') + t + sage: D = X.symbolic_velocities() + sage: eqns = [D[1], -D[0], SR(0)] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t, 0, 6), v,name='c') + sage: c + Integrated curve c in the 3-dimensional differentiable + manifold M + sage: sys = c.system() + Curve c in the 3-dimensional differentiable manifold M + integrated over the Real interval (0, 6) as a solution to + the following system, written w.r.t. + Chart (M, (x1, x2, x3)): + + Initial point: Point p on the 3-dimensional differentiable + manifold M with coordinates [0, 0, 0] w.r.t. + Chart (M, (x1, x2, x3)) + Initial tangent vector: Tangent vector at Point p on the + 3-dimensional differentiable manifold M with + components [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) + + d(x1)/dt = Dx1 + d(Dx1)/dt = Dx2 + d(x2)/dt = Dx2 + d(Dx2)/dt = -Dx1 + d(x3)/dt = Dx3 + d(Dx3)/dt = 0 + + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1.3) + Evaluating point coordinates from the interpolation + associated with the key 'cubic spline-interp-rk4' + by default... + [0.9635578097813995, -0.7325001553656034, 1.3] + sage: tgt_vec = c.tangent_vector_eval_at(3.7) + Evaluating tangent vector components from the interpolation + associated with the key 'cubic spline-interp-rk4' + by default... + sage: tgt_vec[:] + [-0.8481002291911669, 0.5298327234653155, 1.0000000000000036] """ + from sage.manifolds.differentiable.real_line import RealLine from sage.manifolds.differentiable.integrated_curve import IntegratedCurve if len(curve_param) != 3: @@ -2623,51 +2686,127 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, interval = real_field.open_interval(t_min, t_max) curve_set = Hom(interval, self) return IntegratedCurve(curve_set, equations_rhs, velocities, t, - initial_tangent_vector, chart=chart, parameters=parameters, - name=name, latex_name=latex_name, - is_isomorphism=is_isomorphism, - is_identity=is_identity) #use someone's element_constructor ? + initial_tangent_vector, chart=chart, parameters=parameters, + name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, + is_identity=is_identity) #use any element_constructor ? - - - def integrated_autoparallel_curve(self, affine_connection, curve_param, initial_tangent_vector, chart=None, + def integrated_autoparallel_curve(self, affine_connection, curve_param, + initial_tangent_vector, chart=None, parameters=None, name=None, latex_name=None, is_isomorphism=False, is_identity=False): - r""" - - Constructs an numerical autoparallel curve. + Constructs a numerical autoparallel curve on the manifold with + respect to a given affine connection. .. SEEALSO:: - :class:`~sage.manifolds.differentiable.curve.IntegratedCurve` + :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` for details. - INPUT: TO COMPLETE ! ! ! ! + INPUT: - - `` - - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, where + - ``affine_connection`` -- + :class:`~sage.manifolds.differentiable.affine_connection.AffineConnection` + affine connection with respect to which the curve is + autoparallel + - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, + where - * ``t`` is the curve parameter used in ``coord_expression``; + * ``t`` is the symbolic expression to be used as the parameter + of the curve ; * ``t_min`` is its minimal (finite) value; * ``t_max`` its maximal (finite) value; + - ``initial_tangent_vector`` -- + :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` + initial tangent vector of the curve + - ``chart`` -- (default: ``None``) chart on the manifold in + which the equations are given ; if ``None`` the default chart + of the manifold is assumed + - ``parameters`` -- list of the symbolic expressions used in the + coefficients of ``affine_connection`` other than the + coordinates associated with the chart - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used + - ``is_isomorphism`` -- (default: ``False``) determines whether the + constructed object is a diffeomorphism; if set to ``True``, + then `M` must have dimension one + - ``is_identity`` -- (default: ``False``) determines whether the + constructed object is the identity map; if set to ``True``, + then `M` must coincide with the domain of the curve OUTPUT: - - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` - - EXAMPLES: - - #TO DO - - .. SEEALSO:: - - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` - for more examples, including plots. + - :class:`~sage.manifolds.differentiable.curve.IntegratedAutoparallelCurve` + + EXAMPLE: + + Autoparallel curves associated with the Mercator projection of + the unit 2-sphere :MATH:`\mathbb{S}^{2}`:: + + sage: S2 = Manifold(2, 'S^2', start_index=1) + sage: polar.=S2.chart('th ph') + sage: epolar = polar.frame() + sage: ch_basis = S2.automorphism_field() + sage: ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) + sage: epolar_ON=S2.default_frame().new_frame(ch_basis,'epolar_ON') + + Set the affine connection associated with Mercator projection ; + it is metric compatible but it has non-vanishing torsion:: + + sage: nab = S2.affine_connection('nab') + sage: nab.set_coef(epolar_ON)[:] + [[[0, 0], [0, 0]], [[0, 0], [0, 0]]] + sage: g = S2.metric('g') + sage: g[1,1], g[2,2] = 1, (sin(th))^2 + sage: nab(g)[:] + [[[0, 0], [0, 0]], [[0, 0], [0, 0]]] + sage: nab.torsion()[:] + [[[0, 0], [0, 0]], [[0, cos(th)/sin(th)], [-cos(th)/sin(th), 0]]] + + Declare an integrated autoparallel curve w.r.t. this + connection:: + + sage: p = S2.point((pi/4, 0), name='p') + sage: Tp = S2.tangent_space(p) + sage: v = Tp((1,1), basis=epolar_ON.at(p)) + sage: t = var('t') + sage: c = S2.integrated_autoparallel_curve(nab, (t, 0, 6), + ....: v, chart=polar, name='c') + sage: sys = c.system() + Autoparallel curve c in the 2-dimensional differentiable + manifold S^2 equipped with Affine connection nab on the + 2-dimensional differentiable manifold S^2, and integrated + over the Real interval (0, 6) as a solution to the + following equations, written w.r.t. Chart (S^2, (th, ph)): + + Initial point: Point p on the 2-dimensional differentiable + manifold S^2 with coordinates [1/4*pi, 0] w.r.t. + Chart (S^2, (th, ph)) + Initial tangent vector: Tangent vector at Point p on the + 2-dimensional differentiable manifold S^2 with + components [1, sqrt(2)] w.r.t. + Chart (S^2, (th, ph)) + + d(th)/dt = Dth + d(Dth)/dt = 0 + d(ph)/dt = Dph + d(Dph)/dt = -Dph*Dth*cos(th)/sin(th) + + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1.3) + Evaluating point coordinates from the interpolation + associated with the key 'cubic spline-interp-rk4' + by default... + [2.085398163397449, 1.420314580385403] + sage: tgt_vec = c.tangent_vector_eval_at(3.7) + Evaluating tangent vector components from the interpolation + associated with the key 'cubic spline-interp-rk4' + by default... + sage: tgt_vec[:] + [0.9999999999999986, -0.9736581694086809] """ @@ -2682,51 +2821,114 @@ def integrated_autoparallel_curve(self, affine_connection, curve_param, initial_ real_field = RealLine(names=(repr(t),)) interval = real_field.open_interval(t_min, t_max) curve_set = Hom(interval, self) - return IntegratedAutoparallelCurve(curve_set, affine_connection, t, initial_tangent_vector, chart=chart, - name=name, latex_name=latex_name, - is_isomorphism=is_isomorphism, - is_identity=is_identity) #use someone's element_constructor ? - - - - def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, chart=None, - name=None, latex_name=None, is_isomorphism=False, - is_identity=False): - + return IntegratedAutoparallelCurve(curve_set, affine_connection, t, + initial_tangent_vector, chart=chart, parameters=parameters, + name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, + is_identity=is_identity) + #use any element_constructor ? + + def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, + chart=None, parameters=None, name=None, latex_name=None, + is_isomorphism=False, is_identity=False): r""" - - Constructs a numerical geodesic. + Constructs a numerical geodesic on the manifold with respect to + a given metric. .. SEEALSO:: - :class:`~sage.manifolds.differentiable.curve.IntegratedAutoparallelCurve` + :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic` for details. - INPUT: TO COMPLETE ! ! ! ! + INPUT: - - `` - - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, where + - ``metric`` -- + :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric` + metric with respect to which the curve is a geodesic + - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, + where - * ``t`` is the curve parameter used in ``coord_expression``; + * ``t`` is the symbolic expression to be used as the parameter + of the curve ; * ``t_min`` is its minimal (finite) value; * ``t_max`` its maximal (finite) value; + - ``initial_tangent_vector`` -- + :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` + initial tangent vector of the curve + - ``chart`` -- (default: ``None``) chart on the manifold in + which the equations are given ; if ``None`` the default chart + of the manifold is assumed + - ``parameters`` -- list of the symbolic expressions used in the + coefficients of ``metric`` other than the coordinates + associated with the chart - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used + - ``is_isomorphism`` -- (default: ``False``) determines whether the + constructed object is a diffeomorphism; if set to ``True``, + then `M` must have dimension one + - ``is_identity`` -- (default: ``False``) determines whether the + constructed object is the identity map; if set to ``True``, + then `M` must coincide with the domain of the curve OUTPUT: - - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` - - EXAMPLES: - - #TO DO - - .. SEEALSO:: - - :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve` - for more examples, including plots. + - :class:`~sage.manifolds.differentiable.curve.IntegratedGeodesic` + + EXAMPLE: + + Geodesics of the unit 2-sphere :MATH:`\mathbb{S}^{2}`:: + + sage: S2 = Manifold(2, 'S^2', start_index=1) + sage: polar.=S2.chart('th ph') + sage: epolar = polar.frame() + + Set the Euclidean metric tensor :MATH:`g` induced on + :MATH:`\mathbb{S}^{2}`:: + + sage: g = S2.metric('g') + sage: g[1,1], g[2,2] = 1, (sin(th))^2 + + Declare an integrated geodesic w.r.t. this metric:: + + sage: p = S2.point((pi/4, 0), name='p') + sage: Tp = S2.tangent_space(p) + sage: v = Tp((1, 1), basis=epolar.at(p)) + sage: t = var('t') + sage: c = S2.integrated_geodesic(g, (t, 0, 6), v, + ....: chart=polar, name='c') + sage: sys = c.system() + Geodesic c in the 2-dimensional differentiable manifold S^2 + equipped with Riemannian metric g on the 2-dimensional + differentiable manifold S^2, and integrated over the Real + interval (0, 6) as a solution to the following geodesic + equations, written w.r.t. Chart (S^2, (th, ph)): + + Initial point: Point p on the 2-dimensional differentiable + manifold S^2 with coordinates [1/4*pi, 0] w.r.t. + Chart (S^2, (th, ph)) + Initial tangent vector: Tangent vector at Point p on the + 2-dimensional differentiable manifold S^2 with + components [1, 1] w.r.t. Chart (S^2, (th, ph)) + + d(th)/dt = Dth + d(Dth)/dt = Dph^2*cos(th)*sin(th) + d(ph)/dt = Dph + d(Dph)/dt = -2*Dph*Dth*cos(th)/sin(th) + + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1.3) + Evaluating point coordinates from the interpolation + associated with the key 'cubic spline-interp-rk4' + by default... + [2.204750869496952, 0.7986664139663504] + sage: tgt_vec = c.tangent_vector_eval_at(3.7) + Evaluating tangent vector components from the interpolation + associated with the key 'cubic spline-interp-rk4' + by default... + sage: tgt_vec[:] + [-1.0907562667574524, 0.6205613159665633] """ from sage.manifolds.differentiable.real_line import RealLine @@ -2740,10 +2942,10 @@ def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, chart real_field = RealLine(names=(repr(t),)) interval = real_field.open_interval(t_min, t_max) curve_set = Hom(interval, self) - return IntegratedGeodesic(curve_set, metric, t, initial_tangent_vector, chart=chart, - name=name, latex_name=latex_name, - is_isomorphism=is_isomorphism, - is_identity=is_identity) #use someone's element_constructor ? + return IntegratedGeodesic(curve_set, metric, t, initial_tangent_vector, + chart=chart, parameters=parameters, name=name, latex_name=latex_name, + is_isomorphism=is_isomorphism, is_identity=is_identity) + #use any element_constructor ? def affine_connection(self, name, latex_name=None): r""" From e1ca86ed9331a9a438ca8997fbef4327dfece067 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Sat, 10 Jun 2017 20:11:34 +0200 Subject: [PATCH 028/184] trac #23210: add method immediate_dominators to DiGraph --- src/sage/graphs/digraph.py | 117 +++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index cd5d8c482f1..14e9477ba13 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -3184,6 +3184,123 @@ def is_strongly_connected(self): except AttributeError: return len(self.strongly_connected_components()) == 1 + + def immediate_dominators(self, root): + r""" + Return the immediate dominators of all vertices reachable from `root`. + + A flowgraph `G = (V, A, root)` is a digraph where every vertex in `V` is + reachable from a distinguished root vertex `root\in V`. In such digraph, + a vertex `w` dominates a vertex `v` if every path from `root` to `v` + includes `w`. Let `dom(v)` be the set of the vertices that dominate `v`. + Obviously, `root` and `v`, the trivial dominators of `v`, are in + `dom(v)`. For `v \neq root`, the immediate dominator of `v`, denoted by + `d(v)`, is the unique vertex `w \neq v` that dominates `v` and is + dominated by all the vertices in `dom(v)\setminus\{v\}`. The (immediate) + dominator tree is a directed tree (or arborescence) rooted at `root` + that is formed by the arcs `\{ (d(v), v)\mid v\in V\setminus\{root\}\}`. + See [Geo05]_ for more details. + + This method implements the algorithm proposed in [CHK01]_ which performs + very well in practice, although its worst case time complexity is in + `O(n^2)`. + + INPUT: + + - ``root`` -- a vertex of the digraph, the root of the + immediate dominators tree. + + OUTPUT: The (immediate) dominator tree rooted at ``root``, + encoded as a predecessor dictionary. + + .. SEEALSO:: + + - :meth:`~DiGraph.strong_bridges` + - :meth:`~DiGraph.strong_articulation_points` + - :meth:`~DiGraph.strong_connected_components` + + EXAMPLES: + + The output encodes a tree rooted at ``root``:: + + sage: D = digraphs.Complete(4) * 2 + sage: D.add_edges([(0, 4), (7, 3)]) + sage: d = D.immediate_dominators(0) + sage: T = DiGraph([(d[u], u) for u in d if u != d[u]]) + sage: Graph(T).is_tree() + True + sage: all(T.in_degree(u) <= 1 for u in T) + True + + In a strongly connected digraph, the result depends on the root:: + + sage: D = digraphs.Circuit(5) + sage: D.immediate_dominators(0) + {0: 0, 1: 0, 2: 1, 3: 2, 4: 3} + sage: D.immediate_dominators(1) + {0: 4, 1: 1, 2: 1, 3: 2, 4: 3} + + The (immediate) dominator tree contains only reachable vertices:: + + sage: P = digraphs.Path(5) + sage: P.immediate_dominators(0) + {0: 0, 1: 0, 2: 1, 3: 2, 4: 3} + sage: P.immediate_dominators(3) + {3: 3, 4: 3} + + TESTS: + + When ``root`` is not in the digraph:: + + sage: DiGraph().immediate_dominators(0) + Traceback (most recent call last): + ... + ValueError: the given root must be in the digraph + + Comparison with the NetworkX method:: + + sage: import networkx + sage: D = digraphs.RandomDirectedGNP(20,0.1) + sage: d = D.immediate_dominators(0) + sage: dx = networkx.immediate_dominators(D.networkx_graph(), 0) + sage: all(d[i] == dx[i] for i in d) and all(d[i] == dx[i] for i in dx) + True + """ + if not root in self: + raise ValueError("the given root must be in the digraph") + + idom = {root: root} + + n = self.order() + pre_order = list(self.depth_first_search(root)) + number = {u: n-i for i, u in enumerate(pre_order)} + pre_order.pop(0) + + def intersect(u, v): + while u != v: + while number[u] < number[v]: + u = idom[u] + while number[u] > number[v]: + v = idom[v] + return u + + changed = True + while changed: + changed = False + for u in pre_order: + pred = [v for v in self.neighbor_in_iterator(u) if v in idom] + if not pred: + continue + else: + new_idom = pred[0] + for v in pred[1:]: + new_idom = intersect(new_idom, v) + if not u in idom or idom[u] != new_idom: + idom[u] = new_idom + changed = True + + return idom + def is_aperiodic(self): r""" Return whether the current ``DiGraph`` is aperiodic. From 1b4a759f253ba911c4c3f4d7a669935134f42cd1 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Sat, 10 Jun 2017 20:12:12 +0200 Subject: [PATCH 029/184] trac #23210: add method strong_articulation_points to DiGraph --- src/sage/graphs/digraph.py | 104 +++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 14e9477ba13..1800307429d 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -3301,6 +3301,110 @@ def intersect(u, v): return idom + def strong_articulation_points(self): + r""" + Return the strong articulation points of this digraph. + + A vertex is a strong articulation point if its deletion increases the + number of strongly connected components. This method implements the + algorithm described in [ILS11]_. The time complexity is dominated by the + time complexity of the immediate dominators finding algorithm. + + OUTPUT: The list of strong articulation points. + + .. SEEALSO:: + + - :meth:`~DiGraph.strong_bridges` + - :meth:`~DiGraph.strong_connected_components` + - :meth:`~DiGraph.immediate_dominators` + + EXAMPLES: + + Two cliques sharing a vertex:: + + sage: D = digraphs.Complete(4) + sage: D.add_clique([3, 4, 5, 6]) + sage: D.strong_articulation_points() + [3] + + Two cliques connected by some arcs:: + + sage: D = digraphs.Complete(4) * 2 + sage: D.add_edges([(0, 4), (7, 3)]) + sage: sorted( D.strong_articulation_points() ) + [0, 3, 4, 7] + sage: D.add_edge(1, 5) + sage: sorted( D.strong_articulation_points() ) + [3, 7] + sage: D.add_edge(6, 2) + sage: D.strong_articulation_points() + [] + + TESTS: + + All strong articulation points are found:: + + sage: def sap_naive(G): + ....: nscc = len(G.strongly_connected_components()) + ....: S = [] + ....: for u in G: + ....: H = copy(G) + ....: H.delete_vertex(u) + ....: if len(H.strongly_connected_components()) > nscc: + ....: S.append(u) + ....: return S + sage: D = digraphs.RandomDirectedGNP(20, 0.1) + sage: X = sap_naive(D) + sage: SAP = D.strong_articulation_points() + sage: set(X) == set(SAP) + True + + Trivial cases:: + + sage: DiGraph().strong_articulation_points() + [] + sage: DiGraph(1).strong_articulation_points() + [] + sage: DiGraph(2).strong_articulation_points() + [] + """ + # The method is applied on each strongly connected component + if self.is_strongly_connected(): + L = [self] + else: + L = self.strongly_connected_components_subgraphs() + + SAP = list() + for g in L: + n = g.order() + if n <= 1: + continue + if n == 2: + SAP.extend( g.vertices() ) + continue + + # 1. Choose arbitrarily a vertex r, and test whether r is a strong + # articulation point. + r = next(g.vertex_iterator()) + E = g.incoming_edges(r) + g.outgoing_edges(r) + g.delete_vertex(r) + if not g.is_strongly_connected(): + SAP.append(r) + g.add_edges(E) + + # 2. Compute the set of non-trivial immediate dominators in g + Dr = set( g.immediate_dominators(r).values() ) + + # 3. Compute the set of non-trivial immediate dominators in the + # reverse digraph + g_reverse = g.reverse() + DRr = set( g_reverse.immediate_dominators(r).values() ) + + # 4. Store D(r) + DR(r) - r + SAP.extend( Dr.union(DRr).difference([r]) ) + + return SAP + def is_aperiodic(self): r""" Return whether the current ``DiGraph`` is aperiodic. From 59192bbbdae65c25831556c5a03a13fddaadaad7 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Sat, 10 Jun 2017 20:17:35 +0200 Subject: [PATCH 030/184] trac #23210: add entry to module documentation --- src/sage/graphs/digraph.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 1800307429d..087685817a5 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -70,6 +70,8 @@ :meth:`~DiGraph.strongly_connected_components_subgraphs` | Returns the strongly connected components as a list of subgraphs. :meth:`~DiGraph.strongly_connected_component_containing_vertex` | Returns the strongly connected component containing a given vertex :meth:`~DiGraph.strongly_connected_components` | Returns the list of strongly connected components. + :meth:`~DiGraph.immediate_dominators | Return the immediate dominators of all vertices reachable from `root`. + :meth:`~DiGraph.strong_articulation_points` | Return the strong articulation points of this digraph. **Acyclicity:** From e054cdde472974fc554dd1779a95ef17589ed89c Mon Sep 17 00:00:00 2001 From: David Coudert Date: Sat, 10 Jun 2017 20:36:41 +0200 Subject: [PATCH 031/184] trac #23210: biblio and documentation --- src/doc/en/reference/references/index.rst | 16 ++++++++++++++++ src/sage/graphs/digraph.py | 13 +++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index e1c04ad648f..795a5e8e032 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -381,6 +381,11 @@ REFERENCES: .. [ChenDB] Eric Chen, Online database of two-weight codes, http://moodle.tec.hkr.se/~chen/research/2-weight-codes/search.php +.. [CHK2001] Keith D. Cooper, Timothy J. Harvey and Ken Kennedy. *A + Simple, Fast Dominance Algorithm*, Software practice and + Experience, 4:1-10 (2001). + http://www.hipersoft.rice.edu/grads/publications/dom14.pdf + .. [CK1999] David A. Cox and Sheldon Katz. *Mirror symmetry and algebraic geometry*, volume 68 of *Mathematical Surveys and Monographs*. American Mathematical Society, @@ -725,6 +730,11 @@ REFERENCES: operations on finite simplicial complexes* in Homology, Homotopy and Applications 5 (2003), 83-93. +.. [Ge2005] Loukas Georgiadis, *Linear-Time Algorithms for Dominators + and Related Problems*, PhD thesis, Princetown University, + TR-737-05, (2005). + ftp://ftp.cs.princeton.edu/reports/2005/737.pdf + .. [GG2012] Jim Geelen and Bert Gerards, Characterizing graphic matroids by a system of linear equations, submitted, 2012. Preprint: @@ -898,6 +908,12 @@ REFERENCES: **I** +.. [ILS2012] Giuseppe F. Italiano, Luigi Laura, and Federico + Santaroni. *Finding strong bridges and strong + articulation points in linear time*. Theoretical Computer + Science, 447, 74–84 (2012). + :doi:`10.1016/j.tcs.2011.11.011` + .. [IR1990] \K. Ireland and M. Rosen, *A Classical Introduction to Modern Number Theory*, Springer-Verlag, GTM volume 84, 1990. diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 087685817a5..8b802ab87d5 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -3201,11 +3201,11 @@ def immediate_dominators(self, root): dominated by all the vertices in `dom(v)\setminus\{v\}`. The (immediate) dominator tree is a directed tree (or arborescence) rooted at `root` that is formed by the arcs `\{ (d(v), v)\mid v\in V\setminus\{root\}\}`. - See [Geo05]_ for more details. + See [Ge2005]_ for more details. - This method implements the algorithm proposed in [CHK01]_ which performs - very well in practice, although its worst case time complexity is in - `O(n^2)`. + This method implements the algorithm proposed in [CHK2001]_ which + performs very well in practice, although its worst case time complexity + is in `O(n^2)`. INPUT: @@ -3217,6 +3217,7 @@ def immediate_dominators(self, root): .. SEEALSO:: + - :wikipedia:`Dominator_(graph_theory)` - :meth:`~DiGraph.strong_bridges` - :meth:`~DiGraph.strong_articulation_points` - :meth:`~DiGraph.strong_connected_components` @@ -3309,8 +3310,8 @@ def strong_articulation_points(self): A vertex is a strong articulation point if its deletion increases the number of strongly connected components. This method implements the - algorithm described in [ILS11]_. The time complexity is dominated by the - time complexity of the immediate dominators finding algorithm. + algorithm described in [ILS2012]_. The time complexity is dominated by + the time complexity of the immediate dominators finding algorithm. OUTPUT: The list of strong articulation points. From 43b3970232fe9ab49fb75a28b4aefc0c966a7c19 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Sun, 11 Jun 2017 09:27:25 +0200 Subject: [PATCH 032/184] trac #23210: fix issues in the documentation --- src/sage/graphs/digraph.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 8b802ab87d5..d582339ffe4 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -70,7 +70,7 @@ :meth:`~DiGraph.strongly_connected_components_subgraphs` | Returns the strongly connected components as a list of subgraphs. :meth:`~DiGraph.strongly_connected_component_containing_vertex` | Returns the strongly connected component containing a given vertex :meth:`~DiGraph.strongly_connected_components` | Returns the list of strongly connected components. - :meth:`~DiGraph.immediate_dominators | Return the immediate dominators of all vertices reachable from `root`. + :meth:`~DiGraph.immediate_dominators` | Return the immediate dominators of all vertices reachable from `root`. :meth:`~DiGraph.strong_articulation_points` | Return the strong articulation points of this digraph. @@ -3218,9 +3218,8 @@ def immediate_dominators(self, root): .. SEEALSO:: - :wikipedia:`Dominator_(graph_theory)` - - :meth:`~DiGraph.strong_bridges` - :meth:`~DiGraph.strong_articulation_points` - - :meth:`~DiGraph.strong_connected_components` + - :meth:`~DiGraph.strongly_connected_components` EXAMPLES: @@ -3317,8 +3316,7 @@ def strong_articulation_points(self): .. SEEALSO:: - - :meth:`~DiGraph.strong_bridges` - - :meth:`~DiGraph.strong_connected_components` + - :meth:`~DiGraph.strongly_connected_components` - :meth:`~DiGraph.immediate_dominators` EXAMPLES: From 6ce69d316c261a00797b5c1a2c3621ea70a1a837 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Mon, 12 Jun 2017 08:57:46 +0200 Subject: [PATCH 033/184] trac #23210: reviewers comments --- src/sage/graphs/digraph.py | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index d582339ffe4..36c25c21457 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -3187,21 +3187,21 @@ def is_strongly_connected(self): return len(self.strongly_connected_components()) == 1 - def immediate_dominators(self, root): + def immediate_dominators(self, r): r""" - Return the immediate dominators of all vertices reachable from `root`. + Return the immediate dominators of all vertices reachable from `r`. - A flowgraph `G = (V, A, root)` is a digraph where every vertex in `V` is - reachable from a distinguished root vertex `root\in V`. In such digraph, - a vertex `w` dominates a vertex `v` if every path from `root` to `v` + A flowgraph `G = (V, A, r)` is a digraph where every vertex in `V` is + reachable from a distinguished root vertex `r\in V`. In such digraph, a + vertex `w` dominates a vertex `v` if every path from `root` to `v` includes `w`. Let `dom(v)` be the set of the vertices that dominate `v`. - Obviously, `root` and `v`, the trivial dominators of `v`, are in - `dom(v)`. For `v \neq root`, the immediate dominator of `v`, denoted by + Obviously, `r` and `v`, the trivial dominators of `v`, are in + `dom(v)`. For `v \neq r`, the immediate dominator of `v`, denoted by `d(v)`, is the unique vertex `w \neq v` that dominates `v` and is dominated by all the vertices in `dom(v)\setminus\{v\}`. The (immediate) - dominator tree is a directed tree (or arborescence) rooted at `root` - that is formed by the arcs `\{ (d(v), v)\mid v\in V\setminus\{root\}\}`. - See [Ge2005]_ for more details. + dominator tree is a directed tree (or arborescence) rooted at `r` that + is formed by the arcs `\{ (d(v), v)\mid v\in V\setminus\{r\}\}`. See + [Ge2005]_ for more details. This method implements the algorithm proposed in [CHK2001]_ which performs very well in practice, although its worst case time complexity @@ -3209,11 +3209,11 @@ def immediate_dominators(self, root): INPUT: - - ``root`` -- a vertex of the digraph, the root of the - immediate dominators tree. + - ``r`` -- a vertex of the digraph, the root of the immediate dominators + tree - OUTPUT: The (immediate) dominator tree rooted at ``root``, - encoded as a predecessor dictionary. + OUTPUT: The (immediate) dominator tree rooted at `r`, encoded as a + predecessor dictionary. .. SEEALSO:: @@ -3223,7 +3223,7 @@ def immediate_dominators(self, root): EXAMPLES: - The output encodes a tree rooted at ``root``:: + The output encodes a tree rooted at `r`:: sage: D = digraphs.Complete(4) * 2 sage: D.add_edges([(0, 4), (7, 3)]) @@ -3252,7 +3252,7 @@ def immediate_dominators(self, root): TESTS: - When ``root`` is not in the digraph:: + When `r` is not in the digraph:: sage: DiGraph().immediate_dominators(0) Traceback (most recent call last): @@ -3268,13 +3268,13 @@ def immediate_dominators(self, root): sage: all(d[i] == dx[i] for i in d) and all(d[i] == dx[i] for i in dx) True """ - if not root in self: + if r not in self: raise ValueError("the given root must be in the digraph") - idom = {root: root} + idom = {r: r} n = self.order() - pre_order = list(self.depth_first_search(root)) + pre_order = list(self.depth_first_search(r)) number = {u: n-i for i, u in enumerate(pre_order)} pre_order.pop(0) @@ -3375,7 +3375,7 @@ def strong_articulation_points(self): else: L = self.strongly_connected_components_subgraphs() - SAP = list() + SAP = [] for g in L: n = g.order() if n <= 1: From b071c25e9ad96161af7f4728e9854e24c30b1455 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Mon, 12 Jun 2017 17:01:35 +0200 Subject: [PATCH 034/184] trac #23210: cope with mutable graphs --- src/sage/graphs/digraph.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 36c25c21457..f14e1e8fe10 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -3371,9 +3371,13 @@ def strong_articulation_points(self): """ # The method is applied on each strongly connected component if self.is_strongly_connected(): - L = [self] + # Make a mutable copy of self + L = [ DiGraph( [(u, v) for u, v in self.edge_iterator(labels=0) if u != v], + data_structure='sparse', immutable=False) ] else: - L = self.strongly_connected_components_subgraphs() + # Get the list of strongly connected components of self as mutable + # subgraphs + L = [ self.subgraph(scc, immutable=False) for scc in self.strongly_connected_components() ] SAP = [] for g in L: From 08a35a9824ddde635d742c7e57797618c5eaa5a9 Mon Sep 17 00:00:00 2001 From: David Coudert Date: Mon, 12 Jun 2017 17:49:21 +0200 Subject: [PATCH 035/184] trac #23210: add reverse option to immediate_dominators --- src/sage/graphs/digraph.py | 47 ++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index f14e1e8fe10..615663e863c 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -3187,7 +3187,7 @@ def is_strongly_connected(self): return len(self.strongly_connected_components()) == 1 - def immediate_dominators(self, r): + def immediate_dominators(self, r, reverse=False): r""" Return the immediate dominators of all vertices reachable from `r`. @@ -3212,6 +3212,11 @@ def immediate_dominators(self, r): - ``r`` -- a vertex of the digraph, the root of the immediate dominators tree + - ``reverse`` -- boolean (default: ``False``); When set to ``True``, we + consider the reversed digraph in which out-neighbors become the + in-neighbors and vice-versa. This option is available only if the + backend of the digraph is :mod:`~SparseGraphBackend`. + OUTPUT: The (immediate) dominator tree rooted at `r`, encoded as a predecessor dictionary. @@ -3250,6 +3255,17 @@ def immediate_dominators(self, r): sage: P.immediate_dominators(3) {3: 3, 4: 3} + Immediate dominators in the reverse digraph:: + + sage: D = digraphs.Complete(5)+digraphs.Complete(4) + sage: D.add_edges([(0, 5), (1, 6), (7, 2)]) + sage: idom = D.immediate_dominators(0, reverse=True) + sage: idom + {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 7, 6: 7, 7: 2, 8: 7} + sage: D_reverse = D.reverse() + sage: D_reverse.immediate_dominators(0) == idom + True + TESTS: When `r` is not in the digraph:: @@ -3259,6 +3275,15 @@ def immediate_dominators(self, r): ... ValueError: the given root must be in the digraph + The reverse option is available only when the backend of the digraph is + :mod:`~SparseGraphBackend`:: + + sage: H = DiGraph(D.edges(), data_structure='static_sparse') + sage: H.immediate_dominators(0, reverse=True) + Traceback (most recent call last): + ... + ValueError: the reverse option is not available for this digraph + Comparison with the NetworkX method:: sage: import networkx @@ -3272,10 +3297,19 @@ def immediate_dominators(self, r): raise ValueError("the given root must be in the digraph") idom = {r: r} - n = self.order() - pre_order = list(self.depth_first_search(r)) - number = {u: n-i for i, u in enumerate(pre_order)} + if reverse: + from sage.graphs.base.sparse_graph import SparseGraphBackend + if isinstance(self._backend, SparseGraphBackend): + pre_order = list(self._backend.depth_first_search(r, reverse=True)) + number = {u: n-i for i, u in enumerate(pre_order)} + neighbor_iterator = self.neighbor_out_iterator + else: + raise ValueError("the reverse option is not available for this digraph") + else: + pre_order = list(self.depth_first_search(r)) + number = {u: n-i for i, u in enumerate(pre_order)} + neighbor_iterator = self.neighbor_in_iterator pre_order.pop(0) def intersect(u, v): @@ -3290,7 +3324,7 @@ def intersect(u, v): while changed: changed = False for u in pre_order: - pred = [v for v in self.neighbor_in_iterator(u) if v in idom] + pred = [v for v in neighbor_iterator(u) if v in idom] if not pred: continue else: @@ -3402,8 +3436,7 @@ def strong_articulation_points(self): # 3. Compute the set of non-trivial immediate dominators in the # reverse digraph - g_reverse = g.reverse() - DRr = set( g_reverse.immediate_dominators(r).values() ) + DRr = set( g.immediate_dominators(r, reverse=True).values() ) # 4. Store D(r) + DR(r) - r SAP.extend( Dr.union(DRr).difference([r]) ) From 0b6456b4ec712edc3466ad7be12462a789b4dcc4 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 14 Jun 2017 05:20:59 +0000 Subject: [PATCH 036/184] Expand to allow changing many things with the change function on p-adic rings/fields --- src/sage/rings/padics/local_generic.py | 124 ++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index af422b4e515..b39fab53635 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -20,6 +20,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from copy import copy from sage.rings.ring import CommutativeRing from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationRings, CompleteDiscreteValuationFields from sage.structure.category_object import check_default_category @@ -165,13 +166,128 @@ def _latex_(self): """ return self._repr_(do_latex = True) - def change_precision(self, prec): - (functor, ring) = self.construction() + def change(self, **kwds): + """ + Return a new ring with changed attributes. + + INPUT: + + Keyword arguments are passed on to the :mod:`constructors ` + via the :meth:`construction` functor. + + NOTES: + + For extension rings, some keywords take effect only on the extension + (``names``, ``var_name``, ``res_name``, ``unram_name``, ``ram_name``) + and others on both the extension and, recursively, the base ring + (``print_mode``, ``halt``, ``print_pos``, ``print_sep``, + ``print_alphabet``, ``print_max_ram_terms``, ``check``). + + If the precision is increased on an extension ring, + the precision on the base is increased as necessary. + If the precision is decreased, the precision of the base is unchanged. + + EXAMPLES: + + We can use this method to change the precision:: + + sage: Zp(5).change(prec=40) + 5-adic Ring with capped relative precision 40 + + or the precision type:: + + sage: Zp(5).change(type="capped-abs") + 5-adic Ring with capped absolute precision 20 + + or even the prime:: + + sage: ZpCA(3).change(p=17) + 17-adic Ring with capped absolute precision 20 + + You can also change print modes:: + + sage: R = Zp(5).change(prec=5, print_mode='digits') + sage: ~R(17) + ...13403 + + You can change extensions:: + + sage: K. = QqFP(125, prec=4) + sage: K.change(q=64) + Unramified Extension of 2-adic Field with floating precision 4 in a defined by x^6 + x^4 + x^3 + x + 1 + sage: R. = QQ[] + sage: K.change(modulus = x^2 - x + 2) + Unramified Extension of 5-adic Field with floating precision 4 in a defined by x^2 - x + 2 + + and variable names:: + + sage: K.change(names='b') + Unramified Extension of 5-adic Field with floating precision 4 in b defined by x^3 + 3*x + 3 + + and precision:: + + sage: Kup = K.change(prec=8); Kup + Unramified Extension of 5-adic Field with floating precision 8 in a defined by x^3 + 3*x + 3 + sage: Kup.base_ring() + 5-adic Field with floating precision 8 + + If you decrease the precision, the precision of the base stays the same:: + + sage: Kdown = K.change(prec=2); Kdown + Unramified Extension of 5-adic Field with floating precision 2 in a defined by x^3 + 3*x + 3 + sage: Kdown.base_ring() + 5-adic Field with floating precision 4 + """ + functor, ring = self.construction() + functor = copy(functor) + # There are two kinds of functors possible: + # CompletionFunctor and AlgebraicExtensionFunctor + # We distinguish them by the presence of ``prec``, if hasattr(functor, "prec"): - functor.prec = prec + functor.extras = copy(functor.extras) + if 'type' in kwds and kwds['type'] not in functor._dvr_types: + raise ValueError("completion type must be one of %s"%(", ".join(functor._dvr_types[1:]))) + for atr in ('p', 'prec', 'type'): + if atr in kwds: + setattr(functor, atr, kwds.pop(atr)) + for atr in ('print_mode', 'halt', 'names', 'ram_name', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'check'): + if atr in kwds: + functor.extras[atr] = kwds.pop(atr) + if kwds: + raise ValueError("Extra arguments received: %s"%(", ".join(kwds.keys()))) else: + functor.kwds = copy(functor.kwds) + if 'prec' in kwds: + prec = kwds.pop('prec') + baseprec = (prec - 1) // self.e() + 1 + if baseprec > self.base_ring().precision_cap(): + kwds['prec'] = baseprec + functor.kwds['prec'] = prec + from sage.rings.padics.padic_base_generic import pAdicBaseGeneric + n = None + if 'q' in kwds and isinstance(ring, pAdicBaseGeneric): + q = kwds.pop('q') + if not isinstance(q, Integer): + raise TypeError("q must be an integer") + p, n = q.is_prime_power(get_data=True) + if n == 0: + raise ValueError("q must be a prime power") + if 'p' in kwds and kwds['p'] != p: + raise ValueError("q does not match p") + kwds['p'] = p + if 'modulus' in kwds: + modulus = kwds.pop('modulus') + if n is not None and modulus.degree() != n: + raise ValueError("modulus must have degree matching q") + functor.polys = [modulus] + elif n is not None: + functor.polys = [n] + for atr in ('names', 'var_name', 'res_name', 'unram_name', 'ram_name'): + functor.kwds[atr] = kwds.pop(atr) + for atr in ('print_mode', 'halt', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'check'): + functor.kwds[atr] = kwds[atr] try: - ring = ring.change_precision(prec) + ring = ring.change(**kwds) except AttributeError: raise NotImplementedError return functor(ring) From e16f79fb103f7030f167b806d63bf222da88da92 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 14 Jun 2017 06:38:42 +0000 Subject: [PATCH 037/184] Add construction method for extensions of p-adic rings and fields, switch implementation of fraction_field and integer_ring --- src/sage/rings/padics/local_generic.py | 11 ++++++ .../rings/padics/padic_extension_generic.py | 34 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index b39fab53635..c533511ea6b 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -204,6 +204,11 @@ def change(self, **kwds): sage: ZpCA(3).change(p=17) 17-adic Ring with capped absolute precision 20 + You can switch between the ring of integers and its fraction field:: + + sage: ZpCA(3).change(field=True) + 3-adic Field with capped relative precision 20 + You can also change print modes:: sage: R = Zp(5).change(prec=5, print_mode='digits') @@ -247,6 +252,12 @@ def change(self, **kwds): functor.extras = copy(functor.extras) if 'type' in kwds and kwds['type'] not in functor._dvr_types: raise ValueError("completion type must be one of %s"%(", ".join(functor._dvr_types[1:]))) + if 'field' in kwds: + field = kwds.pop('field') + if field: + ring = ring.fraction_field() + else: + ring = ring.ring_of_integers() for atr in ('p', 'prec', 'type'): if atr in kwds: setattr(functor, atr, kwds.pop(atr)) diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index d1c36d69e6e..75b41c6587a 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -243,6 +243,32 @@ def polynomial_ring(self): # xnew = x - x*delta*(1-q*delta) # return x + def construction(self): + """ + Returns the functorial construction of this ring, namely, + the algebraic extension of the base ring defined by the given + polynomial. + + Also preserves other information that makes this ring unique + (e.g. precision, rounding, print mode). + + EXAMPLES:: + + sage: R. = Zq(25, 8, print_mode='val-unit') + sage: c, R0 = R.construction(); R0 + 5-adic Ring with capped relative precision 8 + sage: c(R0) + Unramified Extension of 5-adic Ring with capped relative precision 8 in a defined by (1 + O(5^8))*x^2 + (4 + O(5^8))*x + (2 + O(5^8)) + sage: c(R0) == R + True + """ + from sage.categories.pushout import AlgebraicExtensionFunctor as AEF + print_mode = self._printer.dict() + return (AEF([self._given_poly], [self.variable_name()], + prec=self.precision_cap(), print_mode=self._printer.dict(), + implementation=self._implementation), + self.base_ring()) + def fraction_field(self, print_mode=None): r""" Returns the fraction field of this extension, which is just @@ -268,6 +294,10 @@ def fraction_field(self, print_mode=None): """ if self.is_field() and print_mode is None: return self + if print_mode is None: + return self.change(field=True) + else: + return self.change(field=True, print_mode=print_mode) print_mode = self._modified_print_mode(print_mode) ground_mode = print_mode.copy() # We don't want to confuse the ground ring with different names. @@ -307,6 +337,10 @@ def integer_ring(self, print_mode=None): #Currently does not support fields with non integral defining polynomials. This should change when the padic_general_extension framework gets worked out. if not self.is_field() and print_mode is None: return self + if print_mode is None: + return self.change(field=False) + else: + return self.change(field=False, print_mode=print_mode) print_mode = self._modified_print_mode(print_mode) ground_mode = print_mode.copy() # We don't want to confuse the ground ring with different names. From a88b6307a84ccbb67cdc9ad6ab447b6a00c9f8c1 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 14 Jun 2017 07:11:04 +0000 Subject: [PATCH 038/184] Fixing problems revealed by tests --- src/sage/rings/padics/local_generic.py | 15 +++++++++++---- src/sage/rings/padics/padic_extension_generic.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index c533511ea6b..bac3e226e74 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -212,8 +212,8 @@ def change(self, **kwds): You can also change print modes:: sage: R = Zp(5).change(prec=5, print_mode='digits') - sage: ~R(17) - ...13403 + sage: repr(~R(17)) + '...13403' You can change extensions:: @@ -256,6 +256,11 @@ def change(self, **kwds): field = kwds.pop('field') if field: ring = ring.fraction_field() + if 'type' not in kwds: + if self._prec_type() == 'capped-abs': + kwds['type'] = 'capped-rel' + elif self._prec_type() == 'fixed-mod': + raise TypeError('You must specify the type explicitly') else: ring = ring.ring_of_integers() for atr in ('p', 'prec', 'type'): @@ -294,9 +299,11 @@ def change(self, **kwds): elif n is not None: functor.polys = [n] for atr in ('names', 'var_name', 'res_name', 'unram_name', 'ram_name'): - functor.kwds[atr] = kwds.pop(atr) + if atr in kwds: + functor.kwds[atr] = kwds.pop(atr) for atr in ('print_mode', 'halt', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'check'): - functor.kwds[atr] = kwds[atr] + if atr in kwds: + functor.kwds[atr] = kwds[atr] try: ring = ring.change(**kwds) except AttributeError: diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index 75b41c6587a..45f8ddf9fe3 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -264,7 +264,7 @@ def construction(self): """ from sage.categories.pushout import AlgebraicExtensionFunctor as AEF print_mode = self._printer.dict() - return (AEF([self._given_poly], [self.variable_name()], + return (AEF([self._pre_poly], [self.variable_name()], prec=self.precision_cap(), print_mode=self._printer.dict(), implementation=self._implementation), self.base_ring()) From 53d24469801c5ccea6b13b59a30ecd11eafa4d29 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 14 Jun 2017 08:58:06 +0000 Subject: [PATCH 039/184] Added ability to edit show_prec to p-adic factory --- src/sage/categories/pushout.py | 10 +- src/sage/rings/padics/factory.py | 303 +++++++++++++++---------- src/sage/rings/padics/local_generic.py | 4 +- 3 files changed, 190 insertions(+), 127 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 6b84bcd8e8e..f70f21363f6 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -2234,7 +2234,7 @@ def __init__(self, p, prec, extras=None): raise ValueError("completion type must be one of %s"%(", ".join(self._real_types))) else: if self.type not in self._dvr_types: - raise ValueError("completion type must be one of %s"%(", ".join(self._dvr_types))) + raise ValueError("completion type must be one of %s"%(", ".join(self._dvr_types[1:]))) def _repr_(self): """ @@ -2314,7 +2314,7 @@ def __cmp__(self, other): return c _real_types = ['Interval','Ball','MPFR','RDF','RLF'] - _dvr_types = [None, 'fixed-mod','capped-abs','capped-rel','lazy'] + _dvr_types = [None, 'fixed-mod','floating-point','capped-abs','capped-rel','lazy'] def merge(self, other): """ @@ -2398,9 +2398,9 @@ def merge(self, other): return CompletionFunctor(self.p, new_prec, {'type':new_type, 'sci_not':new_scinot, 'rnd':new_rnd}) else: new_type = self._dvr_types[min(self._dvr_types.index(self.type), self._dvr_types.index(other.type))] - if new_type == 'fixed-mod': - if self.type != 'fixed-mod' or other.type != 'fixed-mod': - return None # no coercion into fixed-mod + if new_type in ('fixed-mod', 'floating-point'): + if self.type != other.type: + return None # no coercion into fixed-mod or floating-point new_prec = min(self.prec, other.prec) else: new_prec = max(self.prec, other.prec) # since elements track their own precision, we don't want to truncate them diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 41fc26b10c3..493964dc9f9 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -69,8 +69,7 @@ ext_table['u', pAdicFieldFloatingPoint] = UnramifiedExtensionFieldFloatingPoint #ext_table['u', pAdicRingLazy] = UnramifiedExtensionRingLazy - -def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, check, valid_non_lazy_types): +def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, valid_non_lazy_types): """ This implements create_key for Zp and Qp: moving it here prevents code duplication. @@ -79,10 +78,10 @@ def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, pr EXAMPLES:: sage: from sage.rings.padics.factory import get_key_base - sage: get_key_base(11, 5, 'capped-rel', None, 0, None, None, None, ':', None, None, True, ['capped-rel']) - (11, 5, 'capped-rel', 'series', '11', True, '|', (), -1) - sage: get_key_base(12, 5, 'capped-rel', 'digits', 0, None, None, None, None, None, None, False, ['capped-rel']) - (12, 5, 'capped-rel', 'digits', '12', True, '|', ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'), -1) + sage: get_key_base(11, 5, 'capped-rel', None, 0, None, None, None, ':', None, None, False, True, ['capped-rel']) + (11, 5, 'capped-rel', 'series', '11', True, '|', (), -1, False) + sage: get_key_base(12, 5, 'capped-rel', 'digits', 0, None, None, None, None, None, None, True, False, ['capped-rel']) + (12, 5, 'capped-rel', 'digits', '12', True, '|', ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'), -1, True) """ if check: if not isinstance(p, Integer): @@ -157,9 +156,9 @@ def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, pr else: name = str(names) if type in valid_non_lazy_types: - key = (p, prec, type, print_mode, name, print_pos, print_sep, tuple(print_alphabet), print_max_terms) + key = (p, prec, type, print_mode, name, print_pos, print_sep, tuple(print_alphabet), print_max_terms, show_prec) elif type == 'lazy': - key = (p, prec, halt, print_mode, name, print_pos, print_sep, tuple(print_alphabet), print_max_terms) + key = (p, prec, halt, print_mode, name, print_pos, print_sep, tuple(print_alphabet), print_max_terms, show_prec) else: print(type) raise ValueError("type must be %s or lazy"%(", ".join(valid_non_lazy_types))) @@ -217,6 +216,9 @@ class Qp_class(UniqueFactory): - ``print_max_terms`` -- integer (default ``None``) The maximum number of terms shown. See PRINTING below. + - ``show_prec`` -- bool (default ``None``) Whether to show the precision + for elements. See PRINTING below. + - ``check`` -- bool (default ``True``) whether to check if `p` is prime. Non-prime input may cause seg-faults (but can also be useful for base n expansions for example) @@ -303,6 +305,11 @@ class Qp_class(UniqueFactory): sage: U.

= Qp(5); p p + O(p^21) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: Qp(5, show_prec=False)(6) + 1 + 5 + *print_sep* and *print_alphabet* have no effect in series mode. Note that print options affect equality:: @@ -328,6 +335,11 @@ class Qp_class(UniqueFactory): sage: T = Qp(5, print_mode='val-unit', names='pi'); a = T(70700); a pi^2 * 2828 + O(pi^22) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: Qp(5, print_mode='val-unit', show_prec=False)(30) + 5 * 6 + *print_max_terms*, *print_sep* and *print_alphabet* have no effect. Equality again depends on the printing options:: @@ -365,6 +377,11 @@ class Qp_class(UniqueFactory): sage: d = T(-707/5^10); d 95367431639918/unif^10 + O(unif^10) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: Qp(5, print_mode='terse', show_prec=False)(6) + 6 + *print_max_terms*, *print_sep* and *print_alphabet* have no effect. Equality depends on printing options:: @@ -413,6 +430,11 @@ class Qp_class(UniqueFactory): sage: T = Qp(5, print_mode='digits', print_max_terms=4, print_alphabet=('1','2','3','4','5')); b = T(-70700); repr(b) '...325311' + *show_prec* determines whether the precision is printed (default ``False``):: + + sage: repr(Zp(5, print_mode='digits', show_prec=True)(6)) + '...11 + O(5^20)' + *print_pos*, *name* and *print_sep* have no effect. Equality depends on printing options:: @@ -457,6 +479,11 @@ class Qp_class(UniqueFactory): sage: U = Qp(5, print_mode='bars', print_sep=']['); a = U(70700); repr(a) '...4][2][3][0][3][0][0' + *show_prec* determines whether the precision is printed (default ``False``):: + + sage: repr(Zp(5, print_mode='bars', show_prec=True)(6)) + '...1|1 + O(5^20)' + *name* and *print_alphabet* have no effect. Equality depends on printing options:: @@ -471,7 +498,7 @@ class Qp_class(UniqueFactory): """ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = None, halt = DEFAULT_HALT, names = None, ram_name = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_terms = None, check = True): + print_sep = None, print_alphabet = None, print_max_terms = None, show_prec=None, check = True): """ Creates a key from input parameters for ``Qp``. @@ -480,9 +507,9 @@ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = N TESTS:: sage: Qp.create_key(5,40) - (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1) + (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, None) """ - return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, check, ['capped-rel', 'floating-point']) + return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, ['capped-rel', 'floating-point']) def create_object(self, version, key): """ @@ -498,8 +525,11 @@ def create_object(self, version, key): if version[0] < 3 or (version[0] == 3 and version[1] < 2) or (version[0] == 3 and version[1] == 2 and version[2] < 3): p, prec, type, print_mode, name = key print_pos, print_sep, print_alphabet, print_max_terms = None, None, None, None - else: + elif version[0] < 8: p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms = key + show_prec = None + else: + p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec = key if isinstance(type, Integer): # lazy raise NotImplementedError("lazy p-adics need more work. Sorry.") @@ -508,29 +538,31 @@ def create_object(self, version, key): # keys changed in order to reduce irrelevant duplications: e.g. two Qps with print_mode 'series' # that are identical except for different 'print_alphabet' now return the same object. key = get_key_base(p, prec, type, print_mode, 0, name, None, print_pos, print_sep, print_alphabet, - print_max_terms, False, ['capped-rel', 'fixed-mod', 'capped-abs']) + print_max_terms, None, False, ['capped-rel', 'fixed-mod', 'capped-abs']) try: obj = self._cache[version, key]() if obj is not None: return obj except KeyError: pass - p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms = key + p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec = key if type == 'capped-rel': if print_mode == 'terse': return pAdicFieldCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'ram_name': name, 'max_terse_terms': print_max_terms}, name) + 'ram_name': name, 'max_terse_terms': print_max_terms, 'show_prec': show_prec}, name) else: return pAdicFieldCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) elif type == 'floating-point': + if show_prec is None: + show_prec = False if print_mode == 'terse': return pAdicFieldFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'ram_name': name, 'max_terse_terms': print_max_terms, 'show_prec': False}, name) + 'ram_name': name, 'max_terse_terms': print_max_terms, 'show_prec': show_prec}, name) else: return pAdicFieldFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': False}, name) + 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) else: raise ValueError("unexpected type") @@ -544,7 +576,7 @@ def create_object(self, version, key): def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, res_name = None, print_pos = None, print_sep = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): + print_max_unram_terms = None, print_max_terse_terms = None, show_prec=None, check = True, implementation = 'FLINT'): """ Given a prime power `q = p^n`, return the unique unramified extension of `\mathbb{Q}_p` of degree `n`. @@ -602,6 +634,9 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, number of terms in the polynomial representation of an element (using ``'terse'``). See PRINTING below. + - ``show_prec`` -- bool (default ``None``) Whether to show the precision + for elements. See PRINTING below. + - ``check`` -- bool (default ``True``) whether to check inputs. OUTPUT: @@ -771,6 +806,11 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: V. = Qq(128, prec = 8, print_mode='series', print_max_unram_terms = 0); repr((1+f)^9 - 1 - f^3) '(...)*2 + (...)*2^2 + (...)*2^3 + (...)*2^4 + (...)*2^5 + (...)*2^6 + (...)*2^7 + O(2^8)' + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: U. = Qq(9, 2, show_prec=False); repr(-3*(1+2*e)^4) + '3 + e*3^2' + *print_sep* and *print_max_terse_terms* have no effect. Note that print options affect equality:: @@ -809,6 +849,11 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: b*17*(a^3-a+14) 1 + O(17^6) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: U. = Qq(9, 2, print_mode='val-unit', show_prec=False); repr(-3*(1+2*e)^4) + '3 * (1 + 3*e)' + *print_sep*, *print_max_ram_terms* and *print_max_unram_terms* have no effect. @@ -854,6 +899,11 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: U. = Qq(625, print_mode='terse', print_max_terse_terms=2); (a-1/5)^6 106251/5^6 + 49994/5^5*a + ... + O(5^14) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: U. = Qq(9, 2, print_mode='terse', show_prec=False); repr(-3*(1+2*e)^4) + '3 + 9*e' + *print_sep*, *print_max_ram_terms* and *print_max_unram_terms* have no effect. @@ -942,6 +992,11 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: with local_print_mode(U, {'max_unram_terms':0}): repr(b-75*a) '...[...][...][...][...][][...][...]' + *show_prec* determines whether the precision is printed (default ``False``):: + + sage: U. = Qq(9, 2, print_mode='bars', show_prec=True); repr(-3*(1+2*e)^4) + '...[0, 1]|[1]|[] + O(3^3)' + *ram_name* and *print_max_terse_terms* have no effect. Equality depends on printing options:: @@ -1047,8 +1102,7 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, # Short constructor names for different types ###################################################### -def QpCR(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_terms = None, check=True): +def QpCR(p, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create capped relative `p`-adic fields. @@ -1060,12 +1114,9 @@ def QpCR(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = sage: QpCR(5, 40) 5-adic Field with capped relative precision 40 """ - return Qp(p=p, prec=prec, print_mode=print_mode, halt=halt, check=check, names=names, - print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, - type = 'capped-rel') + return Qp(p, prec, 'capped-rel', *args, **kwds) -def QpFP(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_terms = None, check=True): +def QpFP(p, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create floating point `p`-adic fields. @@ -1077,9 +1128,7 @@ def QpFP(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = sage: QpFP(5, 40) 5-adic Field with floating precision 40 """ - return Qp(p=p, prec=prec, print_mode=print_mode, halt=halt, check=check, names=names, - print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, - type = 'floating-point') + return Qp(p, prec, 'floating-point', *args, **kwds) #def QpL(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_terms = None, check=True): @@ -1096,10 +1145,7 @@ def QpFP(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = # type = 'lazy') -def QqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, - print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): +def QqCR(q, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create capped relative unramified `p`-adic fields. @@ -1112,15 +1158,9 @@ def QqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, sage: R. = QqCR(25, 40); R Unramified Extension of 5-adic Field with capped relative precision 40 in a defined by (1 + O(5^40))*x^2 + (4 + O(5^40))*x + (2 + O(5^40)) """ - return Qq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, - halt=halt, ram_name=ram_name, print_pos=print_pos, print_max_ram_terms=print_max_ram_terms, - print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, - check=check, implementation=implementation, type = 'capped-rel') - -def QqFP(q, prec = DEFAULT_PREC, modulus = None, names=None, - print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): + return Qq(q, prec, 'capped-rel', *args, **kwds) + +def QqFP(q, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create floating point unramified `p`-adic fields. @@ -1133,10 +1173,7 @@ def QqFP(q, prec = DEFAULT_PREC, modulus = None, names=None, sage: R. = QqFP(25, 40); R Unramified Extension of 5-adic Field with floating precision 40 in a defined by x^2 + 4*x + 2 """ - return Qq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, - halt=halt, ram_name=ram_name, print_pos=print_pos, print_max_ram_terms=print_max_ram_terms, - print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, - check=check, implementation=implementation, type = 'floating-point') + return Qq(q, prec, 'floating-point', *args, **kwds) #def QqL(q, prec = DEFAULT_PREC, modulus = None, names=None, # print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, @@ -1204,6 +1241,9 @@ class Zp_class(UniqueFactory): - ``print_max_terms`` -- integer (default ``None``) The maximum number of terms shown. See PRINTING below. + - ``show_prec`` -- bool (default ``None``) Whether to show the precision + for elements. See PRINTING below. + - ``check`` -- bool (default ``True``) whether to check if `p` is prime. Non-prime input may cause seg-faults (but can also be useful for base `n` expansions for example) @@ -1319,6 +1359,11 @@ class Zp_class(UniqueFactory): sage: U.

= Zp(5); p p + O(p^21) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: Zp(5, show_prec=False)(6) + 1 + 5 + *print_sep* and *print_alphabet* have no effect. Note that print options affect equality:: @@ -1344,6 +1389,11 @@ class Zp_class(UniqueFactory): sage: T = Zp(5, print_mode='val-unit', names='pi'); a = T(70700); a pi^2 * 2828 + O(pi^22) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: Zp(5, print_mode='val-unit', show_prec=False)(30) + 5 * 6 + *print_max_terms*, *print_sep* and *print_alphabet* have no effect. Equality again depends on the printing options:: @@ -1369,6 +1419,11 @@ class Zp_class(UniqueFactory): sage: T. = Zp(5, print_mode='terse'); c = T(-707); c 95367431639918 + O(unif^20) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: Zp(5, print_mode='terse', show_prec=False)(30) + 30 + *print_max_terms*, *print_sep* and *print_alphabet* have no effect. Equality depends on printing options:: @@ -1402,6 +1457,11 @@ class Zp_class(UniqueFactory): sage: T = Zp(5, print_mode='digits', print_max_terms=4, print_alphabet=('1','2','3','4','5')); b = T(-70700); repr(b) '...325311' + *show_prec* determines whether the precision is printed (default ``False``):: + + sage: repr(Zp(5, 2, print_mode='digits', show_prec=True)(6)) + '...11 + O(5^2)' + *print_pos*, *name* and *print_sep* have no effect. Equality depends on printing options:: @@ -1435,6 +1495,11 @@ class Zp_class(UniqueFactory): sage: U = Zp(5, print_mode='bars', print_sep=']['); a = U(70700); repr(a) '...4][2][3][0][3][0][0' + *show_prec* determines whether the precision is printed (default ``False``):: + + sage: repr(Zp(5, 2, print_mode='bars', show_prec=True)(6)) + '...11 + O(5^2)' + *name* and *print_alphabet* have no effect. Equality depends on printing options:: @@ -1521,7 +1586,7 @@ class Zp_class(UniqueFactory): """ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = None, halt = DEFAULT_HALT, names = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, - print_max_terms = None, check = True): + print_max_terms = None, show_prec = None, check = True): """ Creates a key from input parameters for ``Zp``. @@ -1530,12 +1595,12 @@ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = N TESTS:: sage: Zp.create_key(5,40) - (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1) + (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, None) sage: Zp.create_key(5,40,print_mode='digits') - (5, 40, 'capped-rel', 'digits', '5', True, '|', ('0', '1', '2', '3', '4'), -1) + (5, 40, 'capped-rel', 'digits', '5', True, '|', ('0', '1', '2', '3', '4'), -1, None) """ return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, - print_max_terms, check, ['capped-rel', 'fixed-mod', 'capped-abs', 'floating-point']) + print_max_terms, show_prec, check, ['capped-rel', 'fixed-mod', 'capped-abs', 'floating-point']) def create_object(self, version, key): """ @@ -1552,8 +1617,11 @@ def create_object(self, version, key): (len(version) > 2 and version[0] == 3 and version[1] == 2 and version[2] < 3)): p, prec, type, print_mode, name = key print_pos, print_sep, print_alphabet, print_max_terms = None, None, None, None - else: + elif version[0] < 8: p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms = key + show_prec = None + else: + p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec = key if isinstance(type, Integer): # lazy raise NotImplementedError("lazy p-adics need more work. Sorry.") @@ -1562,26 +1630,28 @@ def create_object(self, version, key): # keys changed in order to reduce irrelevant duplications: e.g. two Zps with print_mode 'series' # that are identical except for different 'print_alphabet' now return the same object. key = get_key_base(p, prec, type, print_mode, 0, name, None, print_pos, print_sep, print_alphabet, - print_max_terms, False, ['capped-rel', 'fixed-mod', 'capped-abs']) + print_max_terms, None, False, ['capped-rel', 'fixed-mod', 'capped-abs']) try: obj = self._cache[version, key]() if obj is not None: return obj except KeyError: pass - p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms = key + p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec = key if type == 'capped-rel': return pAdicRingCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) elif type == 'fixed-mod': return pAdicRingFixedMod(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) elif type == 'capped-abs': return pAdicRingCappedAbsolute(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) elif type == 'floating-point': + if show_prec is None: + show_prec = False return pAdicRingFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': False}, name) + 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) else: raise ValueError("unexpected type") @@ -1595,7 +1665,7 @@ def create_object(self, version, key): def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, res_name = None, print_pos = None, print_sep = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): + print_max_unram_terms = None, print_max_terse_terms = None, show_prec = None, check = True, implementation = 'FLINT'): """ Given a prime power `q = p^n`, return the unique unramified extension of `\mathbb{Z}_p` of degree `n`. @@ -1653,6 +1723,9 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, number of terms in the polynomial representation of an element (using ``'terse'``). See PRINTING below. + - ``show_prec`` -- bool (default ``None``) Whether to show the precision + for elements. See PRINTING below. + - ``check`` -- bool (default ``True``) whether to check inputs. - ``implementation`` -- string (default ``FLINT``) which @@ -1864,6 +1937,11 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: V. = Zq(128, prec = 8, print_mode='series', print_max_unram_terms = 0); repr((1+f)^9 - 1 - f^3) '(...)*2 + (...)*2^2 + (...)*2^3 + (...)*2^4 + (...)*2^5 + (...)*2^6 + (...)*2^7 + O(2^8)' + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: U. = Zq(9, 2, show_prec=False); repr(-3*(1+2*e)^4) + '3 + e*3^2' + *print_sep* and *print_max_terse_terms* have no effect. Note that print options affect equality:: @@ -1900,6 +1978,11 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: U. = Zq(17^4, 6, print_mode='val-unit', print_max_terse_terms=3); b = (17*(a^3-a+14)^6); b 17 * (12131797 + 12076378*a + 10809706*a^2 + ...) + O(17^7) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: U. = Zq(9, 2, show_prec=False); repr(-3*(1+2*e)^4) + '3 * (1 + 3*e)' + *print_sep*, *print_max_ram_terms* and *print_max_unram_terms* have no effect. Equality again depends on the printing options:: @@ -1948,6 +2031,11 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: U. = Zq(625, print_mode='terse', print_max_terse_terms=2); (a-1/5)^6 106251/5^6 + 49994/5^5*a + ... + O(5^14) + *show_prec* determines whether the precision is printed (default ``True``):: + + sage: U. = Zq(9, 2, print_mode='terse', show_prec=False); repr(-3*(1+2*e)^4) + '3 + 9*e' + *print_sep*, *print_max_ram_terms* and *print_max_unram_terms* have no effect. @@ -2021,6 +2109,11 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: with local_print_mode(U, {'max_unram_terms':0}): repr(b-75*a) '...[...][...][...][...][][...][...]' + *show_prec* determines whether the precision is printed (default ``False``):: + + sage: U. = Zq(9, 2, print_mode='bars', show_prec=True); repr(-3*(1+2*e)^4) + '...[0, 1]|[1]|[] + O(3^2)' + *ram_name* and *print_max_terse_terms* have no effect. Equality depends on printing options:: @@ -2122,8 +2215,7 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, # Short constructor names for different types ###################################################### -def ZpCR(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_terms = None, check=True): +def ZpCR(p, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create capped relative `p`-adic rings. @@ -2135,12 +2227,9 @@ def ZpCR(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = sage: ZpCR(5, 40) 5-adic Ring with capped relative precision 40 """ - return Zp(p=p, prec=prec, print_mode=print_mode, halt=halt, check=check, names=names, - print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, - type = 'capped-rel') + return Zp(p, prec, 'capped-rel', *args, **kwds) -def ZpCA(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_terms = None, check=True): +def ZpCA(p, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create capped absolute `p`-adic rings. @@ -2151,12 +2240,9 @@ def ZpCA(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = sage: ZpCA(5, 40) 5-adic Ring with capped absolute precision 40 """ - return Zp(p=p, prec=prec, print_mode=print_mode, halt=halt, check=check, names=names, - print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, - type = 'capped-abs') + return Zp(p, prec, 'capped-abs', *args, **kwds) -def ZpFM(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_terms = None, check=True): +def ZpFM(p, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create fixed modulus `p`-adic rings. @@ -2167,12 +2253,9 @@ def ZpFM(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = sage: ZpFM(5, 40) 5-adic Ring of fixed modulus 5^40 """ - return Zp(p=p, prec=prec, print_mode=print_mode, halt=halt, check=check, names=names, - print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, - type = 'fixed-mod') + return Zp(p, prec, 'fixed-mod', *args, **kwds) -def ZpFP(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_terms = None, check=True): +def ZpFP(p, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create floating point `p`-adic rings. @@ -2184,9 +2267,7 @@ def ZpFP(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = sage: ZpFP(5, 40) 5-adic Ring with floating precision 40 """ - return Zp(p=p, prec=prec, print_mode=print_mode, halt=halt, check=check, names=names, - print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, - type = 'floating-point') + return Zp(p, prec, 'floating-point', *args, **kwds) #def ZpL(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_terms = None, check=True): @@ -2202,10 +2283,7 @@ def ZpFP(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = # print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, # type = 'lazy') -def ZqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, - print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): +def ZqCR(q, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create capped relative unramified `p`-adic rings. @@ -2217,15 +2295,9 @@ def ZqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, sage: R. = ZqCR(25, 40); R Unramified Extension of 5-adic Ring with capped relative precision 40 in a defined by (1 + O(5^40))*x^2 + (4 + O(5^40))*x + (2 + O(5^40)) """ - return Zq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, - halt=halt, ram_name=ram_name, print_pos=print_pos, print_max_ram_terms=print_max_ram_terms, - print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, - check=check, implementation=implementation, type = 'capped-rel') - -def ZqCA(q, prec = DEFAULT_PREC, modulus = None, names=None, - print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation='FLINT'): + return Zq(q, prec, 'capped-rel', *args, **kwds) + +def ZqCA(q, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create capped absolute unramified `p`-adic rings. @@ -2236,15 +2308,9 @@ def ZqCA(q, prec = DEFAULT_PREC, modulus = None, names=None, sage: R. = ZqCA(25, 40); R Unramified Extension of 5-adic Ring with capped absolute precision 40 in a defined by (1 + O(5^40))*x^2 + (4 + O(5^40))*x + (2 + O(5^40)) """ - return Zq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, - halt=halt, ram_name=ram_name, print_pos=print_pos, print_max_ram_terms=print_max_ram_terms, - print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, - check=check, implementation=implementation, type = 'capped-abs') - -def ZqFM(q, prec = DEFAULT_PREC, modulus = None, names=None, - print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation='FLINT'): + return Zq(q, prec, 'capped-abs', *args, **kwds) + +def ZqFM(q, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create fixed modulus unramified `p`-adic rings. @@ -2255,15 +2321,9 @@ def ZqFM(q, prec = DEFAULT_PREC, modulus = None, names=None, sage: R. = ZqFM(25, 40); R Unramified Extension of 5-adic Ring of fixed modulus 5^40 in a defined by (1 + O(5^40))*x^2 + (4 + O(5^40))*x + (2 + O(5^40)) """ - return Zq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, - halt=halt, ram_name=ram_name, print_pos=print_pos, print_max_ram_terms=print_max_ram_terms, - print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, - check=check, implementation=implementation, type = 'fixed-mod') - -def ZqFP(q, prec = DEFAULT_PREC, modulus = None, names=None, - print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, - print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): + return Zq(q, prec, 'fixed-mod', *args, **kwds) + +def ZqFP(q, prec = DEFAULT_PREC, *args, **kwds): """ A shortcut function to create floating point unramified `p`-adic rings. @@ -2275,10 +2335,7 @@ def ZqFP(q, prec = DEFAULT_PREC, modulus = None, names=None, sage: R. = ZqFP(25, 40); R Unramified Extension of 5-adic Ring with floating precision 40 in a defined by x^2 + 4*x + 2 """ - return Zq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, - halt=halt, ram_name=ram_name, print_pos=print_pos, print_max_ram_terms=print_max_ram_terms, - print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, - check=check, implementation=implementation, type = 'floating-point') + return Zq(q, prec, 'floating-point', *args, **kwds) #def ZqL(q, prec = DEFAULT_PREC, modulus = None, names=None, # print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, @@ -2322,7 +2379,7 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = unram_name = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_ram_terms = None, print_max_unram_terms = None, print_max_terse_terms = None, - check = True, unram = False, implementation='FLINT'): + show_prec = None, check = True, unram = False, implementation='FLINT'): """ Creates a key from input parameters for pAdicExtension. @@ -2333,7 +2390,7 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = sage: R = Zp(5,3) sage: S. = ZZ[] sage: pAdicExtension.create_key_and_extra_args(R, x^4-15,names='w') - (('e', 5-adic Ring with capped relative precision 3, x^4 - 15, (1 + O(5^3))*x^4 + (O(5^4))*x^3 + (O(5^4))*x^2 + (O(5^4))*x + (2*5 + 4*5^2 + 4*5^3 + O(5^4)), ('w', None, None, 'w'), 12, None, 'series', True, '|', (), -1, -1, -1, 'NTL'), {'shift_seed': (3 + O(5^3))}) + (('e', 5-adic Ring with capped relative precision 3, x^4 - 15, (1 + O(5^3))*x^4 + (O(5^4))*x^3 + (O(5^4))*x^2 + (O(5^4))*x + (2*5 + 4*5^2 + 4*5^3 + O(5^4)), ('w', None, None, 'w'), 12, None, 'series', True, '|', (), -1, -1, -1, None, 'NTL'), {'shift_seed': (3 + O(5^3))}) """ if print_mode is None: print_mode = base.print_mode() @@ -2477,12 +2534,12 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = implementation = "NTL" # for testing - FLINT ramified extensions not implemented yet key = (polytype, base, premodulus, modulus, names, prec, halt, print_mode, print_pos, print_sep, tuple(print_alphabet), print_max_ram_terms, print_max_unram_terms, - print_max_terse_terms, implementation) + print_max_terse_terms, show_prec, implementation) else: upoly, epoly, prec = split(modulus, prec) key = (polytype, base, premodulus, upoly, epoly, names, prec, halt, print_mode, print_pos, print_sep, tuple(print_alphabet), print_max_ram_terms, print_max_unram_terms, - print_max_terse_terms, implementation) + print_max_terse_terms, show_prec, implementation) return key, {'shift_seed': shift_seed} def create_object(self, version, key, shift_seed): @@ -2502,10 +2559,14 @@ def create_object(self, version, key, shift_seed): if version[0] < 6 or version[0] == 6 and version[1] < 1: key = list(key) key.append('NTL') + if version[0] < 8: + key = list(key) + key[-1:-1] = [None] if polytype == 'u' or polytype == 'e': (polytype, base, premodulus, modulus, names, prec, halt, print_mode, print_pos, print_sep, - print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms, implementation) = key - show_prec = base._printer._show_prec() + print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms, show_prec, implementation) = key + if show_prec is None: + show_prec = base._printer._show_prec() T = ext_table[polytype, type(base.ground_ring_of_tower()).__base__] return T(premodulus, modulus, prec, halt, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, @@ -2513,8 +2574,10 @@ def create_object(self, version, key, shift_seed): shift_seed, names, implementation) elif polytype == 'p': (polytype, base, premodulus, upoly, epoly, names, prec, halt, print_mode, print_pos, print_sep, - print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms, implementation) = key + print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms, show_prec, implementation) = key precmult = epoly.degree() + if show_prec is None: + show_prec = base._printer._show_prec() T = ext_table['p', type(base.ground_ring_of_tower()).__base__] return T(premodulus, upoly, epoly, prec*precmult, halt, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 44f526d7329..8504efc426a 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -291,7 +291,7 @@ def change(self, **kwds): for atr in ('p', 'prec', 'type'): if atr in kwds: setattr(functor, atr, kwds.pop(atr)) - for atr in ('print_mode', 'halt', 'names', 'ram_name', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'check'): + for atr in ('print_mode', 'halt', 'names', 'ram_name', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'show_prec', 'check'): if atr in kwds: functor.extras[atr] = kwds.pop(atr) if kwds: @@ -326,7 +326,7 @@ def change(self, **kwds): for atr in ('names', 'var_name', 'res_name', 'unram_name', 'ram_name'): if atr in kwds: functor.kwds[atr] = kwds.pop(atr) - for atr in ('print_mode', 'halt', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'check'): + for atr in ('print_mode', 'halt', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'show_prec', 'check'): if atr in kwds: functor.kwds[atr] = kwds[atr] try: From 55c335efa39a5f9a5b9808a5f86a4017702de522 Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 15 Jun 2017 07:38:55 +0000 Subject: [PATCH 040/184] Fixing doctest problems in the new change method for p-adic rings/fields --- src/sage/rings/padics/factory.py | 17 +++++++++-------- src/sage/rings/padics/local_generic.py | 15 +++++++++++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 493964dc9f9..c6eb9ab80d0 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -1071,7 +1071,7 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, if not isinstance(halt, Integer): halt = Integer(halt) base = Qp(p=p, prec=prec, type=type, print_mode=print_mode, halt=halt, names=ram_name, print_pos=print_pos, - print_sep=print_sep, print_max_terms=print_max_ram_terms, check=check) + print_sep=print_sep, print_max_terms=print_max_ram_terms, show_prec=show_prec, check=check) if k == 1: return base @@ -1095,7 +1095,7 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, names=names, res_name=res_name, ram_name=ram_name, print_pos=print_pos, print_sep=print_sep, print_max_ram_terms=print_max_ram_terms, print_max_unram_terms=print_max_unram_terms, - print_max_terse_terms=print_max_terse_terms, check=check, + print_max_terse_terms=print_max_terse_terms, show_prec=show_prec, check=check, unram=True, implementation=implementation) ###################################################### @@ -1498,7 +1498,7 @@ class Zp_class(UniqueFactory): *show_prec* determines whether the precision is printed (default ``False``):: sage: repr(Zp(5, 2, print_mode='bars', show_prec=True)(6)) - '...11 + O(5^2)' + '1|1 + O(5^2)' *name* and *print_alphabet* have no effect. @@ -1980,7 +1980,7 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, *show_prec* determines whether the precision is printed (default ``True``):: - sage: U. = Zq(9, 2, show_prec=False); repr(-3*(1+2*e)^4) + sage: U. = Zq(9, 2, print_mode='val-unit', show_prec=False); repr(-3*(1+2*e)^4) '3 * (1 + 3*e)' *print_sep*, *print_max_ram_terms* and *print_max_unram_terms* have no effect. @@ -2112,7 +2112,7 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, *show_prec* determines whether the precision is printed (default ``False``):: sage: U. = Zq(9, 2, print_mode='bars', show_prec=True); repr(-3*(1+2*e)^4) - '...[0, 1]|[1]|[] + O(3^2)' + '[0, 1]|[1]|[] + O(3^3)' *ram_name* and *print_max_terse_terms* have no effect. @@ -2192,7 +2192,8 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, F = q q = F[0][0]**F[0][1] base = Zp(p=F[0][0], prec=prec, type=type, print_mode=print_mode, halt=halt, names=ram_name, - print_pos=print_pos, print_sep=print_sep, print_max_terms=print_max_ram_terms, check=False) + print_pos=print_pos, print_sep=print_sep, print_max_terms=print_max_ram_terms, + show_prec=show_prec, check=False) if F[0][1] == 1: return base elif names is None: @@ -2208,7 +2209,7 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, names=names, res_name=res_name, ram_name=ram_name, print_pos=print_pos, print_sep=print_sep, print_max_ram_terms=print_max_ram_terms, print_max_unram_terms=print_max_unram_terms, - print_max_terse_terms=print_max_terse_terms, check=check, + print_max_terse_terms=print_max_terse_terms, show_prec=show_prec, check=check, unram=True, implementation=implementation) ###################################################### @@ -2581,7 +2582,7 @@ def create_object(self, version, key, shift_seed): T = ext_table['p', type(base.ground_ring_of_tower()).__base__] return T(premodulus, upoly, epoly, prec*precmult, halt, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'max_ram_terms': print_max_ram_terms, 'max_unram_terms': print_max_unram_terms, 'max_terse_terms': print_max_terse_terms}, + 'max_ram_terms': print_max_ram_terms, 'max_unram_terms': print_max_unram_terms, 'max_terse_terms': print_max_terse_terms, 'show_prec': show_prec}, names, implementation) ExtensionFactory = pAdicExtension = pAdicExtension_class("pAdicExtension") diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 8504efc426a..e24e9017958 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -246,7 +246,7 @@ def change(self, **kwds): sage: K.change(q=64) Unramified Extension of 2-adic Field with floating precision 4 in a defined by x^6 + x^4 + x^3 + x + 1 sage: R. = QQ[] - sage: K.change(modulus = x^2 - x + 2) + sage: K.change(modulus = x^2 - x + 2, print_pos=False) Unramified Extension of 5-adic Field with floating precision 4 in a defined by x^2 - x + 2 and variable names:: @@ -264,7 +264,9 @@ def change(self, **kwds): If you decrease the precision, the precision of the base stays the same:: sage: Kdown = K.change(prec=2); Kdown - Unramified Extension of 5-adic Field with floating precision 2 in a defined by x^3 + 3*x + 3 + Unramified Extension of 5-adic Field with floating precision 4 in a defined by x^3 + 3*x + 3 + sage: Kdown.precision_cap() + 2 sage: Kdown.base_ring() 5-adic Field with floating precision 4 """ @@ -316,14 +318,16 @@ def change(self, **kwds): if 'p' in kwds and kwds['p'] != p: raise ValueError("q does not match p") kwds['p'] = p + modulus = None if 'modulus' in kwds: modulus = kwds.pop('modulus') if n is not None and modulus.degree() != n: raise ValueError("modulus must have degree matching q") - functor.polys = [modulus] + if 'names' in kwds: + functor.names = [kwds.pop('names')] elif n is not None: functor.polys = [n] - for atr in ('names', 'var_name', 'res_name', 'unram_name', 'ram_name'): + for atr in ('var_name', 'res_name', 'unram_name', 'ram_name'): if atr in kwds: functor.kwds[atr] = kwds.pop(atr) for atr in ('print_mode', 'halt', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'show_prec', 'check'): @@ -333,6 +337,9 @@ def change(self, **kwds): ring = ring.change(**kwds) except AttributeError: raise NotImplementedError + if modulus is not None: + # the following should change after we switch to exact defining polynomials + functor.polys = [modulus.change_ring(ring)] return functor(ring) def precision_cap(self): From fcc8aa5279999dcadf48650897a6b19547952b19 Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 15 Jun 2017 21:16:48 +0000 Subject: [PATCH 041/184] Fixing various problems with the new change method --- src/sage/rings/padics/factory.py | 2 + src/sage/rings/padics/local_generic.py | 130 +++++++++++++----- .../rings/padics/padic_extension_generic.py | 2 +- src/sage/rings/padics/padic_generic.py | 2 +- 4 files changed, 102 insertions(+), 34 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index c6eb9ab80d0..9984594bde7 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -108,6 +108,8 @@ def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, pr print_max_terms = print_mode['max_ram_terms'] if 'max_terms' in print_mode: print_max_terms = print_mode['max_terms'] + if 'show_prec' in print_mode: + show_prec = print_mode['show_prec'] if 'mode' in print_mode: print_mode = print_mode['mode'] else: diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index e24e9017958..4eaf1d7c228 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -26,6 +26,7 @@ from sage.structure.category_object import check_default_category from sage.structure.parent import Parent from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ class LocalGeneric(CommutativeRing): def __init__(self, base, prec, names, element_class, category=None): @@ -197,20 +198,45 @@ def change(self, **kwds): INPUT: - Keyword arguments are passed on to the :mod:`constructors ` - via the :meth:`construction` functor. + The following arguments are applied to every ring in the tower: - NOTES: + - ``type`` -- string, the precision type + - ``p`` -- the prime of the ground ring. Defining polynomials + will be converted to the new base rings. + - ``print_mode`` -- string + - ``print_pos`` -- bool + - ``print_sep`` -- string + - ``print_alphabet`` -- dict + - ``show_prec`` -- bool + - ``check`` -- bool - For extension rings, some keywords take effect only on the extension - (``names``, ``var_name``, ``res_name``, ``unram_name``, ``ram_name``) - and others on both the extension and, recursively, the base ring - (``print_mode``, ``halt``, ``print_pos``, ``print_sep``, - ``print_alphabet``, ``print_max_ram_terms``, ``check``). + The following arguments are only applied to the top ring in the tower: - If the precision is increased on an extension ring, - the precision on the base is increased as necessary. - If the precision is decreased, the precision of the base is unchanged. + - ``var_name`` -- string + - ``res_name`` -- string + - ``unram_name`` -- string + - ``ram_name`` -- string + -- ``names`` -- string + -- ``modulus`` -- polynomial + + The following arguments have special behavior: + + -- ``prec`` -- integer. If the precision is increased on an extension ring, + the precision on the base is increased as necessary (respecting ramification). + If the precision is decreased, the precision of the base is unchanged. + + -- ``field`` -- bool. If True, switch to a tower of fields via the fraction field. + If False, switch to a tower of rings of integers. + + -- ``q`` -- prime power. Replace the initial unramified extension of `\Qp` or `\Zp` + with an unramified extension of residue cardinality `q`. + If the initial extension is ramified, add in an unramified extension. + + -- ``base`` -- ring or field. Use a specific base ring instead of recursively + calling :meth:`change` down the tower. + + See the :mod:`constructors ` for more details on the + meaning of these arguments. EXAMPLES: @@ -270,6 +296,26 @@ def change(self, **kwds): sage: Kdown.base_ring() 5-adic Field with floating precision 4 """ + # We support both print_* and * for *=mode, pos, sep, alphabet + for atr in ('print_mode', 'print_pos', 'print_sep', 'print_alphabet'): + if atr in kwds: + kwds[atr[6:]] = kwds.pop(atr) + def get_unramified_modulus(q, res_name): + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF + return GF(q, res_name).modulus().change_ring(ZZ) + n = None + q = None + from .padic_base_generic import pAdicBaseGeneric + if 'q' in kwds and isinstance(self.base_ring(), pAdicBaseGeneric): + q = kwds.pop('q') + if not isinstance(q, Integer): + raise TypeError("q must be an integer") + p, n = q.is_prime_power(get_data=True) + if n == 0: + raise ValueError("q must be a prime power") + if 'p' in kwds and kwds['p'] != p: + raise ValueError("q does not match p") + kwds['p'] = p functor, ring = self.construction() functor = copy(functor) # There are two kinds of functors possible: @@ -277,6 +323,7 @@ def change(self, **kwds): # We distinguish them by the presence of ``prec``, if hasattr(functor, "prec"): functor.extras = copy(functor.extras) + functor.extras['print_mode'] = copy(functor.extras['print_mode']) if 'type' in kwds and kwds['type'] not in functor._dvr_types: raise ValueError("completion type must be one of %s"%(", ".join(functor._dvr_types[1:]))) if 'field' in kwds: @@ -290,16 +337,41 @@ def change(self, **kwds): raise TypeError('You must specify the type explicitly') else: ring = ring.ring_of_integers() + # If we are switching to 'digits', or changing p, need to ensure a large enough alphabet. + if 'alphabet' not in kwds and (kwds.get('mode') == 'digits' or + (functor.extras['print_mode'].get('mode') == 'digits' and + kwds.get('p', functor.p) > functor.p)): + p = kwds.get('p', functor.p) + from .padic_printing import _printer_defaults + kwds['alphabet'] = _printer_defaults.alphabet()[:p] for atr in ('p', 'prec', 'type'): if atr in kwds: setattr(functor, atr, kwds.pop(atr)) - for atr in ('print_mode', 'halt', 'names', 'ram_name', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'show_prec', 'check'): + if q is not None: + if 'names' in kwds: + names = kwds.pop('names') + else: + raise TypeError("You must specify the name of the generator") + res_name = kwds.pop('res_name', names + '0') + modulus = kwds.pop('modulus', get_unramified_modulus(q, res_name)) + implementation = kwds.pop('implementation', 'FLINT') + for atr in ('names', 'check'): if atr in kwds: functor.extras[atr] = kwds.pop(atr) + for atr in ('mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet', 'show_prec'): + if atr in kwds: + functor.extras['print_mode'][atr] = kwds.pop(atr) if kwds: raise ValueError("Extra arguments received: %s"%(", ".join(kwds.keys()))) + if q is not None: + # Create an unramified extension + base = functor(ring) + from .factory import ExtensionFactory + modulus = modulus.change_ring(base) + return ExtensionFactory(base=base, premodulus=modulus, names=names, res_name=res_name, unram=True, implementation=implementation) else: functor.kwds = copy(functor.kwds) + functor.kwds['print_mode'] = copy(functor.kwds['print_mode']) if 'prec' in kwds: prec = kwds.pop('prec') baseprec = (prec - 1) // self.e() + 1 @@ -307,36 +379,30 @@ def change(self, **kwds): kwds['prec'] = baseprec functor.kwds['prec'] = prec from sage.rings.padics.padic_base_generic import pAdicBaseGeneric - n = None - if 'q' in kwds and isinstance(ring, pAdicBaseGeneric): - q = kwds.pop('q') - if not isinstance(q, Integer): - raise TypeError("q must be an integer") - p, n = q.is_prime_power(get_data=True) - if n == 0: - raise ValueError("q must be a prime power") - if 'p' in kwds and kwds['p'] != p: - raise ValueError("q does not match p") - kwds['p'] = p + if 'names' in kwds: + functor.names = [kwds.pop('names')] modulus = None if 'modulus' in kwds: modulus = kwds.pop('modulus') if n is not None and modulus.degree() != n: raise ValueError("modulus must have degree matching q") - if 'names' in kwds: - functor.names = [kwds.pop('names')] - elif n is not None: - functor.polys = [n] + elif q is not None and self.f() != 1: + # If q is specified, replace the modulus with one from q. + modulus = get_unramified_modulus(q, functor.kwds.get('res_name', functor.names[0] + '0')) for atr in ('var_name', 'res_name', 'unram_name', 'ram_name'): if atr in kwds: functor.kwds[atr] = kwds.pop(atr) - for atr in ('print_mode', 'halt', 'print_pos', 'print_sep', 'print_alphabet', 'print_max_terms', 'show_prec', 'check'): + if 'check' in kwds: + functor.kwds['check'] = kwds['check'] + for atr in ('mode', 'pos', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet', 'show_prec'): if atr in kwds: - functor.kwds[atr] = kwds[atr] - try: + functor.kwds['print_mode'][atr] = kwds[atr] + if 'base' in kwds: + ring = kwds['base'] + else: + if q is not None and self.f() == 1: + kwds['q'] = q ring = ring.change(**kwds) - except AttributeError: - raise NotImplementedError if modulus is not None: # the following should change after we switch to exact defining polynomials functor.polys = [modulus.change_ring(ring)] diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index 45f8ddf9fe3..5ed4feac929 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -297,7 +297,7 @@ def fraction_field(self, print_mode=None): if print_mode is None: return self.change(field=True) else: - return self.change(field=True, print_mode=print_mode) + return self.change(field=True, **print_mode) print_mode = self._modified_print_mode(print_mode) ground_mode = print_mode.copy() # We don't want to confuse the ground ring with different names. diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 87c30d9d76d..6ad1048e281 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -108,7 +108,7 @@ def _modified_print_mode(self, print_mode): print_mode = {} elif isinstance(print_mode, str): print_mode = {'mode': print_mode} - for option in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet']: + for option in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet', 'show_prec']: if option not in print_mode: print_mode[option] = self._printer.dict()[option] return print_mode From 3b2d94a15e0de51fa5ba2419fa53cbfe5b4f810d Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 15 Jun 2017 22:22:39 +0000 Subject: [PATCH 042/184] Remove old code for fraction_field and integer_ring from padic_extension_generic --- .../rings/padics/padic_extension_generic.py | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index 5ed4feac929..c07f469b690 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -298,18 +298,6 @@ def fraction_field(self, print_mode=None): return self.change(field=True) else: return self.change(field=True, **print_mode) - print_mode = self._modified_print_mode(print_mode) - ground_mode = print_mode.copy() - # We don't want to confuse the ground ring with different names. - ground_mode['ram_name'] = None - ground_mode['unram_name'] = None - K = self.ground_ring().fraction_field(ground_mode) - #we don't want to set the print options due to the ground ring since - #different extension fields (with different options) can share the same ground ring. - if self.is_lazy(): - return K.extension(self._pre_poly, prec = self.precision_cap(), halt = self.halting_parameter(), res_name = self.residue_field().variable_name(), print_mode=print_mode, implementation=self._implementation) - else: - return K.extension(self._pre_poly, prec = self.precision_cap(), res_name = self.residue_field().variable_name(), print_mode=print_mode, implementation=self._implementation) def integer_ring(self, print_mode=None): r""" @@ -340,19 +328,7 @@ def integer_ring(self, print_mode=None): if print_mode is None: return self.change(field=False) else: - return self.change(field=False, print_mode=print_mode) - print_mode = self._modified_print_mode(print_mode) - ground_mode = print_mode.copy() - # We don't want to confuse the ground ring with different names. - ground_mode['ram_name'] = None - ground_mode['unram_name'] = None - K = self.ground_ring().integer_ring(ground_mode) - #we don't want to set the print options due to the ground ring since - #different extension fields (with different options) can share the same ground ring. - if self.is_lazy(): - return K.extension(self._pre_poly, prec = self.precision_cap(), halt = self.halting_parameter(), res_name = self.residue_field().variable_name(), print_mode=print_mode) - else: - return K.extension(self._pre_poly, prec = self.precision_cap(), res_name = self.residue_field().variable_name(), print_mode=print_mode) + return self.change(field=False, **print_mode) #def hasGNB(self): # raise NotImplementedError From f386dc614c3369f41cdde8b8057a10025464998e Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 15 Jun 2017 22:44:29 +0000 Subject: [PATCH 043/184] Fix bug in ring_of_integers --- src/sage/rings/padics/local_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 4eaf1d7c228..ddea9d16e16 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -335,7 +335,7 @@ def get_unramified_modulus(q, res_name): kwds['type'] = 'capped-rel' elif self._prec_type() == 'fixed-mod': raise TypeError('You must specify the type explicitly') - else: + elif ring.is_field(): ring = ring.ring_of_integers() # If we are switching to 'digits', or changing p, need to ensure a large enough alphabet. if 'alphabet' not in kwds and (kwds.get('mode') == 'digits' or From f4a534528ce461178ed32e60266beb0f28db00dc Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 16 Jun 2017 08:19:48 +0000 Subject: [PATCH 044/184] Move show prec logic into get_key --- src/sage/rings/padics/factory.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 9984594bde7..fe8edceb65d 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -157,6 +157,13 @@ def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, pr name = names else: name = str(names) + if show_prec is None: + if type == 'floating-point': + show_prec = False + elif print_mode ('series', 'terse', 'val-unit'): + show_prec = True + else: + show_prec = False if type in valid_non_lazy_types: key = (p, prec, type, print_mode, name, print_pos, print_sep, tuple(print_alphabet), print_max_terms, show_prec) elif type == 'lazy': @@ -557,8 +564,6 @@ def create_object(self, version, key): return pAdicFieldCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) elif type == 'floating-point': - if show_prec is None: - show_prec = False if print_mode == 'terse': return pAdicFieldFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_terse_terms': print_max_terms, 'show_prec': show_prec}, name) @@ -1650,8 +1655,6 @@ def create_object(self, version, key): return pAdicRingCappedAbsolute(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) elif type == 'floating-point': - if show_prec is None: - show_prec = False return pAdicRingFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': show_prec}, name) else: @@ -2409,6 +2412,13 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = print_max_unram_terms = base._printer._max_unram_terms() if print_max_terse_terms is None: print_max_terse_terms = base._printer._max_terse_terms() + if show_prec is None: + if base._prec_type() == 'floating-point': + show_prec = False + elif print_mode in ('series', 'terse', 'val-unit'): + show_prec = True + else: + show_prec = False from sage.symbolic.expression import is_Expression if check: if is_Expression(premodulus): From e376b9027401c2e2b83f659f9958aac374d747ea Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 16 Jun 2017 21:49:06 +0000 Subject: [PATCH 045/184] Fix doctest errors and a bug in change related to show_prec --- src/sage/rings/padics/factory.py | 48 +++++++++++++++++--------- src/sage/rings/padics/local_generic.py | 10 ++++++ 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index fe8edceb65d..a12ae1f8c8c 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -69,6 +69,32 @@ ext_table['u', pAdicFieldFloatingPoint] = UnramifiedExtensionFieldFloatingPoint #ext_table['u', pAdicRingLazy] = UnramifiedExtensionRingLazy +def _default_show_prec(type, print_mode): + """ + Returns the default show_prec value for a given type and printing mode. + + INPUT: + + - ``type`` -- a string: ``'capped-rel'``, ``'capped-abs'``, ``'fixed-mod'`` or ``'floating-point'`` + - ``print_mode`` -- a string: ``'series'``, ``'terse'``, ``'val-unit'``, ``'digits'``, ``'bars'`` + + EXAMPLES:: + + sage: from sage.rings.padics.factory import _default_show_prec + sage: _default_show_prec('floating-point', 'series') + False + sage: _default_show_prec('capped-rel', 'series') + True + sage: _default_show_prec('capped-abs', 'digits') + False + """ + if type == 'floating-point': + return False + elif print_mode in ('series', 'terse', 'val-unit'): + return True + else: + return False + def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, valid_non_lazy_types): """ This implements create_key for Zp and Qp: moving it here prevents code duplication. @@ -158,12 +184,7 @@ def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, pr else: name = str(names) if show_prec is None: - if type == 'floating-point': - show_prec = False - elif print_mode ('series', 'terse', 'val-unit'): - show_prec = True - else: - show_prec = False + show_prec = _default_show_prec(type, print_mode) if type in valid_non_lazy_types: key = (p, prec, type, print_mode, name, print_pos, print_sep, tuple(print_alphabet), print_max_terms, show_prec) elif type == 'lazy': @@ -516,7 +537,7 @@ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = N TESTS:: sage: Qp.create_key(5,40) - (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, None) + (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, True) """ return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, ['capped-rel', 'floating-point']) @@ -1602,9 +1623,9 @@ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = N TESTS:: sage: Zp.create_key(5,40) - (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, None) + (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1, True) sage: Zp.create_key(5,40,print_mode='digits') - (5, 40, 'capped-rel', 'digits', '5', True, '|', ('0', '1', '2', '3', '4'), -1, None) + (5, 40, 'capped-rel', 'digits', '5', True, '|', ('0', '1', '2', '3', '4'), -1, False) """ return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, show_prec, check, ['capped-rel', 'fixed-mod', 'capped-abs', 'floating-point']) @@ -2396,7 +2417,7 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = sage: R = Zp(5,3) sage: S. = ZZ[] sage: pAdicExtension.create_key_and_extra_args(R, x^4-15,names='w') - (('e', 5-adic Ring with capped relative precision 3, x^4 - 15, (1 + O(5^3))*x^4 + (O(5^4))*x^3 + (O(5^4))*x^2 + (O(5^4))*x + (2*5 + 4*5^2 + 4*5^3 + O(5^4)), ('w', None, None, 'w'), 12, None, 'series', True, '|', (), -1, -1, -1, None, 'NTL'), {'shift_seed': (3 + O(5^3))}) + (('e', 5-adic Ring with capped relative precision 3, x^4 - 15, (1 + O(5^3))*x^4 + (O(5^4))*x^3 + (O(5^4))*x^2 + (O(5^4))*x + (2*5 + 4*5^2 + 4*5^3 + O(5^4)), ('w', None, None, 'w'), 12, None, 'series', True, '|', (), -1, -1, -1, True, 'NTL'), {'shift_seed': (3 + O(5^3))}) """ if print_mode is None: print_mode = base.print_mode() @@ -2413,12 +2434,7 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = if print_max_terse_terms is None: print_max_terse_terms = base._printer._max_terse_terms() if show_prec is None: - if base._prec_type() == 'floating-point': - show_prec = False - elif print_mode in ('series', 'terse', 'val-unit'): - show_prec = True - else: - show_prec = False + show_prec = _default_show_prec(base._prec_type(), print_mode) from sage.symbolic.expression import is_Expression if check: if is_Expression(premodulus): diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index ddea9d16e16..9f2db98e5e2 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -318,6 +318,16 @@ def get_unramified_modulus(q, res_name): kwds['p'] = p functor, ring = self.construction() functor = copy(functor) + if 'mode' in kwds and 'show_prec' not in kwds: + new_type = kwds.get('type', self._prec_type()) + cur_type = self._prec_type() + cur_mode = self._printer._print_mode() + cur_show_prec = self._printer._show_prec() + from .factory import _default_show_prec + if cur_show_prec == _default_show_prec(cur_type, cur_mode): + kwds['show_prec'] = _default_show_prec(new_type, kwds['mode']) + else: + raise RuntimeError # There are two kinds of functors possible: # CompletionFunctor and AlgebraicExtensionFunctor # We distinguish them by the presence of ``prec``, From 6928d9df219e28a5a94af9a8ab5d133bb49ae001 Mon Sep 17 00:00:00 2001 From: Karim Van Aelst Date: Fri, 23 Jun 2017 20:51:30 +0200 Subject: [PATCH 046/184] more numerical solvers, an analytical solver, parents, parameters detected --- src/sage/manifolds/differentiable/chart.py | 65 +- src/sage/manifolds/differentiable/curve.py | 25 +- .../differentiable/integrated_curve.py | 1531 ++++++++++++----- src/sage/manifolds/differentiable/manifold.py | 121 +- .../differentiable/manifold_homset.py | 1278 ++++++++++++++ 5 files changed, 2531 insertions(+), 489 deletions(-) diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index fb6a2c10651..6f70c6d2b4a 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -550,7 +550,7 @@ def restrict(self, subset, restrictions=None): def symbolic_velocities(self, left='D', right=None): r""" - Returns a tuple of symbolic variables ready to be used by the + Return a list of symbolic variables ready to be used by the user as the derivatives of the coordinate functions with respect to a curve parameter (i.e. the velocities along the curve). It may actually serve to denote anything else than velocities, @@ -575,18 +575,6 @@ def symbolic_velocities(self, left='D', right=None): Symbolic derivatives of the Cartesian coordinates of the 3-dimensional Euclidean space:: - sage: R3 = Manifold(3, 'R3', start_index=1) - sage: cart. = R3.chart() - sage: R3 = Manifold(3, 'R3', start_index=1) - sage: cart. = R3.chart() - sage: Deriv1 = cart.symbolic_velocities(); Deriv1 - [DX, DY, DZ] - sage: Deriv2 = cart.symbolic_velocities(left='d', right="_dt") - sage: Deriv2 - [dX_dt, dY_dt, dZ_dt] - - TESTS:: - sage: R3 = Manifold(3, 'R3', start_index=1) sage: cart. = R3.chart() sage: D = cart.symbolic_velocities(); D @@ -605,21 +593,58 @@ def symbolic_velocities(self, left='D', right=None): identifier. sage: D = cart.symbolic_velocities(left='', right="_dot"); D [X_dot, Y_dot, Z_dot] + sage: R. = RealLine() + sage: canon_chart = R.default_chart() + sage: D = canon_chart.symbolic_velocities() ; D + [Dt] """ from sage.symbolic.ring import var - string_velocities = [left + format(coord_func) - for coord_func in self[:]] # will raise - # error in case left is not a string + # The case len(self[:]) = 1 is treated apart due to the + # following fact. + # In the case of several coordinates, the argument of 'var' (as + # implemented below after the case len(self[:]) = 1) is a list + # of strings of the form ['Dx1', 'Dx2', ...] and not a unique + # string of the form 'Dx1 Dx2 ...'. + # Although 'var' is supposed to accept both syntaxes, the first + # one causes an error when it contains only one argument, due to + # line 784 of sage/symbolic/ring.pyx : + # "return self.symbol(name, latex_name=formatted_latex_name, domain=domain)" + # In this line, the first argument 'name' of 'symbol' is a list + # and not a string if the argument of 'var' is a list of one + # string (of the type ['Dt']), which causes error in 'symbol'. + # This might be corrected. + if len(self[:]) == 1: + string_vel = left + format(self[:][0]) # will raise an error + # in case left is not a string + if right is not None: + string_vel += right # will raise an error in case right + # is not a string + + # If the argument of 'var' contains only one word, for + # instance: + # sage: var('Dt') + # then 'var' does not return a tuple containing one symbolic + # expression, but the symbolic expression itself. + # This is taken into account below in order to return a list + # containing one symbolic expression. + return [var(string_vel)] + + list_strings_velocities = [left + format(coord_func) + for coord_func in self[:]] # will + # raise an error in case left is not a string if right is not None: - string_velocities = [string_vel + right for string_vel - in string_velocities] # will raise - # error in case right is not a string + list_strings_velocities = [string_vel + right for string_vel + in list_strings_velocities] # will + # raise an error in case right is not a string + + return list(var(list_strings_velocities)) + + - return list(var(string_velocities)) #***************************************************************************** diff --git a/src/sage/manifolds/differentiable/curve.py b/src/sage/manifolds/differentiable/curve.py index e6504c4e06b..3583990b1a9 100644 --- a/src/sage/manifolds/differentiable/curve.py +++ b/src/sage/manifolds/differentiable/curve.py @@ -867,7 +867,7 @@ def _graphics(self, plot_curve, ambient_coords, thickness=1, aspect_ratio='automatic', color='red', style='-', label_axes=True): r""" - Plots a 2D or 3D curve in a Cartesian graph with axes labeled by + Plot a 2D or 3D curve in a Cartesian graph with axes labeled by the ambient coordinates; it is invoked by the methods :meth:`plot` of :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve`, @@ -877,6 +877,29 @@ def _graphics(self, plot_curve, ambient_coords, thickness=1, and :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic`). + TESTS:: + + sage: M = Manifold(2, 'R^2') + sage: X. = M.chart() + sage: R. = RealLine() + sage: c = M.curve([cos(t), sin(t)], (t, 0, 2*pi), name='c') + sage: graph = c._graphics([[1,2], [3,4]], [x,y]) + sage: graph._objects[0].xdata == [1,3] + True + sage: graph._objects[0].ydata == [2,4] + True + sage: graph._objects[0]._options['thickness'] == 1 + True + sage: graph._extra_kwds['aspect_ratio'] == 'automatic' + True + sage: graph._objects[0]._options['rgbcolor'] == 'red' + True + sage: graph._objects[0]._options['linestyle'] == '-' + True + sage: l = [r'$'+latex(x)+r'$', r'$'+latex(y)+r'$'] + sage: graph._extra_kwds['axes_labels'] == l + True + """ from sage.plot.graphics import Graphics diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index 630073f79f9..069a4e21b99 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -27,8 +27,8 @@ from sage.symbolic.expression import Expression from sage.rings.infinity import Infinity from sage.calculus.desolvers import desolve_system_rk4 +from sage.calculus.desolvers import desolve_odeint from sage.manifolds.chart import Chart -from sage.manifolds.differentiable.real_line import OpenInterval from sage.manifolds.differentiable.curve import DifferentiableCurve from sage.manifolds.differentiable.tangent_vector import TangentVector from sage.calculus.interpolation import Spline @@ -47,7 +47,7 @@ class IntegratedCurve(DifferentiableCurve): INPUT: - ``parent`` -- - :class:`~sage.manifolds.differentiable.manifold_homset.DifferentiableCurveSet` + :class:`~sage.manifolds.differentiable.manifold_homset.IntegratedCurveSet` the set of curves `\mathrm{Hom}(I, M)` to which the curve belongs - ``equations_rhs`` -- list of the right-hand sides of the equations on the velocities only (the term *velocity* referring to the @@ -63,9 +63,6 @@ class IntegratedCurve(DifferentiableCurve): - ``chart`` -- (default: ``None``) chart on the manifold in which the equations are given; if ``None`` the default chart of the manifold is assumed - - ``parameters`` -- list of the symbolic expressions used in - ``equations_rhs`` and ``initial_tangent_vector`` other than the - coordinates, the velocities and the curve parameter - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used @@ -100,14 +97,14 @@ class IntegratedCurve(DifferentiableCurve): symbolic expressions denoting the velocities and the various parameters:: - sage: M = Manifold(3, 'M') + sage: M = Manifold(3, 'M', start_index=1) sage: X. = M.chart() sage: var('t B_0 m q L T') (t, B_0, m, q, L, T) sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities(); D [Dx1, Dx2, Dx3] - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] Set the initial conditions:: @@ -118,7 +115,7 @@ class IntegratedCurve(DifferentiableCurve): Declare an integrated curve and display information relative to it:: sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: parameters=[B_0, m, q, L, T]); c + ....: verbose=False) ; c Integrated curve c in the 3-dimensional differentiable manifold M sage: sys = c.system() @@ -135,10 +132,10 @@ class IntegratedCurve(DifferentiableCurve): components [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) d(x1)/dt = Dx1 - d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) d(x2)/dt = Dx2 - d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) d(x3)/dt = Dx3 + d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) d(Dx3)/dt = 0 @@ -148,13 +145,18 @@ class IntegratedCurve(DifferentiableCurve): sage: sol = c.solve(step=0.2, ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, ....: solution_key='carac time 1') - Performing 4th order Runge-Kutta integration by default... - Numerical integration completed. Resulting list of points was - associated with the key 'carac time 1' (if this key already - referred to a former numerical solution, such a solution was - erased). + Performing 4th order Runge-Kutta integration with Maxima by + default... + + Numerical integration completed. + Checking all points are in the chart domain... + + All points are in the chart domain. + The resulting list of points was associated with the key + 'carac time 1' (if this key already referred to a former + numerical solution, such a solution was erased). sage: interp = c.interpolate(solution_key='carac time 1', - ....: interpolation_key='interp 1') + ....: interpolation_key='interp 1') Performing cubic spline interpolation by default... Interpolation completed and associated with the key 'interp 1' (if this key already referred to a former interpolation, @@ -180,7 +182,7 @@ class IntegratedCurve(DifferentiableCurve): Plotting a numerical solution (with or without its tangent vector field) also requires the solution to be interpolated at least once:: - sage: c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], + sage: c_plot_2d_1 = c.plot_integrated(ambient_coords=[x1, x2], ....: interpolation_key='interp 1', thickness=2.5, ....: display_tangent=True, plot_points=200, ....: plot_points_tangent=10, scale=0.5, @@ -197,18 +199,17 @@ class IntegratedCurve(DifferentiableCurve): var('x1 x2 x3 t B_0 m q L T') B = B_0*t/T*exp(-(x1**2 + x2**2)/L**2) D = X.symbolic_velocities() - eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + eqns = [q*B/m*D[1], -q*B/m*D[0], 0] p = M.point((0,0,0), name='p') Tp = M.tangent_space(p) v = Tp((1,0,1)) - c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - parameters=[B_0, m, q, L, T]) + c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c') c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, solution_key='carac time 1') c.interpolate(solution_key='carac time 1', interpolation_key='interp 1') - c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], + c_plot_2d_1 = c.plot_integrated(ambient_coords=[x1, x2], interpolation_key='interp 1', thickness=2.5, display_tangent=True, plot_points=200, plot_points_tangent=10, scale=0.5, color='blue', @@ -223,19 +224,18 @@ class IntegratedCurve(DifferentiableCurve): ....: solution_key='carac time 100', verbose=False) sage: interp = c.interpolate(solution_key='carac time 100', ....: interpolation_key='interp 100', verbose=False) - sage: c_plot_3d_100 = c.plot(interpolation_key='interp 100', + sage: c_plot_3d_100=c.plot_integrated(interpolation_key='interp 100', ....: thickness=2.5, display_tangent=True, ....: plot_points=200, plot_points_tangent=10, ....: scale=0.5, color='green', ....: color_tangent='orange', verbose=False) - sage: c_plot_3d_1 = c.plot(interpolation_key='interp 1', + sage: c_plot_3d_1=c.plot_integrated(interpolation_key='interp 1', ....: thickness=2.5, display_tangent=True, ....: plot_points=200, plot_points_tangent=10, ....: scale=0.5, color='blue', ....: color_tangent='red', verbose=False) - sage: viewer3D = 'threejs' sage: graph = c_plot_3d_1 + c_plot_3d_100 - sage: graph.show(viewer = viewer3D) + sage: graph.show() .. PLOT:: @@ -244,12 +244,11 @@ class IntegratedCurve(DifferentiableCurve): var('x1 x2 x3 t B_0 m q L T') B = B_0*t/T*exp(-(x1**2 + x2**2)/L**2) D = X.symbolic_velocities() - eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + eqns = [q*B/m*D[1], -q*B/m*D[0], 0] p = M.point((0,0,0), name='p') Tp = M.tangent_space(p) v = Tp((1,0,1)) - c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - parameters=[B_0, m, q, L, T]) + c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c') c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, solution_key='carac time 1') @@ -260,12 +259,12 @@ class IntegratedCurve(DifferentiableCurve): solution_key='carac time 100') c.interpolate(solution_key='carac time 100', interpolation_key='interp 100') - c_plot_3d_1 = c.plot(interpolation_key='interp 1', + c_plot_3d_1 = c.plot_integrated(interpolation_key='interp 1', thickness=2.5, display_tangent=True, plot_points=200, plot_points_tangent=10, scale=0.5, color='blue', color_tangent='red', verbose=False) - c_plot_3d_100 = c.plot(interpolation_key='interp 100', + c_plot_3d_100 = c.plot_integrated(interpolation_key='interp 100', thickness=2.5, display_tangent=True, plot_points=200, plot_points_tangent=10, scale=0.5, color='green', @@ -277,10 +276,10 @@ class IntegratedCurve(DifferentiableCurve): def __init__(self, parent, equations_rhs, velocities, curve_parameter, initial_tangent_vector, chart=None, - parameters=None, name=None, latex_name=None, - is_isomorphism=False, is_identity=False): + name=None, latex_name=None, + is_isomorphism=False, is_identity=False, verbose=True): r""" - Constructs a numerical curve. + Construct a numerical curve. TESTS:: @@ -289,137 +288,192 @@ def __init__(self, parent, equations_rhs, velocities, sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) sage: c = M.integrated_curve(eqns + [x1], D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) + ....: name='c') Traceback (most recent call last): ... ValueError: Number of equations should equal codomain dimension. - sage: c = M.integrated_curve(eqns, D + [x1],(t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) + sage: c = M.integrated_curve(eqns, D + [x1], (t, 0, 5), v, + ....: name='c') Traceback (most recent call last): ... ValueError: Number of velocities should equal codomain dimension. - sage: c = M.integrated_curve(eqns, D, (t, -oo, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) + sage: c = M.integrated_curve(eqns, D,(t,-oo,5), v, name='c') Traceback (most recent call last): ... - ValueError: Both boundaries of the interval need to be - finite. - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), x1, - ....: name='c', parameters=[B_0, m, q, L, T]) + ValueError: Both boundaries of the interval defining the + domain of a Homset of integrated curves need to be finite. + sage: c = M.integrated_curve(eqns, D, (t,0,5), x1, name='c') Traceback (most recent call last): ... TypeError: x1 should be a tangent vector. - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[m, q, L, T]) - Traceback (most recent call last): - ... - TypeError: B_0 should either be a coordinate function of - Chart (M, (x1, x2, x3)), or one of the corresponding - velocities [Dx1, Dx2, Dx3], or the curve parameter t, or - one of the parameters [m, q, L, T]. - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]); c + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) ; c Integrated curve c in the 3-dimensional differentiable manifold M - sage: # TestSuite(c).run() # pickling and category failed + sage: #TestSuite(c).run() """ - # starting with parent class method to initialize the four last - # arguments + from sage.symbolic.ring import SR + + # start with parent class method to initialize the four last + # arguments: DifferentiableCurve.__init__(self, parent, name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, is_identity=is_identity) # (coord_expression=None) - # checking argument 'parent' + # check argument 'parent': 't_min' and 't_max' below are only + # allowed to be either expressions of finite real values: domain = self.domain() - if not isinstance(domain, OpenInterval): - raise TypeError("{} is not a real interval".format(domain)) - else: - t_min = domain.lower_bound() - t_max = domain.upper_bound() - if t_min == -Infinity or t_max == +Infinity: - raise ValueError("Both boundaries of the interval " + - "need to be finite.") - - # checking argument 'equations_rhs' - codomain_dimension = self.codomain().dim() - if len(equations_rhs) != codomain_dimension: + t_min = domain.lower_bound() + t_max = domain.upper_bound() + if t_min == -Infinity or t_max == +Infinity: + raise ValueError("Both boundaries of the interval " + + "need to be finite.") + + codomain = self.codomain() + + if self._is_identity: # init of DifferentiableCurve should have + # set this attribute to 'True' if argument is_identity of this + # __init__ was set to 'True'. + # In this case, init of DifferentiableCurve has checked that the + # domain and codomain coincide. + # Then, at this stage, they necessarily are a certain finite + # real interval. + # Arguments 'equations_rhs', 'initial_tangent_vector' and + # 'chart' are then modified whatever their values were in order + # to construct an integrated version of the identity map on the + # finite interval. + equations_rhs = [0] + chart = codomain.default_chart() + p = codomain.point([t_min + (t_max-t_min)/10**(6)])#slightly + # shifting the initial point inside the interval is required + # since the interval is open + Tp = codomain.tangent_space(p) + initial_tangent_vector = Tp([1 - 10**(-6)]) # taking a + # derivative slightly less than 1 is required for the last + # point to be inside the open interval as well (this + # obviously damages the approximation of the identity + # provided by 'self') + + # check argument 'equations_rhs': + dim = codomain.dim() + if len(equations_rhs) != dim: raise ValueError("Number of equations should equal " + "codomain dimension.") - for eqn in equations_rhs: - if not isinstance(eqn, Expression): - raise TypeError("{} should be ".format(eqn) + - "a symbolic expression.") - # desolve_system_rk4 called in 'solve' method is in charge of - # raising errors about possibly remaining problems in argument - # equations_rhs - - # checking argument 'velocities' - if len(velocities) != codomain_dimension: + + # check the chart: + if chart is not None: + if chart not in codomain.atlas(): + raise ValueError("{} should be a chart ".format(chart) + + "on the {}".format(codomain)) + else: + chart = codomain.default_chart() + + # check argument 'velocities': + if len(velocities) != dim: raise ValueError("Number of velocities should equal " + "codomain dimension.") - - # checking argument 'curve_parameter' + # in particular, check that no velocity coincides with a + # coordinate: + for vel in velocities: + if vel in chart[:]: + str_error = "{} should not be used as a ".format(vel) + str_error += "velocity since it also denotes " + str_error += "a coordinate." + raise ValueError(str_error) + + # check argument 'curve_parameter': if not isinstance(curve_parameter, Expression): raise TypeError("{} should be ".format(curve_parameter) + "a symbolic expression.") - # desolve_system_rk4 called in 'solve' method is in charge of - # raising errors about possibly remaining problems in argument - # curve_parameter - - # checking argument 'initial_tangent_vector' + # in particular, check that it does not coincide with a + # coordinate or a velocity: + coords_vels = list(chart[:]) + list(velocities) + if curve_parameter in coords_vels: + str_error = "{} should not be used ".format(curve_parameter) + str_error += "as the curve parameter since it also denotes " + str_error += "a coordinate or a velocity." + raise ValueError(str_error) + # the various algorithms called in 'solve' method are in charge + # of raising errors about possibly remaining problems regarding + # 'curve_parameter' + + # check argument 'initial_tangent_vector': if not isinstance(initial_tangent_vector, TangentVector): raise TypeError("{} ".format(initial_tangent_vector) + "should be a tangent vector.") - - # setting the chart - if chart is None: - chart = self.codomain().default_chart() - - # checking argument 'parameters' - codomain_dimension = self.codomain().dim() - if parameters is not None: - for param in parameters: - if not isinstance(param, Expression): - raise TypeError("{} should be ".format(param) + - "a symbolic expression.") - - # checking that equations_rhs only uses the chart coordinates, - # the velocities, the curve parameter (and the parameters if - # there are) - announced_variables = [coord_func for coord_func in chart[:]] - announced_variables += [vel for vel in velocities] - announced_variables += [curve_parameter] - if parameters is not None: - announced_variables += [param for param in parameters] - - equations_rhs_variables = set() + # in particular, check that its base point sits in the domain + # of the chart (if its coordinates are explicitly given): + initial_pt = initial_tangent_vector.parent().base_point() + # line above retrieves the initial point as the base point of + # the tangent space to which the initial tangent vector belongs + initial_pt_coords = initial_pt.coordinates(chart) + i0 = chart.manifold().start_index() + for i in range(dim): + if not isinstance(initial_pt_coords[i], Expression): + coord_value = initial_pt_coords[i] + coord_min = chart.coord_bounds(i+i0)[0][0] + coord_max = chart.coord_bounds(i+i0)[1][0] + if coord_value <= coord_min or coord_value >= coord_max: + raise ValueError("Initial point should be in the " + + "domain of the chart.") + + # prepare attribute '_parameters': + announced_variables = set(coords_vels + [curve_parameter]) + parameters = set() + # extract all the variables appearing in the equations: for eqn in equations_rhs: - equations_rhs_variables=equations_rhs_variables.union(eqn.variables()) - - for rhs_variable in equations_rhs_variables: - if rhs_variable not in announced_variables: - str_error = "{} should either be ".format(rhs_variable) - str_error += "a coordinate function of " - str_error += "{}, ".format(chart) - str_error += "or one of the corresponding velocities " - str_error += "{}, ".format(velocities) - str_error += "or the curve parameter " - str_error += "{}".format(curve_parameter) - if parameters is not None: - str_error += ", or one of the parameters " - str_error += "{}".format(parameters) - raise TypeError(str_error + ".") - - # defining all attributes + if isinstance(eqn, Expression): # some right hand sides + # might merely be real numbers and not expressions, so that + # they do not contain any variable, and method 'variables' + # could not be called on them + parameters = parameters.union(eqn.variables()) + # remove the Expressions that should not be treated as + # parameters (i.e. the coordinate functions, the velocities and + # the curve parameter): + parameters = parameters.difference(announced_variables) + # extract all the variables appearing in the boundaries: + if isinstance(t_min, Expression): + parameters = parameters.union(t_min.variables()) + if isinstance(t_max, Expression): + parameters = parameters.union(t_max.variables()) + # extract all the variables appearing in the initial point + # coordinates: + for coord in initial_pt_coords: + if isinstance(coord,Expression): + parameters = parameters.union(coord.variables()) + # extract all the variables appearing in the initial tangent + # vector components: + initial_coord_basis = chart.frame().at(initial_pt) + initial_tgt_vec_comps=initial_tangent_vector[initial_coord_basis,:] + for comp in initial_tgt_vec_comps: + if isinstance(comp, Expression): + parameters = parameters.union(comp.variables()) + + # check at this stage that no parameter coincides with a + # coordinate, a velocity, or the curve parameter; this would + # mean that an Expression used to denote either a bound, a + # coordinate of the initial point or a component of the initial + # tangent vector coincides with a coordinate, a velocity or the + # curve parameter (which would make no sense): + if len(parameters) != 0: + for param in parameters: + if param in announced_variables: + str_error = "{} should not be used ".format(param) + str_error += "as a parameter since it also denotes " + str_error += "a coordinate, a velocity or the " + str_error += "curve parameter." + raise ValueError(str_error) + + # define all attributes self._equations_rhs = list(equations_rhs) # converts to list # since might not already be a list (which is later required) self._velocities = list(velocities) # converts to list @@ -428,6 +482,9 @@ def __init__(self, parent, equations_rhs, velocities, self._initial_tangent_vector = initial_tangent_vector self._chart = chart self._parameters = parameters + self._ode_solver = None # if needed, becomes an instance of + # 'ode_solver', which performs most of the numerical integrations + # offered by method 'solve' self._solutions = {} # dictionary containing all numerically # computed lists of points of the curve, the keys being chosen # by the user when calling method 'solve' @@ -437,9 +494,19 @@ def __init__(self, parent, equations_rhs, velocities, # and the keys being chosen by the user when calling # method 'interpolate' + if verbose: + print("The curve was correctly set.") + if len(self._parameters) != 0: + print("Parameters appearing in the differential " + + "system defining the curve are " + + "{}.".format(self._parameters)) + else: + print("No parameter appears in the differential " + + "system defining the curve.") + def _repr_(self): r""" - Returns a string representation of ``self``. + Return a string representation of ``self``. TESTS:: @@ -448,16 +515,16 @@ def _repr_(self): sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: parameters=[B_0, m, q, L, T]); c + ....: verbose=False) ; c Integrated curve in the 3-dimensional differentiable manifold M sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]); c + ....: name='c', verbose=False) ; c Integrated curve c in the 3-dimensional differentiable manifold M @@ -469,9 +536,58 @@ def _repr_(self): description += "in the {}".format(self._codomain) return description + def __reduce__(self): + r""" + Reduction function for the pickle protocole. + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') + sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) + sage: D = X.symbolic_velocities() + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) + sage: c.__reduce__() + (, + (Set of Morphisms from Real interval (0, 5) to + 3-dimensional differentiable manifold M in Category of + homsets of subobjects of sets and topological spaces which + actually are integrated curves, + [B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m), + -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m), + 0], + [Dx1, Dx2, Dx3], + t, + Tangent vector at Point p on the 3-dimensional differentiable manifold M, + Chart (M, (x1, x2, x3)), + 'c', + 'c', + False, + False)) + + Test of pickling:: + + sage: loads(dumps(c)) + Integrated curve c in the 3-dimensional differentiable + manifold M + + """ + + return (type(self), (self.parent(), self._equations_rhs, + self._velocities, self._curve_parameter, + self._initial_tangent_vector, self._chart, + self._name, self._latex_name, self._is_isomorphism, + self._is_identity)) + def system(self, verbose=True): r""" - Provides a detailed description of the system defining the curve + Provide a detailed description of the system defining the curve and returns the system defining it: chart, equations and initial conditions. @@ -485,19 +601,21 @@ def system(self, verbose=True): - list containing the attributes :attr:`equations_rhs`, :attr:`initial_tangent_vector` and :attr:`chart` - TESTS:: + EXAMPLE: + + System defining an integrated curve:: sage: M = Manifold(3, 'M') sage: X. = M.chart() sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) sage: sys = c.system() Curve c in the 3-dimensional differentiable manifold M integrated over the Real interval (0, 5) as a solution to @@ -512,10 +630,10 @@ def system(self, verbose=True): components [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) d(x1)/dt = Dx1 - d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) d(x2)/dt = Dx2 - d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) d(x3)/dt = Dx3 + d(Dx1)/dt = B_0*Dx2*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) + d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) d(Dx3)/dt = 0 sage: sys_mute = c.system(verbose=False) @@ -538,7 +656,7 @@ def system(self, verbose=True): # known initial_coord_basis = chart.frame().at(initial_pt) - initial_tgt_vec_comps = v0[:, initial_coord_basis] # will + initial_tgt_vec_comps = v0[initial_coord_basis,:] # will # raise error if components in coordinate basis are not # known @@ -562,30 +680,186 @@ def system(self, verbose=True): description +="{}".format(initial_tgt_vec_comps) description += " w.r.t. {}\n\n".format(chart) - zip_sys = zip(chart[:],self._velocities,self._equations_rhs) - for coord_func, velocity, eqn in zip_sys: + for coord_func,velocity in zip(chart[:],self._velocities): description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) + + for velocity,eqn in zip(self._velocities,self._equations_rhs): description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) + print(description) return [self._equations_rhs, v0, chart] - def solve(self, step=0.1, method=None, solution_key=None, + def solve_analytical(self, verbose=True): + r""" + Solve analytically the differential system defining the curve + using Maxima via Sage solver ``desolve_system``. + In case of success, the analytical expressions are added to the + dictionnary of expressions representing the curve. + Pay attention to the fact that ``desolve_system`` only considers + initial conditions given at an initial parameter value equal to + zero, although the parameter range may not contain zero. + Yet, assuming that it does, values of the coordinates functions + at such zero initial parameter value are denoted by the name of + the coordinate function followed by the string "_0". + + OUTPUT: + + - list of the analytical expressions of the coordinate functions + (when the differential system could be solved analytically), + or boolean 'FALSE' (in case the differential system could not + be solved analytically) + + EXAMPLE: + + Analytical expression of the trajectory of a charged particle in + a uniform, stationnary magnetic field:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: [t, B_0, m, q] = var('t B_0 m q') + sage: D = X.symbolic_velocities() + sage: eqns = [q*B_0/m*D[1], -q*B_0/m*D[0], 0] + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) + sage: sys = c.system() + Curve c in the 3-dimensional differentiable manifold M + integrated over the Real interval (0, 5) as a solution to + the following system, written w.r.t. + Chart (M, (x1, x2, x3)): + + Initial point: Point p on the 3-dimensional differentiable + manifold M with coordinates [0, 0, 0] w.r.t. + Chart (M, (x1, x2, x3)) + Initial tangent vector: Tangent vector at Point p on the + 3-dimensional differentiable manifold M with components + [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) + + d(x1)/dt = Dx1 + d(x2)/dt = Dx2 + d(x3)/dt = Dx3 + d(Dx1)/dt = B_0*Dx2*q/m + d(Dx2)/dt = -B_0*Dx1*q/m + d(Dx3)/dt = 0 + + sage: sol = c.solve_analytical(verbose=False) + sage: c.expr() + ((B_0*q*x1_0 - Dx2_0*m*cos(B_0*q*t/m) + + Dx1_0*m*sin(B_0*q*t/m) + Dx2_0*m)/(B_0*q), + (B_0*q*x2_0 + Dx1_0*m*cos(B_0*q*t/m) + + Dx2_0*m*sin(B_0*q*t/m) - Dx1_0*m)/(B_0*q), + Dx3_0*t + x3_0) + + """ + + from sage.calculus.var import function + from sage.calculus.functional import diff + from sage.calculus.desolvers import desolve_system + from sage.symbolic.assumptions import assume, forget + from sage.symbolic.ring import var + + dim = self.codomain().dim() + i0 = self.codomain().start_index() + des = self._velocities + self._equations_rhs + par = self._curve_parameter + + if len(self._parameters) != 0: + for param in self._parameters: + assume(param != 0) + + y = [] + for i in range(2*dim): + name = "y{}".format(i+i0) + y += [function(name)(par)] + + for i in range(dim): + vel = self._velocities[i] + des[i] = des[i].substitute({vel:y[dim+i]}) + des[i] = diff(y[i],par) == des[i] + for j in range(dim): + coord = self._chart[:][j] # important to use '[:]' on + # 'chart' to avoid problems due to non zero starting + # index (i0) + veloc = self._velocities[j] + des[dim+i]=des[dim+i].substitute({coord:y[j]}) + des[dim+i]=des[dim+i].substitute({veloc:y[dim+j]}) + des[dim+i] = diff(y[dim+i],par) == des[dim+i] + + dvars = y + ics = [0] + y_ics_first_half = [] + y_ics_second_half = [] + for i in range(dim): + coord = self._chart[:][i] # important to use '[:]' + # on 'chart' to avoid problems due to non zero + # starting index (i0) + veloc = self._velocities[i] + str_var_coord = "{}_0".format(coord) + str_var_veloc = "{}_0".format(veloc) + y_coord_0 = var(str_var_coord) + y_veloc_0 = var(str_var_veloc) + y_ics_first_half += [y_coord_0] + y_ics_second_half += [y_veloc_0] + ics += y_ics_first_half + y_ics_second_half + + try: + sol = desolve_system(des, dvars, ivar=self._curve_parameter, + ics=ics) + except NotImplementedError: + coords_sol_expr = False + if verbose: + print("The system could not be solved analytically.") + else: + coords_sol_expr = [] + for relation in sol[0:dim]: + expr = relation.rhs().simplify_full() + coords_sol_expr += [expr] + self.add_expr(self.domain().default_chart(), self._chart, + coords_sol_expr) + + if len(self._parameters) != 0: + for param in self._parameters: + forget(param != 0) + + return tuple(coords_sol_expr) + + def solve(self, step=None, method=None, solution_key=None, parameters_values=None, verbose=True): r""" - Integrates the curve numerically over the domain of integration. + Integrate the curve numerically over the domain of integration. INPUT: - ``step`` -- (default: ``0.1``) step of integration - ``method`` -- (default: ``None``) numerical scheme to use for - the integration of the curve; algorithms available are - - * 'rk4', which uses Sage solver ``desolve_system_rk4`` + the integration of the curve; algorithms available are: + + * 'rk4_maxima' - 4th order classical Runge-Kutta, which makes + use of Maxima's dynamics package via Sage solver + ``desolve_system_rk4`` + * 'ode_int' - makes use of ``odeint`` from scipy.integrate + module via Sage solver ``desolve_odeint`` + + and those provided by ``GSL`` via Sage class + :class:`~sage.calculus.ode.ode_solver`: + + * 'rk2' - embedded Runge-Kutta (2,3) + * 'rk4' - 4th order classical Runge-Kutta + * 'rkf45' - Runge-Kutta-Felhberg (4,5) + * 'rkck' - embedded Runge-Kutta-Cash-Karp (4,5) + * 'rk8pd' - Runge-Kutta prince-dormand (8,9) + * 'rk2imp' - implicit 2nd order Runge-Kutta at Gaussian points + * 'rk4imp' - implicit 4th order Runge-Kutta at Gaussian points + * 'gear1' - M=1 implicit Gear + * 'gear2' - M=2 implicit Gear + * 'bsimp' - implicit Burlisch-Stoer (requires Jacobian) - ``solution_key`` -- (default: ``None``) key which the resulting numerical solution will be associated to ; a default @@ -600,24 +874,26 @@ def solve(self, step=0.1, method=None, solution_key=None, - list of the numerical points of the solution computed - TESTS:: + EXAMPLE: + + Computing a numerical solution:: sage: M = Manifold(3, 'M') sage: X. = M.chart() sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) sage: sol = c.solve(parameters_values={m:1, q:1, L:10, T:1}) Traceback (most recent call last): ... ValueError: Numerical values should be provided for each of - the parameters [B_0, m, q, L, T]. + the parameters set([B_0, m, q, L, T]). sage: sol = c.solve(method='my method', ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) Traceback (most recent call last): @@ -626,13 +902,18 @@ def solve(self, step=0.1, method=None, solution_key=None, as 'my method'. sage: sol = c.solve( ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) - Performing 4th order Runge-Kutta integration by default... - Resulting list of points will be associated with the key - 'rk4' by default. - Numerical integration completed. Resulting list of points - was associated with the key 'rk4' (if this key already - referred to a former numerical solution, such a solution - was erased). + Performing 4th order Runge-Kutta integration with Maxima by + default... + Resulting list of points will be associated with the key + 'rk4_maxima' by default. + + Numerical integration completed. + Checking all points are in the chart domain... + + All points are in the chart domain. + The resulting list of points was associated with the key + 'rk4_maxima' (if this key already referred to a former + numerical solution, such a solution was erased). sage: sol_mute = c.solve(verbose=False, ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) sage: sol_mute == sol @@ -640,11 +921,13 @@ def solve(self, step=0.1, method=None, solution_key=None, """ + from sage.symbolic.ring import SR + if method is None: - method = 'rk4' + method = 'rk4_maxima' if verbose: print("Performing 4th order Runge-Kutta integration " + - "by default...") + "with Maxima by default...") if solution_key is None: solution_key = method @@ -653,115 +936,270 @@ def solve(self, step=0.1, method=None, solution_key=None, "with the key '{}' ".format(solution_key) + "by default.") - if method == 'rk4': - t_min = self.domain().lower_bound() - t_max = self.domain().upper_bound() + t_min = self.domain().lower_bound() + t_max = self.domain().upper_bound() - eqns_rhs = self._equations_rhs + eqns_rhs = self._equations_rhs - v0 = self._initial_tangent_vector - chart = self._chart - - initial_tgt_space = v0.parent() - initial_pt = initial_tgt_space.base_point() # retrieves - # the initial point as the base point of the tangent space - # to which initial tangent vector belongs - initial_pt_coords = list(initial_pt.coordinates(chart)) - # previous line converts to list since would otherwise be a - # tuple (yet will need to be added to [t_min] later); will - # raise error if coordinates in chart cannot be obtained + v0 = self._initial_tangent_vector + chart = self._chart + initial_tgt_space = v0.parent() + initial_pt = initial_tgt_space.base_point() # retrieves + # the initial point as the base point of the tangent space + # to which the initial tangent vector belongs + initial_pt_coords = list(initial_pt.coordinates(chart)) + # previous line converts to list since would otherwise be a + # tuple (yet might need to be added to [t_min] later); will + # raise error if coordinates in chart cannot be obtained + + initial_coord_basis = chart.frame().at(initial_pt) + initial_tgt_vec_comps = list(v0[initial_coord_basis,:])#idem + + dim = self.codomain().dim() + + if len(self._parameters) != 0: + if parameters_values is None or len(parameters_values)!=len(self._parameters): + raise ValueError("Numerical values should be " + + "provided for each of the " + + "parameters " + "{}.".format(self._parameters)) + for key in parameters_values.keys(): + parameters_values[key]=numerical_approx(parameters_values[key])#gets + # numerical values in case some parameters values + # contain expressions such as pi; will raise error if + # any element of parameters_values is not numerical + + if isinstance(t_min, Expression): + t_min = parameters_values[t_min] + if t_min == -Infinity or t_min == +Infinity: + raise ValueError("Both boundaries of the " + + "interval need to be finite.") + + if isinstance(t_max, Expression): + t_max = parameters_values[t_max] + if t_max == -Infinity or t_max == +Infinity: + raise ValueError("Both boundaries of the " + + "interval need to be finite.") - initial_coord_basis = chart.frame().at(initial_pt) - initial_tgt_vec_comps = list(v0[:,initial_coord_basis])#idem - - if self._parameters is not None: - if parameters_values is None or len(parameters_values)!=len(self._parameters): - raise ValueError("Numerical values should be " + - "provided for each of the " + - "parameters " - "{}.".format(self._parameters)) - for key in parameters_values.keys(): - parameters_values[key]=numerical_approx(parameters_values[key])#gets - # numerical values in case some parameters values - # contain expressions such as pi; will raise error if - # any element of parameters_values is not numerical - - if isinstance(t_min, Expression): - t_min=t_min.substitute(parameters_values) - if t_min==-Infinity or t_min==+Infinity: - raise ValueError("Both boundaries of the " + - "interval need to be finite.") - - if isinstance(t_max, Expression): - t_max=t_max.substitute(parameters_values) - if t_max==-Infinity or t_max==+Infinity: - raise ValueError("Both boundaries of the " + - "interval need to be finite.") - - eqns_rhs=[eqn.substitute(parameters_values) for eqn - in eqns_rhs] - - - - for i in range(len(initial_pt_coords)): - if isinstance(initial_pt_coords[i],Expression): - AUX = initial_pt_coords[i] - AUX = AUX.substitute(parameters_values) - initial_pt_coords[i] = AUX - # 'AUX' only used for the lines of - # source code to be shorter - - - for i in range(len(initial_tgt_vec_comps)): - if isinstance(initial_tgt_vec_comps[i],Expression): - AUX = initial_tgt_vec_comps[i] - AUX = AUX.substitute(parameters_values) - initial_tgt_vec_comps[i] = AUX - # 'AUX' only used for the lines of - # source code to be shorter - - t_min = numerical_approx(t_min) - t_max = numerical_approx(t_max) - initial_pt_coords = [numerical_approx(coord) for coord - in initial_pt_coords] - initial_tgt_vec_comps = [numerical_approx(comp) for comp - in initial_tgt_vec_comps] - # the last two instructions retrieve numerical values even - # if no parameters had to be substituted, in case some - # coordinates or components contain expressions such as pi, - # or are not RealNumber, since variable 'ics' of - # 'desolve_system_rk4' used below needs to be a list of - # RealNumber + for i in range(dim): + if isinstance(eqns_rhs[i], Expression): # some right + # hand sides might merely be real numbers and not + # expressions, so that they do not contain any variable, + # and method 'variables' could not be called on them + eqns_rhs[i]=eqns_rhs[i].substitute(parameters_values) + for i in range(dim): + if isinstance(initial_pt_coords[i],Expression): + AUX = initial_pt_coords[i] + AUX = AUX.substitute(parameters_values) + initial_pt_coords[i] = AUX + if isinstance(initial_tgt_vec_comps[i],Expression): + AUX2 = initial_tgt_vec_comps[i] + AUX2 = AUX2.substitute(parameters_values) + initial_tgt_vec_comps[i] = AUX2 + # 'AUX' and 'AUX2' only used for the lines of + # source code to be shorter + + t_min = numerical_approx(t_min) + t_max = numerical_approx(t_max) + + for i in range(dim): + if not isinstance(eqns_rhs[i], Expression): # in case of a + # right hand side that is not an Expression (and then is a + # number), it is needed to be converted to an Expression + # since some solvers called below require only expressions + eqns_rhs[i] = SR(eqns_rhs[i]) + + if step is None: + step = (t_max - t_min)/100 + + initial_pt_coords = [numerical_approx(coord) for coord + in initial_pt_coords] + initial_tgt_vec_comps = [numerical_approx(comp) for comp + in initial_tgt_vec_comps] + # the last two instructions retrieve numerical values even + # if no parameters had to be substituted, in case some + # coordinates or components contain expressions such as pi, + # or are not RealNumber, since variable 'ics' of + # 'desolve_system_rk4' used below needs to be a list of + # RealNumber + + if not chart.valid_coordinates(*initial_pt_coords): + raise ValueError("Initial point should be in the " + + "domain of the chart.") + + ode_solver_methods = ["rk2","rk4","rkf45","rkck","rk8pd"] + ode_solver_methods+= ["rk2imp","rk4imp","gear1","gear2","bsimp"] + + if method == 'rk4_maxima': + des = self._velocities + eqns_rhs + dvars = list(chart[:]) + self._velocities ics = [t_min] + initial_pt_coords + initial_tgt_vec_comps - sol = desolve_system_rk4(self._velocities + eqns_rhs, - list(chart[:]) + self._velocities, - ivar = self._curve_parameter, - ics = ics, + sol = desolve_system_rk4(des, dvars, + ivar=self._curve_parameter, + ics=ics, end_points=[t_min, t_max], step=step) + elif method == "ode_int": + des = self._velocities + eqns_rhs + ics = initial_pt_coords + initial_tgt_vec_comps + times = srange(t_min, t_max, step, include_endpoint=True) + dvars = list(chart[:]) + self._velocities + + sol0 = desolve_odeint(des, ics, times, dvars, + ivar=self._curve_parameter) + + # rewrite the solution to prepare for the extraction (which + # removes information about the velocities), and convert + # elements of type 'numpy.float64' to standard type 'float' + sol = [] + for t, coords_array in zip(times, sol0): + coords_values = [float(coord_value) for coord_value + in coords_array ] + sol += [ [t] + coords_values ] + elif method in ode_solver_methods: + T = self._ode_solver + + if T is None: + def system(t,y): + syst = self._velocities + eqns_rhs + par = self._curve_parameter + for i in range(dim): + vel = self._velocities[i] + syst[i] = syst[i].substitute({vel:y[dim+i]}) + syst[dim+i] = syst[dim+i].substitute({par:t}) + for j in range(dim): + coord = chart[:][j] # important to use '[:]' + # on 'chart' to avoid problems due to non + # zero starting index (i0) + veloc = self._velocities[j] + syst[dim+i]=syst[dim+i].substitute({coord:y[j]}) + syst[dim+i]=syst[dim+i].substitute({veloc:y[dim+j]}) + return syst + from sage.calculus.ode import ode_solver + T = ode_solver(function=system) + + T.algorithm = method + y_0 = initial_pt_coords + initial_tgt_vec_comps + t_span = srange(t_min, t_max, step, include_endpoint=True) + + if method == "bsimp": + # this method requires the expression of the Jacobian + # matrix of the application defining the right-hand side + # of the system to be provided + + if T.jacobian is None: + def jacobian(t,y): + jac = [] + par = self._curve_parameter + for i in range(dim): + new_row = [0 for j in range(2*dim)] + new_row[dim + i] = 1 + jac += [new_row] + + for i in range(dim): + semi_row_coords = [] + semi_row_vels = [] + for j in range(dim): + coord = chart[:][j] # important to use + # '[:]' on 'chart' to avoid problems due + # to non zero starting index (i0) + vel = self._velocities[j] + AUX = eqns_rhs[i].derivative(coord) + AUX2 = eqns_rhs[i].derivative(vel) + AUX = AUX.substitute({par:t}) + AUX2 = AUX2.substitute({par:t}) + for k in range(dim): + coordin = chart[:][k] # important to + # use '[:]' on 'chart' to avoid + # problems due to non zero starting + # index (i0) + veloc = self._velocities[k] + AUX = AUX.substitute({coordin:y[k]}) + AUX = AUX.substitute({veloc:y[dim+k]}) + AUX2 = AUX2.substitute({coordin:y[k]}) + AUX2 = AUX2.substitute({veloc:y[dim+k]}) + semi_row_coords += [AUX] + semi_row_vels += [AUX2] + jac += [semi_row_coords + semi_row_vels] + + last_semi_row_coords = [0 for j in range(dim)] + last_semi_row_vels = [] + for j in range(dim): + AUX3 = eqns_rhs[j].derivative(par) + AUX3 = AUX3.substitute({par:t}) + for m in range(dim): + coordin = chart[:][m] # important to use + # '[:]' on 'chart' to avoid problems due + # to non zero starting index (i0) + veloc = self._velocities[m] + AUX3 = AUX3.substitute({coordin:y[m]}) + AUX3 = AUX3.substitute({veloc:y[dim+m]}) + last_semi_row_vels += [AUX3] + jac += [last_semi_row_coords + last_semi_row_vels] + # 'AUX', 'AUX2' and 'AUX3' only used for the lines + # of source code to be shorter + return jac + T.jacobian = jacobian + + T.ode_solve(jacobian=jacobian, y_0=y_0, t_span=t_span) + else: + T.ode_solve(y_0=y_0, t_span=t_span) + + sol0 = T.solution + sol = [] + for point in sol0: + sol += [[point[0]] + point[1]] + # above loop rewrites the solution in the same form than + # that provided by other methods ('rk4_maxima' and + # 'ode_int'), in order to extract the time and corresponding + # coordinate values a few lines below, in the same way for + # all methods - dim = self.codomain().dim() - self._solutions[solution_key] = [point[0:dim+1] for point - in sol] else: raise ValueError("No available method of integration " + "referred to as '{}'.".format(method)) - if verbose: - print("Numerical integration completed. " + - "Resulting list of points was associated with the key " + - "'{}' ".format(solution_key) + - "(if this key already referred to a former numerical " + - "solution, such a solution was erased).") + # eventually, extract the time and corresponding coordinate + # values from each point of the solution computed (thus removing + # information about the values of the velocities ; should the + # latter be conserved ? They could turn useful in method + # 'tangent_vector_eval_at', and in 'plot' when plotting the + # tangent vectors.) + coords_sol = [point[0:dim+1] for point in sol] - return self._solutions[solution_key] + if verbose: + print("\nNumerical integration completed.\n" + + "Checking all points are in the chart domain...") + + N = len(coords_sol) + n = 0 + while n < N and chart.valid_coordinates(*coords_sol[n][1:dim+1]): + n += 1 + + if n < N: + raise ValueError("The {}th point ".format(n) + + "of the numerical solution (obtained at " + + "time {}) is out ".format(t_min + n*step) + + "of the chart domain. A curve with a " + + "smaller maximal value of the curve " + + "parameter, or a smaller initial tangent "+ + "vector might be considered.") + else: + self._solutions[solution_key] = coords_sol + if verbose: + print("\nAll points are in the chart domain.\n" + + "The resulting list of points was associated " + + "with the key '{}' ".format(solution_key) + + "(if this key already referred to a former " + + "numerical solution, such a solution was erased).") + return self._solutions[solution_key] def solution(self, solution_key=None, verbose=True): r""" - Returns the solution (list of points) associated with the given + Return the solution (list of points) associated with the given key. INPUT: @@ -776,19 +1214,21 @@ def solution(self, solution_key=None, verbose=True): - list of the numerical points of the solution requested - TESTS:: + EXAMPLE: + + Requesting a numerical solution previously computed:: sage: M = Manifold(3, 'M') sage: X. = M.chart() sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) sage: sol = c.solve(verbose=False, ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, ....: solution_key='sol_T1') @@ -807,8 +1247,8 @@ def solution(self, solution_key=None, verbose=True): """ if solution_key is None: - if 'rk4' in self._solutions.keys(): - solution_key = 'rk4' + if 'rk4_maxima' in self._solutions.keys(): + solution_key = 'rk4_maxima' else: solution_key = self._solutions.keys()[0] # will raise # error if self._solutions empty @@ -826,7 +1266,7 @@ def solution(self, solution_key=None, verbose=True): def interpolate(self, solution_key=None, method=None, interpolation_key=None, verbose=True): r""" - Interpolates the chosen numerical solution using the given + Interpolate the chosen numerical solution using the given interpolation method. INPUT: @@ -837,7 +1277,7 @@ def interpolate(self, solution_key=None, method=None, - ``method`` -- (default: ``None``) interpolation scheme to use; algorithms available are - * 'cubic spline', which uses ``GSL`` via Sage class + * 'cubic spline', which makes use of ``GSL`` via Sage class :class:`~sage.calculus.interpolation.Spline` - ``interpolation_key`` -- (default: ``None``) key which the @@ -850,20 +1290,23 @@ def interpolate(self, solution_key=None, method=None, - built interpolation object - TESTS:: + EXAMPLE: + + Interpolating a numerical solution previously computed:: sage: M = Manifold(3, 'M') sage: X. = M.chart() sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) - sage: sol = c.solve(method='rk4', solution_key='sol_T1', + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) + sage: sol = c.solve(method='rk4_maxima', + ....: solution_key='sol_T1', ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, ....: verbose=False) sage: interp = c.interpolate(solution_key='my solution') @@ -897,8 +1340,8 @@ def interpolate(self, solution_key=None, method=None, """ if solution_key is None: - if 'rk4' in self._solutions.keys(): - solution_key = 'rk4' + if 'rk4_maxima' in self._solutions.keys(): + solution_key = 'rk4_maxima' else: solution_key = self._solutions.keys()[0] # will raise # error if self._solutions empty @@ -948,7 +1391,7 @@ def interpolate(self, solution_key=None, method=None, def interpolation(self, interpolation_key=None, verbose=True): r""" - Returns the interpolation object associated with the given key. + Return the interpolation object associated with the given key. INPUT: @@ -962,20 +1405,23 @@ def interpolation(self, interpolation_key=None, verbose=True): - requested interpolation object - TESTS:: + EXAMPLE: + + Requesting an interpolation object previously computed:: sage: M = Manifold(3, 'M') sage: X. = M.chart() sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) - sage: sol = c.solve(method='rk4', solution_key='sol_T1', + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) + sage: sol = c.solve(method='rk4_maxima', + ....: solution_key='sol_T1', ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, ....: verbose=False) sage: interp = c.interpolate(method='cubic spline', @@ -1017,7 +1463,7 @@ def interpolation(self, interpolation_key=None, verbose=True): def __call__(self, t, interpolation_key=None, verbose=True): r""" - Returns the image of the curve for the given value of the curve + Return the image of the curve for the given value of the curve parameter, using the chosen interpolation. INPUT: @@ -1041,13 +1487,14 @@ def __call__(self, t, interpolation_key=None, sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) - sage: sol = c.solve(method='rk4', solution_key='sol_T1', + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) + sage: sol = c.solve(method='rk4_maxima', + ....: solution_key='sol_T1', ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, ....: verbose=False) sage: interp = c.interpolate(method='cubic spline', @@ -1061,9 +1508,9 @@ def __call__(self, t, interpolation_key=None, sage: c(1.1) Evaluating point coordinates from the interpolation associated with the key 'interp_T1' by default... - [1.060743431308544, -0.2153838226258469, 1.1] + [1.060743343394347, -0.2153835404373033, 1.1] sage: pt = c(1.1, verbose=False); pt - [1.060743431308544, -0.2153838226258469, 1.1] + [1.060743343394347, -0.2153835404373033, 1.1] """ @@ -1096,7 +1543,7 @@ def __call__(self, t, interpolation_key=None, def tangent_vector_eval_at(self, t, interpolation_key=None, verbose=True): r""" - Returns the vector tangent to the curve at the given curve + Return the vector tangent to the curve at the given curve parameter with components evaluated from the given interpolation. @@ -1115,20 +1562,23 @@ def tangent_vector_eval_at(self, t, - :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` tangent vector with numerical components - TESTS:: + EXAMPLE: + + Evaluating a vector tangent to the curve:: sage: M = Manifold(3, 'M') sage: X. = M.chart() sage: [t, B_0, m, q, L, T] = var('t B_0 m q L T') sage: B = B_0*t/T*exp(-(x1^2 + x2^2)/L^2) sage: D = X.symbolic_velocities() - sage: eqns = [q*B/m*D[1], -q*B/m*D[0], SR(0)] + sage: eqns = [q*B/m*D[1], -q*B/m*D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', parameters=[B_0, m, q, L, T]) - sage: sol = c.solve(method='rk4', solution_key='sol_T1', + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', + ....: verbose=False) + sage: sol = c.solve(method='rk4_maxima', + ....: solution_key='sol_T1', ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, ....: verbose=False) sage: interp = c.interpolate(method='cubic spline', @@ -1146,8 +1596,7 @@ def tangent_vector_eval_at(self, t, Tangent vector at Point on the 3-dimensional differentiable manifold M sage: tg_vec[:] - [0.7392716344834512, -0.6734470583131389, - 0.9999999999999999] + [0.7392639473853356, -0.6734182305341726, 1.0000000000000007] sage: tg_vec_mute = c.tangent_vector_eval_at(1.22, ....: verbose=False, ....: interpolation_key='interp_T1') @@ -1194,20 +1643,20 @@ def tangent_vector_eval_at(self, t, @options(thickness=1, width_tangent=1, plot_points=75, aspect_ratio='automatic', plot_points_tangent=10, scale=1) - def plot(self, chart=None, ambient_coords=None, mapping=None, - prange=None, interpolation_key=None, + def plot_integrated(self, chart=None, ambient_coords=None, + mapping=None, prange=None, interpolation_key=None, include_end_point=(True, True), end_point_offset=(0.001, 0.001), verbose=True, color='red', style='-', label_axes=True, display_tangent=False, color_tangent='blue', **kwds): r""" - Plots the 2D or 3D projection of the curve onto the space of the + Plot the 2D or 3D projection of the curve onto the space of the chosen two or three ambient coordinates, based on the interpolation of a numerical solution previously computed. .. SEEALSO:: - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve.plot` + :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve.plot` for complete information about the input. ADDITIONAL INPUT: @@ -1228,20 +1677,21 @@ def plot(self, chart=None, ambient_coords=None, mapping=None, sage: var('t') t sage: D = X.symbolic_velocities() - sage: eqns = [D[1], -D[0], SR(0)] + sage: eqns = [D[1], -D[0], 0] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 6), v,name='c') + sage: c = M.integrated_curve(eqns, D, (t, 0, 6), v, + ....: name='c', verbose=False) sage: sol = c.solve(verbose=False) sage: interp = c.interpolate(verbose=False) - sage: c_plot_2d = c.plot(ambient_coords=[x1, x2], + sage: c_plot_2d = c.plot_integrated(ambient_coords=[x1, x2], ....: thickness=2.5, ....: display_tangent=True, plot_points=200, ....: plot_points_tangent=10, scale=0.5, ....: color='blue', color_tangent='red') Plotting from the interpolation associated with the key - 'cubic spline-interp-rk4' by default... + 'cubic spline-interp-rk4_maxima' by default... A tiny final offset equal to the value of 'end_point_offset[1]' (= 0.001) was introduced in order to safely compute the last tangent vector from the @@ -1254,14 +1704,15 @@ def plot(self, chart=None, ambient_coords=None, mapping=None, X = M.chart('x1 x2 x3') var('x1 x2 x3 t') D = X.symbolic_velocities() - eqns = [D[1], -D[0], SR(0)] + eqns = [D[1], -D[0], 0] p = M.point((0,0,0), name='p') Tp = M.tangent_space(p) v = Tp((1,0,1)) - c = M.integrated_curve(eqns, D, (t, 0, 6), v, name='c') + c = M.integrated_curve(eqns, D, (t, 0, 6), v, name='c', + ....: verbose=False) c.solve(verbose=False) c.interpolate(verbose=False) - c_plot_2d_1 = c.plot(ambient_coords=[x1, x2], + c_plot_2d_1 = c.plot_integrated(ambient_coords=[x1, x2], thickness=2.5, display_tangent=True, plot_points=200, plot_points_tangent=10, scale=0.5, @@ -1599,7 +2050,7 @@ def plot(self, chart=None, ambient_coords=None, mapping=None, class IntegratedAutoparallelCurve(IntegratedCurve): r""" - Constructs a numerical autoparallel curve on the manifold with + Numerical autoparallel curve on the manifold with respect to a given affine connection. INPUT: @@ -1612,16 +2063,15 @@ class IntegratedAutoparallelCurve(IntegratedCurve): affine connection with respect to which the curve is autoparallel - ``curve_parameter`` -- symbolic expression to be used as the - parameter of the curve + parameter of the curve (the equations defining an instance of + IntegratedAutoparallelCurve are such that ``t`` will actually be + an affine parameter of the curve) - ``initial_tangent_vector`` -- :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` initial tangent vector of the curve - - ``chart`` -- (default: ``None``) chart on the manifold in - which the equations are given; if ``None`` the default chart + - ``chart`` -- (default: ``None``) chart on the manifold in terms of + which the equations are expressed; if ``None`` the default chart of the manifold is assumed - - ``parameters`` -- list of the symbolic expressions used in the - coefficients of ``affine_connection`` other than the - coordinates associated with the chart - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used @@ -1654,14 +2104,14 @@ class IntegratedAutoparallelCurve(IntegratedCurve): corresponding coordinate frame :MATH:`(e_{\theta}, e_{\phi})`:: sage: S2 = Manifold(2, 'S^2', start_index=1) - sage: polar.=S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + sage: polar.=S2.chart() sage: epolar = polar.frame() Normalizing :MATH:`e_{\phi}` provides an orthonormal basis:: sage: ch_basis = S2.automorphism_field() sage: ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) - sage: epolar_ON=S2.default_frame().new_frame(ch_basis,'epolar_ON') + sage: epolar_ON = epolar.new_frame(ch_basis,'epolar_ON') Denote :MATH:`(\hat{e}_{\theta}, \hat{e}_{\phi})` such an orthonormal frame field. @@ -1704,13 +2154,26 @@ class IntegratedAutoparallelCurve(IntegratedCurve): sage: Tp = S2.tangent_space(p) sage: v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) - Declare the corresponding integrated autoparallel curve and display + Note here that the components ``(v\_th0, v\_ph0)`` of the initial + tangent vector ``v`` refer to the basis + ``epolar\_ON`` :MATH:` = (\hat{e}_{\theta}, \hat{e}_{\phi})` + and not the coordinate basis + ``epolar`` :MATH:` = (e_{\theta}, e_{\phi})`. + This is merely to help picture the aspect of the tangent vector in + the usual embedding of :MATH:`\mathbb{S}^{2}` in + :MATH:`\mathbb{R}^{3}` thanks to using an orthonormal frame, + since providing the components w.r.t. the coordinate basis would + require mutliplying the second component (i.e. in :MATH:`\phi`) in + order to picture the vector in the same way. + This subtlety will need to be taken into account later when the + numerical curve will be compared to the analytical solution. + + Now, declare the corresponding integrated autoparallel curve and display the differential system it satisfies:: sage: [t, tmin, tmax] = var('t tmin tmax') sage: c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), - ....: v, chart=polar, - ....: parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0],name='c') + ....: v, chart=polar, name='c', verbose=False) sage: sys = c.system() Autoparallel curve c in the 2-dimensional differentiable manifold S^2 equipped with Affine connection nab on the @@ -1726,8 +2189,8 @@ class IntegratedAutoparallelCurve(IntegratedCurve): components [v_th0, v_ph0/sin(th0)] w.r.t. Chart (S^2, (th, ph)) d(th)/dt = Dth - d(Dth)/dt = 0 d(ph)/dt = Dph + d(Dth)/dt = 0 d(Dph)/dt = -Dph*Dth*cos(th)/sin(th) @@ -1763,7 +2226,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): ....: parameters_values=dict_params[key], verbose=False) ....: interp = c.interpolate(solution_key='sol-'+key, ....: interpolation_key='interp-'+key, verbose=False) - ....: graph2D_mercator+=c.plot(interpolation_key='interp-'+key, + ....: graph2D_mercator+=c.plot_integrated(interpolation_key='interp-'+key, ....: chart=mercator, thickness=2, ....: verbose=False) @@ -1777,12 +2240,12 @@ class IntegratedAutoparallelCurve(IntegratedCurve): .. PLOT:: S2 = Manifold(2, 'S^2', start_index=1) - polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + polar = S2.chart('th ph') [th, ph] = var('th ph') epolar = polar.frame() ch_basis = S2.automorphism_field() ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) - epolar_ON = S2.default_frame().new_frame(ch_basis, 'e') + epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') nab.set_coef(epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') @@ -1790,8 +2253,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): Tp = S2.tangent_space(p) v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, - chart=polar,parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], - name='c') + chart=polar, name='c') dict_params={'latit':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:0,v_ph0:1}, 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}} mercator = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta') @@ -1805,7 +2267,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): parameters_values=dict_params[key], verbose=False) c.interpolate(solution_key='sol-'+key, interpolation_key='interp-'+key, verbose=False) - graph2D_mercator += c.plot(interpolation_key='interp-'+key, + graph2D_mercator += c.plot_integrated(interpolation_key='interp-'+key, chart=mercator, thickness=2, verbose=False) graph2D_mercator_coords = mercator.plot(chart=mercator, number_values=8, color='yellow') @@ -1828,7 +2290,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): sage: graph3D_embedded_curves = Graphics() sage: for key in dict_params.keys(): - ....: graph3D_embedded_curves+=c.plot(interpolation_key='interp-'+key, + ....: graph3D_embedded_curves+=c.plot_integrated(interpolation_key='interp-'+key, ....: mapping=euclid_embedding, thickness=5, ....: display_tangent=True, scale=0.4, width_tangent=0.5, ....: verbose=False) @@ -1836,18 +2298,17 @@ class IntegratedAutoparallelCurve(IntegratedCurve): ....: mapping=euclid_embedding, ....: number_values=15, color='yellow') sage: graph=graph3D_embedded_curves+graph3D_embedded_polar_coords - sage: viewer3D = 'threejs' - sage: graph.show(viewer=viewer3D) + sage: graph.show() .. PLOT:: S2 = Manifold(2, 'S^2', start_index=1) - polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + polar = S2.chart('th ph') [th, ph] = var('th ph') epolar = polar.frame() ch_basis = S2.automorphism_field() ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) - epolar_ON = S2.default_frame().new_frame(ch_basis, 'e') + epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') nab.set_coef(epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') @@ -1855,8 +2316,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): Tp = S2.tangent_space(p) v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, - chart=polar,parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], - name='c') + chart=polar, name='c') dict_params={'latit':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:0,v_ph0:1}, 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}} R3 = Manifold(3, 'R3', start_index=1) @@ -1870,7 +2330,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): parameters_values=dict_params[key], verbose=False) c.interpolate(solution_key='sol-'+key, interpolation_key='interp-'+key, verbose=False) - graph3D_embedded_curves+=c.plot(interpolation_key='interp-'+key, + graph3D_embedded_curves+=c.plot_integrated(interpolation_key='interp-'+key, mapping=euclid_embedding, thickness=5, display_tangent=True, scale=0.4, width_tangent=0.5, verbose=False) @@ -1896,7 +2356,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): Plot the resulting curve in the Mercator plane. This generates a straight line, as expected:: - sage: graph2D_mercator_angle_curve=c.plot(interpolation_key='interp-angle', + sage: graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', ....: chart=mercator, thickness=1, display_tangent=True, ....: scale=0.2, width_tangent=0.2, verbose=False) sage: graph2D_mercator_angle_curve.show() @@ -1904,12 +2364,12 @@ class IntegratedAutoparallelCurve(IntegratedCurve): .. PLOT:: S2 = Manifold(2, 'S^2', start_index=1) - polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + polar = S2.chart('th ph') [th, ph] = var('th ph') epolar = polar.frame() ch_basis = S2.automorphism_field() ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) - epolar_ON = S2.default_frame().new_frame(ch_basis, 'e') + epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') nab.set_coef(epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') @@ -1917,8 +2377,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): Tp = S2.tangent_space(p) v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, - chart=polar,parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], - name='c') + chart=polar, name='c') mercator = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta') [xi,ze] = var('xi ze') polar.transition_map(mercator, (log(tan(th/2)), ph)) @@ -1929,29 +2388,29 @@ class IntegratedAutoparallelCurve(IntegratedCurve): verbose=False) interp = c.interpolate(solution_key='sol-angle', interpolation_key='interp-angle', verbose=False) - graph2D_mercator_angle_curve=c.plot(interpolation_key='interp-angle', + graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', chart=mercator, thickness=1, display_tangent=True, scale=0.2, width_tangent=0.2, verbose=False) sphinx_plot(graph2D_mercator_angle_curve) One may eventually plot such a curve on :MATH:`\mathbb{S}^{2}`:: - sage: graph3D_embedded_angle_curve=c.plot(interpolation_key='interp-angle', + sage: graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle', ....: mapping=euclid_embedding, thickness=5, ....: display_tangent=True, scale=0.1, width_tangent=0.5, ....: verbose=False) sage: graph=graph3D_embedded_angle_curve+graph3D_embedded_polar_coords - sage: graph.show(viewer=viewer3D) + sage: graph.show() .. PLOT:: S2 = Manifold(2, 'S^2', start_index=1) - polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + polar = S2.chart('th ph') [th, ph] = var('th ph') epolar = polar.frame() ch_basis = S2.automorphism_field() ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) - epolar_ON = S2.default_frame().new_frame(ch_basis, 'e') + epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON) nab = S2.affine_connection('nab') nab.set_coef(epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') @@ -1959,8 +2418,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): Tp = S2.tangent_space(p) v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, - chart=polar,parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], - name='c') + chart=polar, name='c') R3 = Manifold(3, 'R3', start_index=1) cart = R3.chart('X Y Z') [X,Y,Z] = var('X Y Z') @@ -1971,7 +2429,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): verbose=False) interp = c.interpolate(solution_key='sol-angle', interpolation_key='interp-angle', verbose=False) - graph3D_embedded_angle_curve=c.plot(interpolation_key='interp-angle', + graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle', mapping=euclid_embedding, thickness=5, display_tangent=True, scale=0.1, width_tangent=0.5, verbose=False) graph3D_embedded_polar_coords = polar.plot(chart=cart, @@ -1979,13 +2437,163 @@ class IntegratedAutoparallelCurve(IntegratedCurve): graph=graph3D_embedded_angle_curve+graph3D_embedded_polar_coords sphinx_plot(graph) + All the curves presented are loxodromes, and the differential system + defining them (displayed above) may be solved analytically, + providing the following expressions: + + .. MATH:: + + \theta(t) &= \theta_{0} + \dot{\theta}_{0} (t - t_{0}) \\ + \phi(t) &= \phi_{0} - \frac{1}{\tan \alpha} \left( + \ln \tan \frac{\theta_{0} + \dot{\theta}_{0} (t - t_{0})}{2} - + \ln \tan \frac{\theta_{0}}{2} \right) + + where :MATH:`\alpha` is the angle between the curve and any latitude + line it crosses; then, one finds + :MATH:`\tan \alpha = - \dot{\theta}_{0}/(\dot{\phi}_{0} \sin \theta_{0})`) + (then :MATH:`\tan \alpha \leq 0` when the initial tangent vector + points towards the southeast). + + In order to use these expressions to compare with the result + provided by the numerical integration, remember that the components + ``(v\_th0, v\_ph0)`` of the initial + tangent vector ``v`` refer to the basis + ``epolar\_ON`` :MATH:`= (\hat{e}_{\theta}, \hat{e}_{\phi})` and not the + coordinate basis + ``epolar`` :MATH:`= (e_{\theta}, e_{\phi})`. + Therefore, the following relations hold: + ``v\_ph0`` = :MATH:`\dot{\phi}_{0} \sin \theta_{0}` (and not merely + :MATH:`\dot{\phi}_{0}`), while ``v\_th0`` clearly is + :MATH:`\dot{\theta}_{0}`. + + With this in mind, plot an analytical curve to compare with a + numerical solution:: + + sage: graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', + ....: chart=mercator, thickness=1, verbose=False) + sage: expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2))) + sage: c_loxo = S2.curve({polar:[th0+v_th0*t, expr_ph]}, (t,0,2), + ....: name='c_loxo') + + Ask for the expression of the loxodrome in terms of the Mercator + chart in order to add it to its dictionnary of expressions. + It is a particularly long expression, and there is no particular + need to diplay it, which is why it may simply be affected to an + arbitrary variable ``expr_mercator``, which will never be used + again. + But adding the expression to the dictionnary is required to plot the + curve w.r.t the Mercator chart:: + + sage: expr_mercator = c_loxo.expression(chart2=mercator) + + Plot the curves (for clarity, set a 2 degrees shift in the initial + value of :MATH:`\theta_{0}` so that the curves do not overlap):: + + sage: graph2D_mercator_loxo = c_loxo.plot(chart=mercator, + ....: parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8}, + ....: thickness=1, color='blue') + sage: (graph2D_mercator_angle_curve+graph2D_mercator_loxo).show() + + .. PLOT:: + + S2 = Manifold(2, 'S^2', start_index=1) + polar = S2.chart('th ph') + [th, ph] = var('th ph') + epolar = polar.frame() + ch_basis = S2.automorphism_field() + ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) + epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') + nab = S2.affine_connection('nab') + nab.set_coef(epolar_ON)[:] + [t, tmin, tmax, th0, ph0] = var('t tmin tmax th0 ph0') + [v_th0, v_ph0, alpha] = var('v_th0 v_ph0 alpha') + p = S2.point((th0, ph0), name='p') + Tp = S2.tangent_space(p) + v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) + c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, + chart=polar, name='c') + mercator = S2.chart(r'xi:(-oo,oo):\xi ze:(0,2*pi):\zeta') + [xi,ze] = var('xi ze') + polar.transition_map(mercator, (log(tan(th/2)), ph)) + identity = S2.identity_map() + identity.coord_functions(polar, mercator) + sol = c.solve(solution_key='sol-angle', + parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, + verbose=False) + interp = c.interpolate(solution_key='sol-angle', + interpolation_key='interp-angle', verbose=False) + graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', + chart=mercator, thickness=1, verbose=False) + expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2))) + c_loxo = S2.curve({polar:[th0+v_th0*t, expr_ph]}, (t,0,2), + name='c_loxo') + c_loxo.expression(chart2=mercator) + graph2D_mercator_loxo = c_loxo.plot(chart=mercator, + parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8}, + thickness=1, color='blue') + sphinx_plot(graph2D_mercator_angle_curve+graph2D_mercator_loxo) + + Both curves do have the same aspect. + One may eventually compare these curves on :MATH:`\mathbb{S}^{2}`:: + + sage: graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle', + ....: mapping=euclid_embedding, thickness=3, verbose=False) + sage: graph3D_embedded_loxo = c_loxo.plot(mapping=euclid_embedding, + ....: parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8}, + ....: thickness=3, color = 'blue') + sage: graph=graph3D_embedded_angle_curve + graph3D_embedded_loxo + sage: graph += graph3D_embedded_polar_coords + sage: graph.show() + + .. PLOT:: + + S2 = Manifold(2, 'S^2', start_index=1) + polar = S2.chart('th ph') + [th, ph] = var('th ph') + epolar = polar.frame() + ch_basis = S2.automorphism_field() + ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) + epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') + nab = S2.affine_connection('nab') + nab.set_coef(epolar_ON)[:] + [t, tmin, tmax, th0, ph0] = var('t tmin tmax th0 ph0') + [v_th0, v_ph0, alpha] = var('v_th0 v_ph0 alpha') + p = S2.point((th0, ph0), name='p') + Tp = S2.tangent_space(p) + v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) + c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), v, + chart=polar, name='c') + R3 = Manifold(3, 'R3', start_index=1) + cart = R3.chart('X Y Z') + [X,Y,Z] = var('X Y Z') + euclid_embedding = S2.diff_map(R3, + {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) + sol = c.solve(solution_key='sol-angle', + parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, + verbose=False) + interp = c.interpolate(solution_key='sol-angle', + interpolation_key='interp-angle', verbose=False) + graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle', + mapping=euclid_embedding, thickness=3, verbose=False) + expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2))) + c_loxo = S2.curve({polar:[th0+v_th0*t, expr_ph]}, (t,0,2), + name='c_loxo') + graph3D_embedded_loxo = c_loxo.plot(mapping=euclid_embedding, + parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8}, + thickness=3, color='blue') + graph3D_embedded_polar_coords = polar.plot(chart=cart, + mapping=euclid_embedding, number_values=15, color='yellow') + graph = graph3D_embedded_angle_curve + graph3D_embedded_loxo + graph += graph3D_embedded_polar_coords + sphinx_plot(graph) + """ def __init__(self, parent, affine_connection, curve_parameter, - initial_tangent_vector, chart=None, parameters=None, - name=None, latex_name=None, is_isomorphism=False, - is_identity=False): - r"""Constructs an autoparallel curve with respect to the given + initial_tangent_vector, chart=None, name=None, + latex_name=None, is_isomorphism=False, + is_identity=False, verbose=True): + r"""Construct an autoparallel curve with respect to the given affine connection with the given initial tangent vector. TESTS:: @@ -1999,48 +2607,51 @@ def __init__(self, parent, affine_connection, curve_parameter, sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, - ....: name='c', parameters=[A, B]); c + ....: name='c', verbose=False) ; c Integrated autoparallel curve c in the 3-dimensional differentiable manifold M + sage: TestSuite(c).run() """ - from sage.symbolic.ring import SR - - dim = parent.codomain().dim() - i0 = parent.codomain().start_index() - equations_rhs = [] - # setting the chart to gain access to the coordinate functions if chart is None: chart = parent.codomain().default_chart() coordinate_functions = chart[:] velocities = chart.symbolic_velocities() - gamma = affine_connection.coef() - for alpha in range(dim): - rhs = SR(0) - for mu in range(dim): - for nu in range(dim): - AUX = velocities[mu] * velocities[nu] - rhs-= gamma[alpha+i0, mu+i0, nu+i0].expr() * AUX - # 'AUX' only used for the line above to be shorter - equations_rhs += [rhs.simplify_full()] + if is_identity: + equations_rhs = None + else: + dim = parent.codomain().dim() + i0 = parent.codomain().start_index() + equations_rhs = [] + + gamma = affine_connection.coef() + + for alpha in range(dim): + rhs = 0 + for mu in range(dim): + for nu in range(dim): + AUX = velocities[mu] * velocities[nu] + rhs-= gamma[alpha+i0, mu+i0, nu+i0].expr() * AUX + # 'AUX' only used for the line above to be shorter + equations_rhs += [rhs.simplify_full()] IntegratedCurve.__init__(self, parent, equations_rhs, velocities, curve_parameter, initial_tangent_vector, chart=chart, - parameters=parameters, name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, - is_identity=is_identity) + is_identity=is_identity, + verbose=verbose) self._affine_connection = affine_connection def _repr_(self): r""" - Returns a string representation of ``self``. + Return a string representation of ``self``. TESTS:: @@ -2052,12 +2663,12 @@ def _repr_(self): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, - ....: parameters=[A, B]); c + sage: c = M.integrated_autoparallel_curve(nab, (t,0,5), v, + ....: verbose=False) ; c Integrated autoparallel curve in the 3-dimensional differentiable manifold M sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, - ....: name='c', parameters=[A, B]); c + ....: name='c', verbose=False) ; c Integrated autoparallel curve c in the 3-dimensional differentiable manifold M @@ -2069,9 +2680,54 @@ def _repr_(self): description += "in the {}".format(self._codomain) return description + def __reduce__(self): + r""" + Reduction function for the pickle protocole. + + TESTS:: + + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: [t, A, B] = var('t A B') + sage: nab = M.affine_connection('nabla', r'\nabla') + sage: nab[X.frame(),0,0,1],nab[X.frame(),2,1,2]=A*x1^2,B*x2*x3 + sage: p = M.point((0,0,0), name='p') + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,0,1)) + sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, + ....: name='c', verbose=False) + sage: c.__reduce__() + (, + (Set of Morphisms from Real interval (0, 5) to + 3-dimensional differentiable manifold M in Category of + homsets of subobjects of sets and topological spaces which + actually are integrated autoparallel curves w.r.t a + certain affine connection, + Affine connection nabla on the 3-dimensional + differentiable manifold M, + t, + Tangent vector at Point p on the 3-dimensional differentiable manifold M, + Chart (M, (x1, x2, x3)), + 'c', + 'c', + False, + False)) + + Test of pickling:: + + sage: loads(dumps(c)) + Integrated autoparallel curve c in the 3-dimensional differentiable manifold M + + """ + + return (type(self), (self.parent(), self._affine_connection, + self._curve_parameter, self._initial_tangent_vector, + self._chart, self._name, self._latex_name, + self._is_isomorphism, self._is_identity)) + def system(self, verbose=True): r""" - Provides a detailed description of the system defining the + Provide a detailed description of the system defining the autoparallel curve and returns the system defining it: chart, equations and initial conditions. @@ -2085,7 +2741,9 @@ def system(self, verbose=True): - list containing the attributes :attr:`equations_rhs`, :attr:`initial_tangent_vector` and :attr:`chart` - TESTS:: + EXAMPLE: + + System defining an autoparallel curve:: sage: M = Manifold(3, 'M') sage: X. = M.chart() @@ -2096,7 +2754,7 @@ def system(self, verbose=True): sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, - ....: parameters=[A, B]) + ....: verbose=False) sage: sys = c.system() Autoparallel curve in the 3-dimensional differentiable manifold M equipped with Affine connection nabla on the @@ -2113,10 +2771,10 @@ def system(self, verbose=True): components [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) d(x1)/dt = Dx1 - d(Dx1)/dt = -A*Dx1*Dx2*x1^2 d(x2)/dt = Dx2 - d(Dx2)/dt = 0 d(x3)/dt = Dx3 + d(Dx1)/dt = -A*Dx1*Dx2*x1^2 + d(Dx2)/dt = 0 d(Dx3)/dt = -B*Dx2*Dx3*x2*x3 sage: sys_bis = c.system(verbose=False) @@ -2139,7 +2797,7 @@ def system(self, verbose=True): # known initial_coord_basis = chart.frame().at(initial_pt) - initial_tgt_vec_comps = v0[:,initial_coord_basis] # will + initial_tgt_vec_comps = v0[initial_coord_basis,:] # will # raise error if components in coordinate basis are not # known @@ -2165,21 +2823,23 @@ def system(self, verbose=True): description +="{}".format(initial_tgt_vec_comps) description += " w.r.t. {}\n\n".format(chart) - zip_sys = zip(chart[:],self._velocities,self._equations_rhs) - for coord_func, velocity, eqn in zip_sys: + for coord_func,velocity in zip(chart[:],self._velocities): description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) + + for velocity,eqn in zip(self._velocities,self._equations_rhs): description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) + print(description) return [self._equations_rhs, v0, chart] class IntegratedGeodesic(IntegratedAutoparallelCurve): r""" - Constructs a numerical geodesic on the manifold with respect to a + Numerical geodesic on the manifold with respect to a given metric. INPUT: @@ -2191,16 +2851,15 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric` metric with respect to which the curve is a geodesic - ``curve_parameter`` -- symbolic expression to be used as the - parameter of the curve; + parameter of the curve (the equations defining an instance of + IntegratedGeodesic are such that ``t`` will actually be an affine + parameter of the curve); - ``initial_tangent_vector`` -- :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` initial tangent vector of the curve - - ``chart`` -- (default: ``None``) chart on the manifold in - which the equations are given; if ``None`` the default chart + - ``chart`` -- (default: ``None``) chart on the manifold in terms of + which the equations are expressed; if ``None`` the default chart of the manifold is assumed - - ``parameters`` -- list of the symbolic expressions used in the - coefficients of ``metric`` other than the coordinates - associated with the chart - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used @@ -2219,7 +2878,7 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): corresponding coordinate frame :MATH:`(e_{\theta}, e_{\phi})`:: sage: S2 = Manifold(2, 'S^2', start_index=1) - sage: polar.=S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + sage: polar.=S2.chart('th ph') sage: epolar = polar.frame() Set the Euclidean metric tensor :MATH:`g` induced on @@ -2240,8 +2899,7 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): sage: [t, tmin, tmax] = var('t tmin tmax') sage: c = S2.integrated_geodesic(g, (t, tmin, tmax), v, - ....: chart=polar, - ....: parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0],name='c') + ....: chart=polar, name='c', verbose=False) sage: sys = c.system() Geodesic c in the 2-dimensional differentiable manifold S^2 equipped with Riemannian metric g on the 2-dimensional @@ -2257,8 +2915,8 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): components [v_th0, v_ph0] w.r.t. Chart (S^2, (th, ph)) d(th)/dt = Dth - d(Dth)/dt = Dph^2*cos(th)*sin(th) d(ph)/dt = Dph + d(Dth)/dt = Dph^2*cos(th)*sin(th) d(Dph)/dt = -2*Dph*Dth*cos(th)/sin(th) @@ -2287,7 +2945,7 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): ....: parameters_values=dict_params[key], verbose=False) ....: interp = c.interpolate(solution_key='sol-'+key, ....: interpolation_key='interp-'+key, verbose=False) - ....: graph3D_embedded_geods+=c.plot(interpolation_key='interp-'+key, + ....: graph3D_embedded_geods+=c.plot_integrated(interpolation_key='interp-'+key, ....: mapping=euclid_embedding, thickness=5, ....: display_tangent=True, scale=0.3, ....: width_tangent=0.5, verbose=False) @@ -2299,13 +2957,12 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): ....: mapping=euclid_embedding, ....: number_values=15, color='yellow') sage: graph=graph3D_embedded_geods+graph3D_embedded_polar_coords - sage: viewer3D = 'threejs' - sage: graph.show(viewer=viewer3D) + sage: graph.show() .. PLOT:: S2 = Manifold(2, 'S^2', start_index=1) - polar = S2.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') + polar = S2.chart('th ph') [th, ph] = var('th ph') epolar = polar.frame() g = S2.metric('g') @@ -2315,7 +2972,7 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): Tp = S2.tangent_space(p) v = Tp((v_th0, v_ph0), basis=epolar.at(p)) c = S2.integrated_geodesic(g, (t, tmin, tmax), v, chart=polar, - parameters=[tmin,tmax,th0,ph0,v_th0,v_ph0], name='c') + name='c') dict_params={'equat':{tmin:0,tmax:3,th0:pi/2,ph0:0.1,v_th0:0,v_ph0:1}, 'longi':{tmin:0,tmax:3,th0:0.1,ph0:0.1,v_th0:1,v_ph0:0}, 'angle':{tmin:0,tmax:3,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:1}} @@ -2330,7 +2987,7 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): parameters_values=dict_params[key], verbose=False) interp = c.interpolate(solution_key='sol-'+key, interpolation_key='interp-'+key, verbose=False) - graph3D_embedded_geods+=c.plot(interpolation_key='interp-'+key, + graph3D_embedded_geods+=c.plot_integrated(interpolation_key='interp-'+key, mapping=euclid_embedding, thickness=5, display_tangent=True, scale=0.3, width_tangent=0.5, verbose=False) @@ -2343,11 +3000,11 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): """ def __init__(self, parent, metric, curve_parameter, - initial_tangent_vector, chart=None, parameters=None, - name=None, latex_name=None, is_isomorphism=False, - is_identity=False): + initial_tangent_vector, chart=None, name=None, + latex_name=None, is_isomorphism=False, + is_identity=False, verbose=True): - r"""Constructs a geodesic curve with respect to the given metric + r"""Construct a geodesic curve with respect to the given metric with the given initial tangent vector. TESTS:: @@ -2362,26 +3019,32 @@ def __init__(self, parent, metric, curve_parameter, sage: p = S2.point((pi/2,0), name='p') sage: Tp = S2.tangent_space(p) sage: v = Tp((1/sqrt(2),1/sqrt(2))) - sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', - ....: parameters=[A]); c + sage: c = S2.integrated_geodesic(g, (t,0,pi), v, name='c', + ....: verbose=False) ; c Integrated geodesic c in the 2-dimensional differentiable manifold S^2 + sage: TestSuite(c).run() """ + if is_identity: + affine_connection = None + else: + affine_connection = metric.connection() + IntegratedAutoparallelCurve.__init__(self, parent, - metric.connection(), curve_parameter, + affine_connection, curve_parameter, initial_tangent_vector, chart=chart, - parameters=parameters, name=name, - latex_name=latex_name, + name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, - is_identity=is_identity) + is_identity=is_identity, + verbose=verbose) self._metric = metric def _repr_(self): r""" - Returns a string representation of ``self``. + Return a string representation of ``self``. TESTS:: @@ -2396,11 +3059,11 @@ def _repr_(self): sage: Tp = S2.tangent_space(p) sage: v = Tp((1/sqrt(2),1/sqrt(2))) sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, - ....: parameters=[A]); c + ....: verbose=False) ; c Integrated geodesic in the 2-dimensional differentiable manifold S^2 - sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', - ....: parameters=[A]); c + sage: c = S2.integrated_geodesic(g, (t,0,pi), v, + ....: name='c', verbose=False) ; c Integrated geodesic c in the 2-dimensional differentiable manifold S^2 @@ -2412,9 +3075,55 @@ def _repr_(self): description += "in the {}".format(self._codomain) return description + def __reduce__(self): + r""" + Reduction function for the pickle protocole. + + TESTS:: + + sage: S2 = Manifold(2, 'S^2') + sage: X. = S2.chart() + sage: [t, A] = var('t A') + sage: g = S2.metric('g') + sage: g[0,0] = A + sage: g[1,0] = 0 + sage: g[1,1] = A*sin(theta)^2 + sage: p = S2.point((pi/2,0), name='p') + sage: Tp = S2.tangent_space(p) + sage: v = Tp((1/sqrt(2),1/sqrt(2))) + sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', + ....: verbose=False) + sage: c.__reduce__() + (, + (Set of Morphisms from Real interval (0, pi) to + 2-dimensional differentiable manifold S^2 in Category of + homsets of subobjects of sets and topological spaces which + actually are integrated geodesics w.r.t a certain metric, + Riemannian metric g on the 2-dimensional differentiable + manifold S^2, + t, + Tangent vector at Point p on the 2-dimensional differentiable manifold S^2, + Chart (S^2, (theta, phi)), + 'c', + 'c', + False, + False)) + + Test of pickling:: + + sage: loads(dumps(c)) + Integrated geodesic c in the 2-dimensional differentiable manifold S^2 + + """ + + return (type(self), (self.parent(), self._metric, + self._curve_parameter, self._initial_tangent_vector, + self._chart, self._name, self._latex_name, + self._is_isomorphism, self._is_identity)) + def system(self, verbose=True): r""" - Returns the system defining the geodesic : chart, equations and + Return the system defining the geodesic : chart, equations and initial conditions INPUT: @@ -2427,7 +3136,9 @@ def system(self, verbose=True): - list containing the attributes :attr:`equations_rhs`, :attr:`initial_tangent_vector` and :attr:`chart` - TESTS:: + EXAMPLE: + + System defining a geodesic:: sage: S2 = Manifold(2, 'S^2') sage: X. = S2.chart() @@ -2440,7 +3151,7 @@ def system(self, verbose=True): sage: Tp = S2.tangent_space(p) sage: v = Tp((1/sqrt(2),1/sqrt(2))) sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', - ....: parameters=[A]) + ....: verbose=False) sage: sys = c.system() Geodesic c in the 2-dimensional differentiable manifold S^2 equipped with Riemannian metric g on the 2-dimensional @@ -2457,8 +3168,8 @@ def system(self, verbose=True): Chart (S^2, (theta, phi)) d(theta)/dt = Dtheta - d(Dtheta)/dt = Dphi^2*cos(theta)*sin(theta) d(phi)/dt = Dphi + d(Dtheta)/dt = Dphi^2*cos(theta)*sin(theta) d(Dphi)/dt = -2*Dphi*Dtheta*cos(theta)/sin(theta) sage: sys_bis = c.system(verbose=False) @@ -2481,7 +3192,7 @@ def system(self, verbose=True): # known initial_coord_basis = chart.frame().at(initial_pt) - initial_tgt_vec_comps=v0[:,initial_coord_basis]#will + initial_tgt_vec_comps = v0[initial_coord_basis,:] # will # raise error if components in coordinate basis are not # known @@ -2507,14 +3218,16 @@ def system(self, verbose=True): description +="{}".format(initial_tgt_vec_comps) description += " w.r.t. {}\n\n".format(chart) - zip_sys = zip(chart[:],self._velocities,self._equations_rhs) - for coord_func, velocity, eqn in zip_sys: + for coord_func,velocity in zip(chart[:],self._velocities): description += "d({})/d{} = {}\n".format(coord_func, self._curve_parameter, velocity) + + for velocity,eqn in zip(self._velocities,self._equations_rhs): description += "d({})/d{} = {}\n".format(velocity, self._curve_parameter, eqn) + print(description) return [self._equations_rhs, v0, chart] diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index eb2cc4bb94e..1d133574f2d 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -2571,11 +2571,10 @@ def curve(self, coord_expression, param, chart=None, return curve_set(coord_expression, name=name, latex_name=latex_name) def integrated_curve(self, equations_rhs, velocities, curve_param, - initial_tangent_vector, chart=None, parameters=None, - name=None, latex_name=None, is_isomorphism=False, - is_identity=False): + initial_tangent_vector, chart=None, name=None, + latex_name=None, verbose=True): r""" - Constructs a numerical curve defined by a system of second order + Construct a numerical curve defined by a system of second order differential equations in the coordinate functions. .. SEEALSO:: @@ -2622,19 +2621,19 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, EXAMPLE: - Trajectory of a particle of unit mass and unit charge in an - unit, axial, uniform, stationnary magnetic field:: + Trajectory of a particle of unit mass and unit charge in a + unit, uniform, stationnary magnetic field:: sage: M = Manifold(3, 'M') sage: X. = M.chart() - sage: var('t') - t + sage: t = var('t') sage: D = X.symbolic_velocities() sage: eqns = [D[1], -D[0], SR(0)] sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 6), v,name='c') + sage: c = M.integrated_curve(eqns, D, (t, 0, 6), v, + ....: name='c', verbose=False) sage: c Integrated curve c in the 3-dimensional differentiable manifold M @@ -2652,30 +2651,31 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, components [1, 0, 1] w.r.t. Chart (M, (x1, x2, x3)) d(x1)/dt = Dx1 - d(Dx1)/dt = Dx2 d(x2)/dt = Dx2 - d(Dx2)/dt = -Dx1 d(x3)/dt = Dx3 + d(Dx1)/dt = Dx2 + d(Dx2)/dt = -Dx1 d(Dx3)/dt = 0 sage: sol = c.solve(verbose=False) sage: interp = c.interpolate(verbose=False) sage: c(1.3) Evaluating point coordinates from the interpolation - associated with the key 'cubic spline-interp-rk4' + associated with the key 'cubic spline-interp-rk4_maxima' by default... - [0.9635578097813995, -0.7325001553656034, 1.3] + [0.9635581155730744, -0.7325010457963622, 1.3] sage: tgt_vec = c.tangent_vector_eval_at(3.7) Evaluating tangent vector components from the interpolation - associated with the key 'cubic spline-interp-rk4' + associated with the key 'cubic spline-interp-rk4_maxima' by default... sage: tgt_vec[:] - [-0.8481002291911669, 0.5298327234653155, 1.0000000000000036] + [-0.8481008455360024, 0.5298346120470748, 1.0000000000000007] """ from sage.manifolds.differentiable.real_line import RealLine - from sage.manifolds.differentiable.integrated_curve import IntegratedCurve + from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet + if len(curve_param) != 3: raise ValueError("the argument 'curve_param' must be of the form " + "(t, t_min, t_max)") @@ -2684,18 +2684,17 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, t_max = curve_param[2] real_field = RealLine(names=(repr(t),)) interval = real_field.open_interval(t_min, t_max) - curve_set = Hom(interval, self) - return IntegratedCurve(curve_set, equations_rhs, velocities, t, - initial_tangent_vector, chart=chart, parameters=parameters, - name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, - is_identity=is_identity) #use any element_constructor ? - - def integrated_autoparallel_curve(self, affine_connection, curve_param, - initial_tangent_vector, chart=None, parameters=None, - name=None, latex_name=None, is_isomorphism=False, - is_identity=False): + integrated_curve_set = IntegratedCurveSet(interval, self) # not + # possible to use Hom(interval, self) + return integrated_curve_set(equations_rhs, velocities, t, + initial_tangent_vector, chart=chart, name=name, + latex_name=latex_name, verbose=verbose) + + def integrated_autoparallel_curve(self, affine_connection, + curve_param, initial_tangent_vector, chart=None, + name=None, latex_name=None, verbose=True): r""" - Constructs a numerical autoparallel curve on the manifold with + Construct a numerical autoparallel curve on the manifold with respect to a given affine connection. .. SEEALSO:: @@ -2713,7 +2712,9 @@ def integrated_autoparallel_curve(self, affine_connection, curve_param, where * ``t`` is the symbolic expression to be used as the parameter - of the curve ; + of the curve (the equations defining an instance of + IntegratedAutoparallelCurve are such that ``t`` will + actually be an affine parameter of the curve); * ``t_min`` is its minimal (finite) value; * ``t_max`` its maximal (finite) value; @@ -2773,7 +2774,7 @@ def integrated_autoparallel_curve(self, affine_connection, curve_param, sage: v = Tp((1,1), basis=epolar_ON.at(p)) sage: t = var('t') sage: c = S2.integrated_autoparallel_curve(nab, (t, 0, 6), - ....: v, chart=polar, name='c') + ....: v, chart=polar, name='c', verbose=False) sage: sys = c.system() Autoparallel curve c in the 2-dimensional differentiable manifold S^2 equipped with Affine connection nab on the @@ -2790,28 +2791,29 @@ def integrated_autoparallel_curve(self, affine_connection, curve_param, Chart (S^2, (th, ph)) d(th)/dt = Dth - d(Dth)/dt = 0 d(ph)/dt = Dph + d(Dth)/dt = 0 d(Dph)/dt = -Dph*Dth*cos(th)/sin(th) sage: sol = c.solve(verbose=False) sage: interp = c.interpolate(verbose=False) sage: c(1.3) Evaluating point coordinates from the interpolation - associated with the key 'cubic spline-interp-rk4' + associated with the key 'cubic spline-interp-rk4_maxima' by default... - [2.085398163397449, 1.420314580385403] + [2.085398163397449, 1.4203172015958863] sage: tgt_vec = c.tangent_vector_eval_at(3.7) Evaluating tangent vector components from the interpolation - associated with the key 'cubic spline-interp-rk4' + associated with the key 'cubic spline-interp-rk4_maxima' by default... sage: tgt_vec[:] - [0.9999999999999986, -0.9736581694086809] + [0.9999999999999732, -1.016513736236512] """ from sage.manifolds.differentiable.real_line import RealLine - from sage.manifolds.differentiable.integrated_curve import IntegratedAutoparallelCurve + from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet + if len(curve_param) != 3: raise ValueError("the argument 'curve_param' must be of the form " + "(t, t_min, t_max)") @@ -2820,18 +2822,17 @@ def integrated_autoparallel_curve(self, affine_connection, curve_param, t_max = curve_param[2] real_field = RealLine(names=(repr(t),)) interval = real_field.open_interval(t_min, t_max) - curve_set = Hom(interval, self) - return IntegratedAutoparallelCurve(curve_set, affine_connection, t, - initial_tangent_vector, chart=chart, parameters=parameters, - name=name, latex_name=latex_name, is_isomorphism=is_isomorphism, - is_identity=is_identity) - #use any element_constructor ? - - def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, - chart=None, parameters=None, name=None, latex_name=None, - is_isomorphism=False, is_identity=False): + autoparallel_curve_set=IntegratedAutoparallelCurveSet(interval,self) + # not possible to use Hom(interval, self) + return autoparallel_curve_set(affine_connection, t, + initial_tangent_vector, chart=chart, + name=name, latex_name=latex_name, verbose=verbose) + + def integrated_geodesic(self, metric, curve_param, + initial_tangent_vector, chart=None, + name=None, latex_name=None, verbose=True): r""" - Constructs a numerical geodesic on the manifold with respect to + Construct a numerical geodesic on the manifold with respect to a given metric. .. SEEALSO:: @@ -2848,7 +2849,9 @@ def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, where * ``t`` is the symbolic expression to be used as the parameter - of the curve ; + of the curve (the equations defining an instance of + IntegratedGeodesic are such that ``t`` will actually be an + affine parameter of the curve); * ``t_min`` is its minimal (finite) value; * ``t_max`` its maximal (finite) value; @@ -2896,7 +2899,7 @@ def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, sage: v = Tp((1, 1), basis=epolar.at(p)) sage: t = var('t') sage: c = S2.integrated_geodesic(g, (t, 0, 6), v, - ....: chart=polar, name='c') + ....: chart=polar, name='c', verbose=False) sage: sys = c.system() Geodesic c in the 2-dimensional differentiable manifold S^2 equipped with Riemannian metric g on the 2-dimensional @@ -2912,27 +2915,28 @@ def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, components [1, 1] w.r.t. Chart (S^2, (th, ph)) d(th)/dt = Dth - d(Dth)/dt = Dph^2*cos(th)*sin(th) d(ph)/dt = Dph + d(Dth)/dt = Dph^2*cos(th)*sin(th) d(Dph)/dt = -2*Dph*Dth*cos(th)/sin(th) sage: sol = c.solve(verbose=False) sage: interp = c.interpolate(verbose=False) sage: c(1.3) Evaluating point coordinates from the interpolation - associated with the key 'cubic spline-interp-rk4' + associated with the key 'cubic spline-interp-rk4_maxima' by default... - [2.204750869496952, 0.7986664139663504] + [2.2047444794514663, 0.7986609561213334] sage: tgt_vec = c.tangent_vector_eval_at(3.7) Evaluating tangent vector components from the interpolation - associated with the key 'cubic spline-interp-rk4' + associated with the key 'cubic spline-interp-rk4_maxima' by default... sage: tgt_vec[:] - [-1.0907562667574524, 0.6205613159665633] + [-1.090742147346732, 0.620568327518154] """ from sage.manifolds.differentiable.real_line import RealLine - from sage.manifolds.differentiable.integrated_curve import IntegratedGeodesic + from sage.manifolds.differentiable.manifold_homset import IntegratedGeodesicSet + if len(curve_param) != 3: raise ValueError("the argument 'curve_param' must be of the form " + "(t, t_min, t_max)") @@ -2941,11 +2945,10 @@ def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, t_max = curve_param[2] real_field = RealLine(names=(repr(t),)) interval = real_field.open_interval(t_min, t_max) - curve_set = Hom(interval, self) - return IntegratedGeodesic(curve_set, metric, t, initial_tangent_vector, - chart=chart, parameters=parameters, name=name, latex_name=latex_name, - is_isomorphism=is_isomorphism, is_identity=is_identity) - #use any element_constructor ? + integrated_geodesic_set = IntegratedGeodesicSet(interval, self) + return integrated_geodesic_set(metric, t, initial_tangent_vector, + chart=chart,name=name, latex_name=latex_name, + verbose=verbose) def affine_connection(self, name, latex_name=None): r""" diff --git a/src/sage/manifolds/differentiable/manifold_homset.py b/src/sage/manifolds/differentiable/manifold_homset.py index ec9506a5c01..dcccce4b94d 100644 --- a/src/sage/manifolds/differentiable/manifold_homset.py +++ b/src/sage/manifolds/differentiable/manifold_homset.py @@ -33,6 +33,10 @@ from sage.manifolds.manifold_homset import TopologicalManifoldHomset from sage.manifolds.differentiable.diff_map import DiffMap from sage.manifolds.differentiable.curve import DifferentiableCurve +from sage.manifolds.differentiable.integrated_curve import IntegratedCurve +from sage.manifolds.differentiable.integrated_curve import IntegratedAutoparallelCurve +from sage.manifolds.differentiable.integrated_curve import IntegratedGeodesic +from sage.misc.cachefunc import cached_method class DifferentiableManifoldHomset(TopologicalManifoldHomset): r""" @@ -500,3 +504,1277 @@ def _an_element_(self): coord_expression = {chart2: target_coord} return self.element_class(self, coord_expression) + +#****************************************************************************** + +class IntegratedCurveSet(DifferentiableCurveSet): + r""" + Set of integrated curves in a differentiable manifold. + + INPUT: + + - ``domain`` -- + :class:`~sage.manifolds.differentiable.real_line.OpenInterval` + open interval `I \subset \RR` with finite boundaries (domain of + the morphisms) + - ``codomain`` -- + :class:`~sage.manifolds.differentiable.manifold.DifferentiableManifold`; + differentiable manifold `M` (codomain of the morphisms) + - ``name`` -- (default: ``None``) string; name given to the set of + integrated curves; if ``None``, ``Hom_integrated(I, M)`` will be + used + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the set of integrated curves; if ``None``, + `\mathrm{Hom_{integrated}}(I,M)` will be used + + EXAMPLES: + + + + + + TO DO ! + + + + + + This parent class needs to be imported:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet + + Integrated curves are only allowed to defined on interval with + finite bounds. + This forbids to define an instance of this parent class whose domain + has infinite bounds:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: H = IntegratedCurveSet(R, M) + Traceback (most recent call last): + ... + ValueError: Both boundaries of the interval defining the domain + of a Homset of integrated curves need to be finite. + + An instance whose domain is an interval with finite bounds allows to + build an integrated curve defined on the interval:: + + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedCurveSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 3-dimensional + differentiable manifold M in Category of homsets of subobjects + of sets and topological spaces which actually are integrated + curves + sage: eqns_rhs = [1,1] + sage: vels = X.symbolic_velocities() + sage: t = var('t') + sage: p = M.point((3,4)) + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,2)) + sage: c = H(eqns_rhs, vels, t, v, name='c') ; c + Integrated curve c in the 2-dimensional differentiable + manifold M + + An element of ``H`` is a curve in ``M``:: + + sage: d = H.an_element(); d + Integrated curve in the 2-dimensional differentiable manifold M + sage: sys = d.system() + Curve in the 2-dimensional differentiable manifold M integrated + over the Real interval (-1, 2) as a solution to the following + system, written w.r.t. Chart (M, (x, y)): + + Initial point: Point on the 2-dimensional differentiable + manifold M with coordinates [0, 0] w.r.t. Chart (M, (x, y)) + Initial tangent vector: Tangent vector at Point on the + 2-dimensional differentiable manifold M with components + [1/4, 0] w.r.t. Chart (M, (x, y)) + + d(x)/dt = Dx + d(y)/dt = Dy + d(Dx)/dt = -1/4*sin(t + 1) + d(Dy)/dt = 0 + + + The test suite is passed:: + + sage: TestSuite(H).run() + + More generally, an instance of this class may be defined with + abstract bounds :MATH:`(a,b)`:: + + sage: [a,b] = var('a b') + sage: J = R.open_interval(a, b) + sage: H = IntegratedCurveSet(J, M) ; H + Set of Morphisms from Real interval (a, b) to 2-dimensional + differentiable manifold M in Category of homsets of subobjects + of sets and topological spaces which actually are integrated + curves + + An element of ``H`` is a curve in ``M``:: + + sage: f = H.an_element(); f + Integrated curve in the 2-dimensional differentiable manifold M + sage: sys = f.system() + Curve in the 2-dimensional differentiable manifold M integrated + over the Real interval (a, b) as a solution to the following + system, written w.r.t. Chart (M, (x, y)): + + Initial point: Point on the 2-dimensional differentiable + manifold M with coordinates [0, 0] w.r.t. Chart (M, (x, y)) + Initial tangent vector: Tangent vector at Point on the + 2-dimensional differentiable manifold M with components + [1/4, 0] w.r.t. Chart (M, (x, y)) + + d(x)/dt = Dx + d(y)/dt = Dy + d(Dx)/dt = -1/4*sin(-a + t) + d(Dy)/dt = 0 + + + Yet, even in the case of abstract bounds, considering any of them to + be infinite is still prohibited since no numerical integration could + be performed:: + + sage: f.solve(parameters_values={a:-1, b:+oo}) + Traceback (most recent call last): + ... + ValueError: Both boundaries of the interval need to be finite. + + The set of integrated curves `J \longrightarrow J` is a set of + numerical (manifold) endomorphisms:: + + sage: H = IntegratedCurveSet(J, J); H + Set of Morphisms from Real interval (a, b) to Real interval + (a, b) in Category of endsets of subobjects of sets and + topological spaces which actually are integrated curves + sage: H.category() + Category of endsets of subobjects of sets and topological spaces + + It is a monoid for the law of morphism composition:: + + sage: H in Monoids() + True + + The identity element of the monoid is a numerical version of the + identity map of `J`:: + + + COMPLETE ! + + """ + + + + Element = IntegratedCurve + + def __init__(self, domain, codomain, name=None, latex_name=None): + r""" + Initialize ``self``. + + TESTS:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: H = IntegratedCurveSet(R, M) + Traceback (most recent call last): + ... + ValueError: Both boundaries of the interval defining the + domain of a Homset of integrated curves need to be finite. + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedCurveSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 3-dimensional + differentiable manifold M in Category of homsets of + subobjects of sets and topological spaces which actually + are integrated curves + sage: TestSuite(H).run() + sage: H = IntegratedCurveSet(I, I); H + Set of Morphisms from Real interval (-1, 2) to Real interval + (-1, 2) in Category of endsets of subobjects of sets and + topological spaces which actually are integrated curves + sage: TestSuite(H).run() + + """ + + from sage.rings.infinity import Infinity + + DifferentiableCurveSet.__init__(self, domain, codomain, + name=name, latex_name=latex_name) + + # checking argument 'domain': 't_min' and 't_max' are only + # allowed to be either expressions of finite real values + t_min = domain.lower_bound() + t_max = domain.upper_bound() + if t_min == -Infinity or t_max == +Infinity: + raise ValueError("Both boundaries of the interval " + + "defining the domain of a Homset of " + + "integrated curves need to be finite.") + + if name is None: + self._name = "Hom_integrated({},{})".format(domain._name, + codomain._name) + else: + self._name = name + if latex_name is None: + self._latex_name = r"\mathrm{{Hom}_{integrated}}" + self._latex_name += r"\left({},{}\right)".format( + domain._latex_name, codomain._latex_name) + else: + self._latex_name = latex_name + + #### Parent methods #### + + def _repr_(self): + """ + TESTS:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedCurveSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 3-dimensional + differentiable manifold M in Category of homsets of + subobjects of sets and topological spaces which actually + are integrated curves + + """ + description = "Set of Morphisms " + description += "from {} to {} in {} ".format(self._domain, + self._codomain, self.category()) + description += "which actually are integrated curves" + return description + + + def _element_constructor_(self, equations_rhs, velocities, + curve_parameter, initial_tangent_vector, chart=None, + name=None, latex_name=None, is_isomorphism=False, + is_identity=False, verbose=True): + r""" + Construct an element of ``self``, i.e. an integrated curve + `I \to M`, where `I` is a real interval and `M` some + differentiable manifold. + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` + + EXAMPLE:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedCurveSet(I, M) + sage: eqns_rhs = [1,1] + sage: vels = X.symbolic_velocities() + sage: t = var('t') + sage: p = M.point((3,4)) + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,2)) + sage: c = H(eqns_rhs, vels, t, v, name='c') ; c + Integrated curve c in the 2-dimensional differentiable + manifold M + + """ + # Standard construction + return self.element_class(self, equations_rhs, velocities, + curve_parameter, initial_tangent_vector, chart=chart, + name=name, latex_name=latex_name, + is_isomorphism=is_isomorphism, is_identity=is_identity, + verbose=verbose) + + def _an_element_(self): + r""" + Construct some element of ``self``. + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` + + EXAMPLE:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedCurveSet(I, M) + sage: c = H._an_element_() ; c + Integrated curve in the 2-dimensional differentiable + manifold M + sage: sys = c.system() + Curve in the 2-dimensional differentiable manifold M + integrated over the Real interval (-1, 2) as a solution to + the following system, written w.r.t. Chart (M, (x, y)): + + Initial point: Point on the 2-dimensional differentiable + manifold M with coordinates [0, 0] w.r.t. Chart (M, (x, y)) + Initial tangent vector: Tangent vector at Point on the + 2-dimensional differentiable manifold M with components + [1/4, 0] w.r.t. Chart (M, (x, y)) + + d(x)/dt = Dx + d(y)/dt = Dy + d(Dx)/dt = -1/4*sin(t + 1) + d(Dy)/dt = 0 + + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1, verbose=False) + [0.22732435599328793, 0.0] + sage: H = IntegratedCurveSet(I, I) + sage: c = H._an_element_() ; c + Integrated curve in the Real interval (-1, 2) + sage: sys = c.system() + Curve in the Real interval (-1, 2) integrated over the Real + interval (-1, 2) as a solution to the following system, + written w.r.t. Chart ((-1, 2), (t,)): + + Initial point: Point on the Real number line R with + coordinates [1/2] w.r.t. Chart ((-1, 2), (t,)) + Initial tangent vector: Tangent vector at Point on the Real + number line R with components [3/8] w.r.t. + Chart ((-1, 2), (t,)) + + d(t)/ds = Dt + d(Dt)/ds = -3/8*sin(s + 1) + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1, verbose=False) + [0.840986533989932] + + """ + + from sage.categories.homset import Hom + from sage.functions.trig import sin + from sage.functions.trig import cos + from sage.symbolic.ring import var + from sage.symbolic.expression import Expression + + dom = self.domain() + t = dom.canonical_coordinate() + t_min = dom.lower_bound() # this is either an expression or a + # finite value thanks to tests in '__init__' + t_max = dom.upper_bound() # this is either an expression or a + # finite value thanks to tests in '__init__' + + codom = self.codomain() + dim = codom.dim() + chart2 = codom.default_chart() + # In case the codomain coincides with the domain, + # it is important to distinguish between the canonical + # coordinate, and the curve parameter since, in such a + # situation, the coordinate should not be used to denote the + # curve parameter, since it actually becomes a function of the + # curve parameter, and such a function is an unknown of the + # system defining the curve. + # In other cases, it might still happen for a coordinate of the + # codomain to be denoted the same as the canonical coordinate of + # the domain (for instance, the codomain could be another + # real interval, different from the domain, and yet with same + # letter denoting its canonical coordinate). + # In such case, an error is raised from method 'init' + # of class IntegratedCurve; to solve it, the user is + # free to change the name of the codomain coordinate in the + # chart used on the codomain. + if dom == codom: + param = var('s') + if t == param: # the canonical coordinate of the domain + # might be the expression 's' even though it was affected + # above to the variable 't' + param = var('u') + else: + param = t + + # An analytical curve is used to find a region of the codomain + # where a certain integrated curve may be defined: + H = Hom(dom, codom) + c = H.an_element() + x0_A = c.expr()[0].substitute({t:1}) + x0_B = c.expr()[0].substitute({t:0}) # necessarily, x0_A < x0_B + p_coords = [x0_A] + list(c.expr()[1:dim]) + p = codom.point(p_coords) + + # The initial tangent vector: + v_comps = [(x0_B-x0_A)/2] + [0 for i in range(dim-1)] + v = codom.tangent_space(p)(v_comps) + + # The equations defining the curve: + eqns_rhs=[-(x0_B-x0_A)/2*sin(param-t_min)]+[0 for i in range(dim-1)] + # combined with the initial components above, all velocities + # vanish, except the first one, which is a cosine function. + # This differential system results in a curve constant in all + # its coordinates, except the first one, which oscillates around + # the value 'x0_A' with an amplitude '(x0_B-x0_A)/2' + + # The symbolic expressions for the velocities: + vels = chart2.symbolic_velocities() + + return self.element_class(self,eqns_rhs,vels,param,v) + + @cached_method + def one(self): + r""" + Return the identity element of ``self`` considered as a monoid + (case of a set of endomorphisms). + + This applies only when the codomain of the homset is equal to its + domain, i.e. when the homset is of the type `\mathrm{Hom}(M,M)`. + Indeed, `\mathrm{Hom}(M,M)` equipped with the law of morphisms + composition is a monoid, whose identity element is nothing but the + identity map of `M`. + + OUTPUT: + + - the identity map of `M`, as an instance of + :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` + + EXAMPLES: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedCurveSet(I, I); H + Set of Morphisms from Real interval (-1, 2) to Real interval + (-1, 2) in Category of endsets of subobjects of sets and + topological spaces which actually are integrated curves + sage: e = H.one() ; e + Integrated curve Id_(-1, 2) in the Real interval (-1, 2) + + """ + + from sage.symbolic.ring import var + + if self.codomain() != self.domain(): + raise TypeError("{} is not a monoid".format(self)) + + t = self.domain().canonical_coordinate() + # It is important to distinguish between the canonical + # coordinate, and the curve parameter since, in such a + # situation, the coordinate should not be used to denote the + # curve parameter, since it actually becomes a function of the + # curve parameter, and such a function is an unknown of the + # system defining the curve. + param = var('s') + if t == param: # the canonical coordinate of the domain + # might be the expression 's' even though it was affected + # above to the variable 't' + param = var('u') + + vel = [var("D{}".format(t))] + + # Below, set argument to 'None' where the value does not have + # any influence since argument 'is_identity' is set to 'True' + return self.element_class(self, None, vel, param, None, + is_identity=True) + +#****************************************************************************** + +class IntegratedAutoparallelCurveSet(IntegratedCurveSet): + r""" + Set of integrated autoparallel curves in a differentiable manifold. + + INPUT: + + - ``domain`` -- + :class:`~sage.manifolds.differentiable.real_line.OpenInterval` + open interval `I \subset \RR` with finite boundaries (domain of + the morphisms) + - ``codomain`` -- + :class:`~sage.manifolds.differentiable.manifold.DifferentiableManifold`; + differentiable manifold `M` (codomain of the morphisms) + - ``name`` -- (default: ``None``) string; name given to the set of + integrated autoparallel curves; if ``None``, + ``Hom_integrated_autoparallel(I, M)`` will be used + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote + the set of integrated autoparallel curves; if ``None``, + `\mathrm{Hom_{integrated autoparallel}}(I,M)` will be used + + EXAMPLES: + + TO DO ! + + """ + + Element = IntegratedAutoparallelCurve + + def __init__(self, domain, codomain, name=None, latex_name=None): + r""" + Initialize ``self``. + + TESTS:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: H = IntegratedAutoparallelCurveSet(R, M) + Traceback (most recent call last): + ... + ValueError: Both boundaries of the interval defining the + domain of a Homset of integrated autoparallel curves need + to be finite. + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedAutoparallelCurveSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 3-dimensional + differentiable manifold M in Category of homsets of + subobjects of sets and topological spaces which actually are + integrated autoparallel curves w.r.t a certain affine connection + sage: TestSuite(H).run() + sage: H = IntegratedAutoparallelCurveSet(I, I); H + Set of Morphisms from Real interval (-1, 2) to Real interval + (-1, 2) in Category of endsets of subobjects of sets and + topological spaces which actually are integrated + autoparallel curves w.r.t a certain affine connection + sage: TestSuite(H).run() + + """ + + from sage.rings.infinity import Infinity + + DifferentiableCurveSet.__init__(self, domain, codomain, + name=name, latex_name=latex_name) + + # checking argument 'domain' + t_min = domain.lower_bound() + t_max = domain.upper_bound() + if t_min == -Infinity or t_max == +Infinity: + raise ValueError("Both boundaries of the interval " + + "defining the domain of a Homset of " + + "integrated autoparallel curves need to " + + "be finite.") + + if name is None: + self._name = "Hom_integrated_autoparallel" + self._name += "({},{})".format(domain._name, codomain._name) + else: + self._name = name + if latex_name is None: + self._latex_name=r"\mathrm{{Hom}_{integrated autoparallel}}" + self._latex_name+= r"\left({},{}\right)".format( + domain._latex_name, codomain._latex_name) + else: + self._latex_name = latex_name + + #### Parent methods #### + + def _repr_(self): + """ + TESTS:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedAutoparallelCurveSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 3-dimensional + differentiable manifold M in Category of homsets of + subobjects of sets and topological spaces which actually + are integrated autoparallel curves w.r.t a certain affine + connection + + """ + + description = "Set of Morphisms " + description += "from {} to {} in {} ".format(self._domain, + self._codomain, self.category()) + description += "which actually are integrated autoparallel " + description += "curves w.r.t a certain affine connection" + return description + + + def _element_constructor_(self, affine_connection, curve_parameter, + initial_tangent_vector, chart=None, name=None, + latex_name=None, is_isomorphism=False, + is_identity=False, verbose=True): + r""" + Construct an element of ``self``, i.e. an integrated + autoparallel curve `I \to M`, where `I` is a real interval and + `M` some differentiable manifold. + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` + + EXAMPLE:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedAutoparallelCurveSet(I, M) + sage: nab = M.affine_connection('nabla') + sage: nab[0,1,0], nab[0,0,1] = 1,2 + sage: nab.torsion()[:] + [[[0, -1], [1, 0]], [[0, 0], [0, 0]]] + sage: t = var('t') + sage: p = M.point((3,4)) + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,2)) + sage: c = H(nab, t, v, name='c') ; c + Integrated autoparallel curve c in the 2-dimensional + differentiable manifold M + + """ + # Standard construction + return self.element_class(self, affine_connection, + curve_parameter, initial_tangent_vector, chart=chart, + name=name,latex_name=latex_name, + is_isomorphism=is_isomorphism, is_identity=is_identity, + verbose=verbose) + + def _an_element_(self): + r""" + Construct some element of ``self``. + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` + + EXAMPLE:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedAutoparallelCurveSet(I, M) + sage: c = H._an_element_() ; c + Integrated autoparallel curve in the 2-dimensional + differentiable manifold M + sage: sys = c.system() + Autoparallel curve in the 2-dimensional differentiable + manifold M equipped with Affine connection nab on the Open + subset U of the 2-dimensional differentiable manifold M, + and integrated over the Real interval (-1, 2) as a solution + to the following equations, written w.r.t. + Chart (M, (x, y)): + + Initial point: Point on the 2-dimensional differentiable + manifold M with coordinates [0.0312500000000000, -1/2] + w.r.t. Chart (M, (x, y)) + Initial tangent vector: Tangent vector at Point on the + 2-dimensional differentiable manifold M with components + [0.156250000000000, 2.07698341289763] + w.r.t. Chart (M, (x, y)) + + d(x)/dt = Dx + d(y)/dt = Dy + d(Dx)/dt = 0 + d(Dy)/dt = -Dx*Dy*cos(x)/sin(x) + + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1, verbose=False) + [0.34375000000000056, 0.49999813529956155] + sage: H = IntegratedAutoparallelCurveSet(I, I) + sage: c = H._an_element_() ; c + Integrated autoparallel curve in the Real interval (-1, 2) + sage: sys = c.system() + Autoparallel curve in the Real interval (-1, 2) equipped + with Affine connection nab on the Open subset U of the Real + number line R, and integrated over the Real + interval (-1, 2) as a solution to the following equations, + written w.r.t. Chart ((-1, 2), (t,)): + + Initial point: Point on the Real number line R with + coordinates [1/2] w.r.t. Chart ((-1, 2), (t,)) + Initial tangent vector: Tangent vector at Point on the Real + number line R with components [7/16] w.r.t. + Chart ((-1, 2), (t,)) + + d(t)/ds = Dt + d(Dt)/ds = -Dt^2/t + + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1, verbose=False) + [1.0606601601128764] + + """ + + from sage.categories.homset import Hom + from sage.symbolic.ring import var + from sage.functions.trig import sin + from sage.functions.trig import tan + from sage.functions.log import ln + from sage.symbolic.constants import pi + from sage.rings.infinity import Infinity + from sage.rings.rational_field import QQ + + dom = self.domain() + t = dom.canonical_coordinate() + t_min = dom.lower_bound() # this is either an expression or a + # finite value thanks to tests in '__init__' + t_max = dom.upper_bound() # idem + + codom = self.codomain() + dim = codom.dim() + i0 = codom.start_index() + chart2 = codom.default_chart() + th = chart2[:][0] + # In case the codomain coincides with the domain, + # it is important to distinguish between the canonical + # coordinate, and the curve parameter since, in such a + # situation, the coordinate should not be used to denote the + # curve parameter, since it actually becomes a function of the + # curve parameter, and such a function is an unknown of the + # system defining the curve. + # In other cases, it might still happen for a coordinate of the + # codomain to be denoted the same as the canonical coordinate of + # the domain (for instance, the codomain could be another + # real interval, different from the domain, and yet with same + # letter denoting its canonical coordinate). + # In such case, an error is raised from method 'init' + # of class IntegratedCurve; to solve it, the user is + # free to change the name of the codomain coordinate in the + # chart used on the codomain. + if dom == codom: + param = var('s') + else: + param = t + + # An analytical curve is used to find a region of the codomain + # where a certain integrated autoparallel curve may be defined: + H = Hom(dom, codom) + c = H.an_element() + th_A = c.expr()[0].substitute({t:1}) + th_B = c.expr()[0].substitute({t:0}) # necessarily, th_A < th_B + + if dim == 1: + # Redefine 'th_A' and 'th_B' so that they are of the same + # sign and such that th_A < th_B still holds. + # This will avoid division by zero in the equations + # defining autoparallel curves w.r.t. the connection defined + # below + if th_A.numerical_approx().sign() != th_B.numerical_approx().sign(): + # knowing that th_A < th_B + if th_B > 0: + th_A = th_B/100 + else: # th_A is necessarily strictly less than 0 + th_B = th_A/100 + + # The initial point: + p = codom.point([th_A]) + + # The initial tangent vector: + th_dot_A = (th_B**2-th_A**2)/(2*th_A*(t_max-t_min)) + v = codom.tangent_space(p)([th_dot_A]) + + U = codom.open_subset('U') + chart2_U = chart2.restrict(U, [th > th_A, th < th_B]) # this + # is to prevent from problems arising from a division by + # zero in the equations defining autoparallel curves w.r.t. + # the connection defined below + nab = U.affine_connection('nab') + nab.set_coef()[i0,i0,i0] = 1/th + return self.element_class(self, nab, param, v) # the + # autoparallel curve returned will correspond to the + # following analytical solution: + # th(t) = (th_A*(th_A + 2*th_dot_A*(t-t_min)))^(1/2) + + # else: (i.e. dim >= 2) + + # in dimension greater than 1, the idea is to consider the first + # two coordinates of the chart to be the polar coordinates on the + # unit 2-sphere, in order to reproduce autoparallel curves on + # the sphere with respect to the Mercator connection (which is + # described in the example introducing the class + # IntegratedAutoparallelCurve) + + # Redefine 'th_A' and 'th_B' so that [th_A, th_B] is contained + # within an interval (k*pi, (k+1)*pi), k being an integer. + # This is needed to take the inverse of the sine on [th_A, th_B] + # and to avoid problems from evaluating the tangent of 'th/2', + # which appear in the equations: + + # th_A = k_A*pi + r_A, k_A integer, 0 <= r_A < pi + # idem for th_B + r_A = th_A.numerical_approx()%pi.numerical_approx() + r_B = th_B.numerical_approx()%pi.numerical_approx() + k_A = (th_A-r_A)/pi + k_B = (th_B-r_B)/pi + if k_A == k_B and r_A == 0: + th_A = k_A*pi + (th_B-th_A)/16 + elif k_A != k_B: + th_B = (k_A+1)*pi - 1/16*((k_A+1)*pi - th_A) + if r_A == 0: + th_A = k_A*pi + (th_B-th_A)/16 + + bounds = chart2._bounds[1] # bounds of second coordinate + # Determination of an interval (x1, x2) arround target_point: + ph_min = bounds[0][0] + ph_max = bounds[1][0] + one_half = QQ(1) / QQ(2) + if ph_min == -Infinity: + if ph_max == Infinity: + ph_A = - one_half + ph_B = one_half + else: + ph_A = ph_max - 3*one_half + ph_B = ph_max - one_half + else: + if ph_max == Infinity: + ph_A = ph_min + one_half + ph_B = ph_min + 3*one_half + else: + dph = (ph_max - ph_min) / 4 + ph_A = ph_min + dph + ph_B = ph_max - dph + + # The initial point: + p_coords = [th_A] + [ph_A] + list(c.expr()[2:dim]) + p = codom.point(p_coords) + + # The initial tangent vector: + th_dot_A = (th_B - th_A)/(t_max - t_min) + num_tan_alpha = ln(tan(th_A/2))-ln(tan((th_dot_A*t_max+th_A)/2)) + denom_tan_alpha = ph_B - ph_A + tan_alpha = num_tan_alpha/denom_tan_alpha + ph_dot_A = - th_dot_A/(tan_alpha * sin(th_A)) + v_comps = [th_dot_A] + [ph_dot_A] + [0 for i in range(dim-2)] + v = codom.tangent_space(p)(v_comps) + + # The Mercator connection is reproduced on the first two + # coordinates + U = codom.open_subset('U') + chart2_U = chart2.restrict(U, [th > th_A, th < th_B]) # this is + # to prevent from problems arising from defining the orhtonormal + # basis 'epolar_ON' with the inverse of a sine + epolar_U = chart2_U.frame() + ch_basis = U.automorphism_field() + ch_basis[i0,i0], ch_basis[i0+1,i0+1] = 1, 1/sin(th) + if dim > 2: + for i in range(2,dim): + ch_basis[i0+i,i0+i] = 1 + epolar_ON_U = epolar_U.new_frame(ch_basis, 'epolar_ON') + nab = U.affine_connection('nab') # the connection is only + # defined on U since it was defined with respect to epolar_ON_U, + # which was defined on U only + nab.set_coef(epolar_ON_U)[:] # this sets all the coefficients to + # zero w.r.t. 'epolar_ON' + + return self.element_class(self, nab, param, v) + + @cached_method + def one(self): + r""" + Return the identity element of ``self`` considered as a monoid + (case of a set of endomorphisms). + + This applies only when the codomain of the homset is equal to its + domain, i.e. when the homset is of the type `\mathrm{Hom}(M,M)`. + Indeed, `\mathrm{Hom}(M,M)` equipped with the law of morphisms + composition is a monoid, whose identity element is nothing but the + identity map of `M`. + + OUTPUT: + + - the identity map of `M`, as an instance of + :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` + + EXAMPLES: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedAutoparallelCurveSet(I, I); H + Set of Morphisms from Real interval (-1, 2) to Real interval + (-1, 2) in Category of endsets of subobjects of sets and + topological spaces which actually are integrated + autoparallel curves w.r.t a certain affine connection + sage: e = H.one() ; e + Integrated autoparallel curve Id_(-1, 2) in the Real + interval (-1, 2) + + """ + + from sage.symbolic.ring import var + + if self.codomain() != self.domain(): + raise TypeError("{} is not a monoid".format(self)) + + t = self.domain().canonical_coordinate() + # It is important to distinguish between the canonical + # coordinate, and the curve parameter since, in such a + # situation, the coordinate should not be used to denote the + # curve parameter, since it actually becomes a function of the + # curve parameter, and such a function is an unknown of the + # system defining the curve. + param = var('s') + if t == param: # the canonical coordinate of the domain + # might be the expression 's' even though it was affected + # above to the variable 't' + param = var('u') + + vel = [var("D{}".format(t))] + + # Below, set argument to 'None' where the value does not have any + # influence since argument 'is_identity' is set to 'True' + # As a result, it does not need to be overriden in + # IntegratedGeodesicSet since the signature of the 'init' + # methods of the classes IntegratedAutoparallelCurve and + # IntegratedGeodesic are the same, except argument + # 'affine_connection' of the first one is replaced by 'metric' + # in the second one. + # Since both do not have any influence on the definition of the + # curve when argument 'is_identity' is set to 'True', the class + # IntegratedGeodesicSet may use this method 'one' with arguments + # set to 'None' in the same places. + return self.element_class(self, None, param, None, + is_identity=True) + + +#****************************************************************************** + +class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): + r""" + Set of integrated geodesic in a differentiable manifold. + + INPUT: + + - ``domain`` -- + :class:`~sage.manifolds.differentiable.real_line.OpenInterval` + open interval `I \subset \RR` with finite boundaries (domain of + the morphisms) + - ``codomain`` -- + :class:`~sage.manifolds.differentiable.manifold.DifferentiableManifold`; + differentiable manifold `M` (codomain of the morphisms) + - ``name`` -- (default: ``None``) string; name given to the set of + integrated geodesics; if ``None``, ``Hom(I, M)`` will be used + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote + the set of integrated geodesics; if ``None``, + `\mathrm{Hom_{geodesic}}(I,M)` will be used + + EXAMPLES: + + TO DO ! + + """ + + Element = IntegratedGeodesic + + def __init__(self, domain, codomain, name=None, latex_name=None): + r""" + Initialize ``self``. + + TESTS:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedGeodesicSet + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: H = IntegratedGeodesicSet(R, M) + Traceback (most recent call last): + ... + ValueError: Both boundaries of the interval defining the + domain of a Homset of integrated geodesics need to be + finite. + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedGeodesicSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 3-dimensional + differentiable manifold M in Category of homsets of + subobjects of sets and topological spaces which actually + are integrated geodesics w.r.t a certain metric + sage: TestSuite(H).run() + sage: H = IntegratedGeodesicSet(I, I); H + Set of Morphisms from Real interval (-1, 2) to Real interval + (-1, 2) in Category of endsets of subobjects of sets and + topological spaces which actually are integrated geodesics + w.r.t a certain metric + sage: TestSuite(H).run() + + """ + + from sage.rings.infinity import Infinity + + DifferentiableCurveSet.__init__(self, domain, codomain, + name=name, latex_name=latex_name) + + # checking argument 'domain' + t_min = domain.lower_bound() + t_max = domain.upper_bound() + if t_min == -Infinity or t_max == +Infinity: + raise ValueError("Both boundaries of the interval " + + "defining the domain of a Homset of " + + "integrated geodesics need to be finite.") + + if name is None: + self._name = "Hom_geodesic" + self._name += "({},{})".format(domain._name, codomain._name) + else: + self._name = name + if latex_name is None: + self._latex_name = r"\mathrm{{Hom}_{geodesic}}" + self._latex_name+= r"\left({},{}\right)".format( + domain._latex_name, codomain._latex_name) + else: + self._latex_name = latex_name + + #### Parent methods #### + + def _repr_(self): + """ + TESTS:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedGeodesicSet + sage: M = Manifold(3, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedGeodesicSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 3-dimensional + differentiable manifold M in Category of homsets of + subobjects of sets and topological spaces which actually + are integrated geodesics w.r.t a certain metric + + """ + description = "Set of Morphisms " + description += "from {} to {} in {} ".format(self._domain, + self._codomain, self.category()) + description += "which actually are integrated geodesics " + description += "w.r.t a certain metric" + return description + + def _element_constructor_(self, metric, curve_parameter, + initial_tangent_vector, chart=None, name=None, + latex_name=None, is_isomorphism=False, + is_identity=False, verbose=True): + r""" + Construct an element of ``self``, i.e. an integrated geodesic + `I \to M`, where `I` is a real interval and + `M` some differentiable manifold. + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic` + + EXAMPLE:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedGeodesicSet + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedGeodesicSet(I, M) + sage: g = M.metric('g') + sage: g[0,0], g[1,1], g[0,1] = 1, 1, 2 + sage: t = var('t') + sage: p = M.point((3,4)) + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,2)) + sage: c = H(g, t, v, name='c') ; c + Integrated geodesic c in the 2-dimensional differentiable + manifold M + + """ + # Standard construction + return self.element_class(self, metric, curve_parameter, + initial_tangent_vector, chart=chart, name=name, + latex_name=latex_name, is_isomorphism=is_isomorphism, + is_identity=is_identity, verbose=verbose) + + def _an_element_(self): + r""" + Construct some element of ``self``. + + OUTPUT: + + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic` + + EXAMPLE:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedGeodesicSet + sage: M = Manifold(4, 'M', start_index=1) + sage: X. = M.chart() + sage: R. = RealLine() + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedGeodesicSet(I, M) + sage: c = H._an_element_() ; c + Integrated geodesic in the 4-dimensional differentiable + manifold M + sage: sys = c.system() + Geodesic in the 4-dimensional differentiable manifold M + equipped with Riemannian metric g on the 4-dimensional + differentiable manifold M, and integrated over the Real + interval (-1, 2) as a solution to the following geodesic + equations, written w.r.t. Chart (M, (w, x, y, z)): + + Initial point: Point on the 4-dimensional differentiable + manifold M with coordinates [0.0312500000000000, 0, 0, 0] + w.r.t. Chart (M, (w, x, y, z)) + Initial tangent vector: Tangent vector at Point on the + 4-dimensional differentiable manifold M with components + [0.156250000000000, 0, 0, 0] w.r.t. Chart (M, (w, x, y, z)) + + d(w)/dt = Dw + d(x)/dt = Dx + d(y)/dt = Dy + d(z)/dt = Dz + d(Dw)/dt = Dx^2*cos(w)*sin(w) + d(Dx)/dt = -2*Dw*Dx*cos(w)/sin(w) + d(Dy)/dt = 0 + d(Dz)/dt = 0 + + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1, verbose=False) + [0.34375000000000056, 0.0, 0.0, 0.0] + sage: H = IntegratedGeodesicSet(I, I) + sage: c = H._an_element_() ; c + Integrated geodesic in the Real interval (-1, 2) + sage: sys = c.system() + Geodesic in the Real interval (-1, 2) equipped with + Riemannian metric g on the Open subset U of the Real number + line R, and integrated over the Real interval (-1, 2) as a + solution to the following geodesic equations, written + w.r.t. Chart ((-1, 2), (t,)): + + Initial point: Point on the Real number line R with + coordinates [1/2] w.r.t. Chart ((-1, 2), (t,)) + Initial tangent vector: Tangent vector at Point on the Real + number line R with components [1031/320] w.r.t. + Chart ((-1, 2), (t,)) + + d(t)/ds = Dt + d(Dt)/ds = -4*Dt^2/t + + sage: sol = c.solve(verbose=False) + sage: interp = c.interpolate(verbose=False) + sage: c(1, verbose=False) + [1.1495758053215723] + + """ + + from sage.categories.homset import Hom + from sage.symbolic.ring import var + from sage.functions.trig import sin + from sage.symbolic.constants import pi + + dom = self.domain() + t = dom.canonical_coordinate() + t_min = dom.lower_bound() # this is either an expression or a + # finite value thanks to tests in '__init__' + t_max = dom.upper_bound() # idem + + codom = self.codomain() + dim = codom.dim() + i0 = codom.start_index() + chart2 = codom.default_chart() + th = chart2[:][0] + # In case the codomain coincides with the domain, + # it is important to distinguish between the canonical + # coordinate, and the curve parameter since, in such a + # situation, the coordinate should not be used to denote the + # curve parameter, since it actually becomes a function of the + # curve parameter, and such a function is an unknown of the + # system defining the curve. + # In other cases, it might still happen for a coordinate of the + # codomain to be denoted the same as the canonical coordinate of + # the domain (for instance, the codomain could be another + # real interval, different from the domain, and yet with same + # letter denoting its canonical coordinate). + # In such case, an error is raised from method 'init' + # of class IntegratedCurve; to solve it, the user is + # free to change the name of the codomain coordinate in the + # chart used on the codomain. + if dom == codom: + param = var('s') + else: + param = t + + # An analytical curve is used to find a region of the codomain + # where a certain integrated autoparallel curve may be defined: + H = Hom(dom, codom) + c = H.an_element() + th_A = c.expr()[0].substitute({t:1}) + th_B = c.expr()[0].substitute({t:0}) # necessarily, th_A < th_B + + if dim == 1: + # Redefine 'th_A' and 'th_B' so that they are of the same + # sign and such that th_A < th_B still holds. + # This will avoid division by zero in the equations + # defining geodesics w.r.t. the metric defined below. + if th_A.numerical_approx().sign() != th_B.numerical_approx().sign(): + # knowing that th_A < th_B + if th_B > 0: + th_A = th_B/100 + else: # th_A is necessarily strictly less than 0 + th_B = th_A/100 + + # The initial point: + p = codom.point([th_A]) + + # The initial tangent vector: + th_dot_A = (th_B**5-th_A**5)/(5*(th_A**4)*(t_max-t_min)) + v = codom.tangent_space(p)([th_dot_A]) + + U = codom.open_subset('U') + chart2_U = chart2.restrict(U, [th > th_A, th < th_B]) # this + # is to prevent from problems arising from a division by + # zero in the equations defining autoparallel curves w.r.t. + # the connection defined below + g = U.metric('g') + g[i0,i0] = th**8 + return self.element_class(self, g, param, v) # the + # autoparallel curve returned will correspond to the + # following analytical solution: + # th(t) = (th_A^4*(th_A + 5*th_dot_A*(t-t_min)))^(1/5) + + # else: (i.e. dim >= 2) + + # in dimension greater than 1, the idea is to consider the first + # two coordinates of the chart to be the polar coordinates on + # the unit 2-sphere, in order to reproduce a line of longitude, + # which is a geodesic on the sphere w.r.t. the standard metric + # induced by the 3D Euclidean metric on the sphere. + + # Redefine 'th_A' and 'th_B' so that [th_A, th_B] is contained + # within an interval (k*pi, (k+1)*pi), k being an integer. + # This is needed to take the inverse of the tangent function on + # [th_A, th_B] in the geodesic equations: + + # th_A = k_A*pi + r_A, k_A integer, 0 <= r_A < pi + # idem for th_B + r_A = th_A.numerical_approx()%pi.numerical_approx() + r_B = th_B.numerical_approx()%pi.numerical_approx() + k_A = (th_A-r_A)/pi + k_B = (th_B-r_B)/pi + if k_A == k_B and r_A == 0: + th_A = k_A*pi + (th_B-th_A)/16 + elif k_A != k_B: + th_B = (k_A+1)*pi - 1/16*((k_A+1)*pi - th_A) + if r_A == 0: + th_A = k_A*pi + (th_B-th_A)/16 + + # The initial point: + p_coords = [th_A] + list(c.expr()[1:dim]) + p = codom.point(p_coords) + + # The initial tangent vector: + th_dot_A = (th_B - th_A)/(t_max - t_min) + v_comps = [th_dot_A] + [0 for i in range(dim-1)] + v = codom.tangent_space(p)(v_comps) + + # The metric of the unit 2-sphere is reproduced on the first two + # coordinates + g = codom.metric('g') + g[i0,i0] = 1 + g[i0+1,i0+1] = sin(th)**2 + if dim > 2: + for i in range(2,dim): + g[i0+i,i0+i] = 1 + + return self.element_class(self, g, param, v) From 1adbcdc288727d60413f08b0dac7a78cae86dd48 Mon Sep 17 00:00:00 2001 From: Karim Van Aelst Date: Tue, 27 Jun 2017 10:01:05 +0200 Subject: [PATCH 047/184] corrections to doctests, documentation, iteration over dictionary, and verbose set to False everywhere --- .../differentiable/integrated_curve.py | 482 +++++++++--------- src/sage/manifolds/differentiable/manifold.py | 58 +-- .../differentiable/manifold_homset.py | 335 +++++++++--- 3 files changed, 536 insertions(+), 339 deletions(-) diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index 069a4e21b99..0e065de7c44 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -2,8 +2,8 @@ Integrated Curves in Manifolds Given a differentiable manifold `M`, an *integrated curve* curve in `M` -is a differentiable curve constructed as a numerical solution to a -system of second order differential equations. +is a differentiable curve constructed as a solution to a system of +second order differential equations. Integrated curves are implemented by :class:`IntegratedCurve`, which the classes :class:`IntegratedAutoparallelCurve` and @@ -40,15 +40,16 @@ class IntegratedCurve(DifferentiableCurve): Given a chart with coordinates denoted :MATH:`(x_{1}, ..., x_{n})`, an instance of :class:`IntegratedCurve` is a curve :MATH:`t \mapsto (x_{1}(t), ..., x_{n}(t))` constructed as a - numerical solution to a system of second order differential - equations satisfied by the coordinate curves + solution to a system of second order differential equations + satisfied by the coordinate curves :MATH:`t \mapsto x_{i}(t)`. INPUT: - ``parent`` -- :class:`~sage.manifolds.differentiable.manifold_homset.IntegratedCurveSet` - the set of curves `\mathrm{Hom}(I, M)` to which the curve belongs + the set of curves `\mathrm{Hom_{integrated}}(I, M)` to which the + curve belongs - ``equations_rhs`` -- list of the right-hand sides of the equations on the velocities only (the term *velocity* referring to the derivatives :MATH:`d x_{i} / dt` of the coordinate curves) @@ -115,10 +116,14 @@ class IntegratedCurve(DifferentiableCurve): Declare an integrated curve and display information relative to it:: sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c', - ....: verbose=False) ; c + ....: verbose=True) + The curve was correctly set. + Parameters appearing in the differential system defining the + curve are [q, B_0, m, T, L]. + sage: c Integrated curve c in the 3-dimensional differentiable manifold M - sage: sys = c.system() + sage: sys = c.system(verbose=True) Curve c in the 3-dimensional differentiable manifold M integrated over the Real interval (0, 5) as a solution to the following system, written w.r.t. @@ -144,7 +149,7 @@ class IntegratedCurve(DifferentiableCurve): sage: sol = c.solve(step=0.2, ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, - ....: solution_key='carac time 1') + ....: solution_key='carac time 1', verbose=True) Performing 4th order Runge-Kutta integration with Maxima by default... @@ -156,7 +161,7 @@ class IntegratedCurve(DifferentiableCurve): 'carac time 1' (if this key already referred to a former numerical solution, such a solution was erased). sage: interp = c.interpolate(solution_key='carac time 1', - ....: interpolation_key='interp 1') + ....: interpolation_key='interp 1', verbose=True) Performing cubic spline interpolation by default... Interpolation completed and associated with the key 'interp 1' (if this key already referred to a former interpolation, @@ -165,11 +170,11 @@ class IntegratedCurve(DifferentiableCurve): Such an interpolation is required to evaluate the curve and the vector tangent to the curve for any value of the curve parameter:: - sage: c(1.9) + sage: c(1.9, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'interp 1' by default... [1.3776707219621374, -0.9000776970132945, 1.9] - sage: v = c.tangent_vector_eval_at(4.3) + sage: v = c.tangent_vector_eval_at(4.3, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'interp 1' by default... sage: v @@ -186,7 +191,8 @@ class IntegratedCurve(DifferentiableCurve): ....: interpolation_key='interp 1', thickness=2.5, ....: display_tangent=True, plot_points=200, ....: plot_points_tangent=10, scale=0.5, - ....: color='blue', color_tangent='red') + ....: color='blue', color_tangent='red', + ....: verbose=True) A tiny final offset equal to the value of 'end_point_offset[1]' (= 0.001) was introduced in order to safely compute the last tangent vector from the interpolation. @@ -221,19 +227,19 @@ class IntegratedCurve(DifferentiableCurve): sage: sol = c.solve(step=0.2, ....: parameters_values={B_0:1, m:1, q:1, L:10, T:100}, - ....: solution_key='carac time 100', verbose=False) + ....: solution_key='carac time 100') sage: interp = c.interpolate(solution_key='carac time 100', - ....: interpolation_key='interp 100', verbose=False) + ....: interpolation_key='interp 100') sage: c_plot_3d_100=c.plot_integrated(interpolation_key='interp 100', ....: thickness=2.5, display_tangent=True, ....: plot_points=200, plot_points_tangent=10, ....: scale=0.5, color='green', - ....: color_tangent='orange', verbose=False) + ....: color_tangent='orange') sage: c_plot_3d_1=c.plot_integrated(interpolation_key='interp 1', ....: thickness=2.5, display_tangent=True, ....: plot_points=200, plot_points_tangent=10, ....: scale=0.5, color='blue', - ....: color_tangent='red', verbose=False) + ....: color_tangent='red') sage: graph = c_plot_3d_1 + c_plot_3d_100 sage: graph.show() @@ -249,26 +255,23 @@ class IntegratedCurve(DifferentiableCurve): Tp = M.tangent_space(p) v = Tp((1,0,1)) c = M.integrated_curve(eqns, D, (t, 0, 5), v, name='c') - c.solve(step=0.2, - parameters_values={B_0:1, m:1, q:1, L:10, T:1}, - solution_key='carac time 1') + c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + solution_key='carac time 1') c.interpolate(solution_key='carac time 1', - interpolation_key='interp 1') - c.solve(step=0.2, - parameters_values={B_0:1, m:1, q:1, L:10, T:100}, - solution_key='carac time 100') + interpolation_key='interp 1') + c.solve(step=0.2, parameters_values={B_0:1, m:1, q:1, L:10, T:100}, + solution_key='carac time 100') c.interpolate(solution_key='carac time 100', - interpolation_key='interp 100') + interpolation_key='interp 100') c_plot_3d_1 = c.plot_integrated(interpolation_key='interp 1', thickness=2.5, display_tangent=True, plot_points=200, plot_points_tangent=10, - scale=0.5, color='blue', color_tangent='red', - verbose=False) + scale=0.5, color='blue', color_tangent='red') c_plot_3d_100 = c.plot_integrated(interpolation_key='interp 100', thickness=2.5, display_tangent=True, plot_points=200, plot_points_tangent=10, scale=0.5, color='green', - color_tangent='orange', verbose=False) + color_tangent='orange') graph = c_plot_3d_1 + c_plot_3d_100 sphinx_plot(graph) @@ -277,9 +280,10 @@ class IntegratedCurve(DifferentiableCurve): def __init__(self, parent, equations_rhs, velocities, curve_parameter, initial_tangent_vector, chart=None, name=None, latex_name=None, - is_isomorphism=False, is_identity=False, verbose=True): + is_isomorphism=False, is_identity=False, verbose=False): r""" - Construct a numerical curve. + Construct a curve defined by a system of second order + differential equations in the coordinate functions. TESTS:: @@ -313,11 +317,17 @@ def __init__(self, parent, equations_rhs, velocities, Traceback (most recent call last): ... TypeError: x1 should be a tangent vector. - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) ; c + + sage: c = M.integrated_curve(eqns, D, (x1,0,5), v, name='c') + Traceback (most recent call last): + ... + ValueError: x1 should not be used as the curve parameter + since it also denotes a coordinate or a velocity. + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c'); c Integrated curve c in the 3-dimensional differentiable manifold M - sage: #TestSuite(c).run() + sage: TestSuite(c).run() + """ @@ -499,7 +509,7 @@ def __init__(self, parent, equations_rhs, velocities, if len(self._parameters) != 0: print("Parameters appearing in the differential " + "system defining the curve are " + - "{}.".format(self._parameters)) + "{}.".format(sorted(self._parameters))) else: print("No parameter appears in the differential " + "system defining the curve.") @@ -519,12 +529,10 @@ def _repr_(self): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: verbose=False) ; c + sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v) ; c Integrated curve in the 3-dimensional differentiable manifold M - sage: c = M.integrated_curve(eqns, D, (t, 0, 5), v, - ....: name='c', verbose=False) ; c + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c'); c Integrated curve c in the 3-dimensional differentiable manifold M @@ -551,8 +559,7 @@ def __reduce__(self): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') sage: c.__reduce__() (, (Set of Morphisms from Real interval (0, 5) to @@ -564,7 +571,8 @@ def __reduce__(self): 0], [Dx1, Dx2, Dx3], t, - Tangent vector at Point p on the 3-dimensional differentiable manifold M, + Tangent vector at Point p on the 3-dimensional + differentiable manifold M, Chart (M, (x1, x2, x3)), 'c', 'c', @@ -585,7 +593,7 @@ def __reduce__(self): self._name, self._latex_name, self._is_isomorphism, self._is_identity)) - def system(self, verbose=True): + def system(self, verbose=False): r""" Provide a detailed description of the system defining the curve and returns the system defining it: chart, equations and initial @@ -593,7 +601,7 @@ def system(self, verbose=True): INPUT: - - ``verbose`` -- (default: ``True``) prints a detailed + - ``verbose`` -- (default: ``False``) prints a detailed description of the curve OUTPUT: @@ -614,9 +622,8 @@ def system(self, verbose=True): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) - sage: sys = c.system() + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') + sage: sys = c.system(verbose=True) Curve c in the 3-dimensional differentiable manifold M integrated over the Real interval (0, 5) as a solution to the following system, written w.r.t. @@ -636,7 +643,7 @@ def system(self, verbose=True): d(Dx2)/dt = -B_0*Dx1*q*t*e^(-(x1^2 + x2^2)/L^2)/(T*m) d(Dx3)/dt = 0 - sage: sys_mute = c.system(verbose=False) + sage: sys_mute = c.system() sage: sys_mute == sys True @@ -694,7 +701,7 @@ def system(self, verbose=True): return [self._equations_rhs, v0, chart] - def solve_analytical(self, verbose=True): + def solve_analytical(self, verbose=False): r""" Solve analytically the differential system defining the curve using Maxima via Sage solver ``desolve_system``. @@ -711,7 +718,7 @@ def solve_analytical(self, verbose=True): - list of the analytical expressions of the coordinate functions (when the differential system could be solved analytically), - or boolean 'FALSE' (in case the differential system could not + or boolean 'False' (in case the differential system could not be solved analytically) EXAMPLE: @@ -727,9 +734,8 @@ def solve_analytical(self, verbose=True): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) - sage: sys = c.system() + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') + sage: sys = c.system(verbose=True) Curve c in the 3-dimensional differentiable manifold M integrated over the Real interval (0, 5) as a solution to the following system, written w.r.t. @@ -749,7 +755,7 @@ def solve_analytical(self, verbose=True): d(Dx2)/dt = -B_0*Dx1*q/m d(Dx3)/dt = 0 - sage: sol = c.solve_analytical(verbose=False) + sage: sol = c.solve_analytical() sage: c.expr() ((B_0*q*x1_0 - Dx2_0*m*cos(B_0*q*t/m) + Dx1_0*m*sin(B_0*q*t/m) + Dx2_0*m)/(B_0*q), @@ -831,7 +837,7 @@ def solve_analytical(self, verbose=True): return tuple(coords_sol_expr) def solve(self, step=None, method=None, solution_key=None, - parameters_values=None, verbose=True): + parameters_values=None, verbose=False): r""" Integrate the curve numerically over the domain of integration. @@ -867,7 +873,7 @@ def solve(self, step=None, method=None, solution_key=None, - ``parameters_values`` -- (default: ``None``) list of numerical values of the parameters present in the system defining the curve, to be substituted in the equations before integration - - ``verbose`` -- (default: ``True``) prints information about + - ``verbose`` -- (default: ``False``) prints information about the computation in progress OUTPUT: @@ -887,13 +893,12 @@ def solve(self, step=None, method=None, solution_key=None, sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') sage: sol = c.solve(parameters_values={m:1, q:1, L:10, T:1}) Traceback (most recent call last): ... ValueError: Numerical values should be provided for each of - the parameters set([B_0, m, q, L, T]). + the parameters [q, B_0, m, T, L]. sage: sol = c.solve(method='my method', ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) Traceback (most recent call last): @@ -901,7 +906,8 @@ def solve(self, step=None, method=None, solution_key=None, ValueError: No available method of integration referred to as 'my method'. sage: sol = c.solve( - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, + ....: verbose=True) Performing 4th order Runge-Kutta integration with Maxima by default... Resulting list of points will be associated with the key @@ -914,7 +920,7 @@ def solve(self, step=None, method=None, solution_key=None, The resulting list of points was associated with the key 'rk4_maxima' (if this key already referred to a former numerical solution, such a solution was erased). - sage: sol_mute = c.solve(verbose=False, + sage: sol_mute = c.solve( ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) sage: sol_mute == sol True @@ -963,8 +969,8 @@ def solve(self, step=None, method=None, solution_key=None, raise ValueError("Numerical values should be " + "provided for each of the " + "parameters " - "{}.".format(self._parameters)) - for key in parameters_values.keys(): + "{}.".format(sorted(self._parameters))) + for key in parameters_values: parameters_values[key]=numerical_approx(parameters_values[key])#gets # numerical values in case some parameters values # contain expressions such as pi; will raise error if @@ -1197,7 +1203,7 @@ def jacobian(t,y): "numerical solution, such a solution was erased).") return self._solutions[solution_key] - def solution(self, solution_key=None, verbose=True): + def solution(self, solution_key=None, verbose=False): r""" Return the solution (list of points) associated with the given key. @@ -1207,7 +1213,7 @@ def solution(self, solution_key=None, verbose=True): - ``solution_key`` -- (default: ``None``) key which the requested numerical solution is associated to ; a default value is chosen if none is provided - - ``verbose`` -- (default: ``True``) prints information about + - ``verbose`` -- (default: ``False``) prints information about the solution returned OUTPUT: @@ -1227,12 +1233,10 @@ def solution(self, solution_key=None, verbose=True): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) - sage: sol = c.solve(verbose=False, - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, - ....: solution_key='sol_T1') - sage: sol_bis = c.solution() + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') + sage: sol = c.solve(solution_key='sol_T1', + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) + sage: sol_bis = c.solution(verbose=True) Returning the numerical solution associated with the key 'sol_T1' by default... sage: sol_bis == sol @@ -1240,14 +1244,14 @@ def solution(self, solution_key=None, verbose=True): sage: sol_ter = c.solution(solution_key='sol_T1') sage: sol_ter == sol True - sage: sol_mute = c.solution(verbose=False) + sage: sol_mute = c.solution() sage: sol_mute == sol True """ if solution_key is None: - if 'rk4_maxima' in self._solutions.keys(): + if 'rk4_maxima' in self._solutions: solution_key = 'rk4_maxima' else: solution_key = self._solutions.keys()[0] # will raise @@ -1256,7 +1260,7 @@ def solution(self, solution_key=None, verbose=True): print("Returning the numerical solution associated " + "with the key '{}' ".format(solution_key) + "by default...") - elif solution_key not in self._solutions.keys(): + elif solution_key not in self._solutions: raise ValueError("No existing key " + "'{}' ".format(solution_key) + "referring to any numerical solution.") @@ -1264,7 +1268,7 @@ def solution(self, solution_key=None, verbose=True): return self._solutions[solution_key] def interpolate(self, solution_key=None, method=None, - interpolation_key=None, verbose=True): + interpolation_key=None, verbose=False): r""" Interpolate the chosen numerical solution using the given interpolation method. @@ -1283,7 +1287,7 @@ def interpolate(self, solution_key=None, method=None, - ``interpolation_key`` -- (default: ``None``) key which the resulting interpolation will be associated to ; a default value is given if none is provided - - ``verbose`` -- (default: ``True``) prints information about + - ``verbose`` -- (default: ``False``) prints information about the interpolation in progress OUTPUT: @@ -1303,12 +1307,10 @@ def interpolate(self, solution_key=None, method=None, sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') sage: sol = c.solve(method='rk4_maxima', ....: solution_key='sol_T1', - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, - ....: verbose=False) + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) sage: interp = c.interpolate(solution_key='my solution') Traceback (most recent call last): ... @@ -1322,11 +1324,12 @@ def interpolate(self, solution_key=None, method=None, as 'my method'. sage: interp = c.interpolate(method='cubic spline', ....: solution_key='sol_T1', - ....: interpolation_key='interp_T1') + ....: interpolation_key='interp_T1', + ....: verbose=True) Interpolation completed and associated with the key 'interp_T1' (if this key already referred to a former interpolation, such an interpolation was erased). - sage: interp = c.interpolate() + sage: interp = c.interpolate(verbose=True) Interpolating the numerical solution associated with the key 'sol_T1' by default... Performing cubic spline interpolation by default... @@ -1340,7 +1343,7 @@ def interpolate(self, solution_key=None, method=None, """ if solution_key is None: - if 'rk4_maxima' in self._solutions.keys(): + if 'rk4_maxima' in self._solutions: solution_key = 'rk4_maxima' else: solution_key = self._solutions.keys()[0] # will raise @@ -1350,7 +1353,7 @@ def interpolate(self, solution_key=None, method=None, "associated with the key " + "'{}' ".format(solution_key) + "by default...") - elif solution_key not in self._solutions.keys(): + elif solution_key not in self._solutions: raise ValueError("No existing key " + "'{}' ".format(solution_key) + "referring to any numerical solution.") @@ -1389,7 +1392,7 @@ def interpolate(self, solution_key=None, method=None, return self._interpolations[interpolation_key] - def interpolation(self, interpolation_key=None, verbose=True): + def interpolation(self, interpolation_key=None, verbose=False): r""" Return the interpolation object associated with the given key. @@ -1398,7 +1401,7 @@ def interpolation(self, interpolation_key=None, verbose=True): - ``interpolation_key`` -- (default: ``None``) key which the requested interpolation is associated to ; a default value is chosen if none is provided - - ``verbose`` -- (default: ``True``) prints information about + - ``verbose`` -- (default: ``False``) prints information about the interpolation object returned OUTPUT: @@ -1418,33 +1421,31 @@ def interpolation(self, interpolation_key=None, verbose=True): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') sage: sol = c.solve(method='rk4_maxima', ....: solution_key='sol_T1', - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, - ....: verbose=False) + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) sage: interp = c.interpolate(method='cubic spline', - ....: solution_key='sol_T1', verbose=False, - ....: interpolation_key='interp_T1') + ....: solution_key='sol_T1', + ....: interpolation_key='interp_T1') sage: c.interpolation(interpolation_key='my interp') Traceback (most recent call last): ... ValueError: No existing key 'my interp' referring to any interpolation. - sage: default_interp = c.interpolation() + sage: default_interp = c.interpolation(verbose=True) Returning the interpolation associated with the key 'interp_T1' by default... sage: default_interp == interp True - sage: interp_mute = c.interpolation(verbose=False) + sage: interp_mute = c.interpolation() sage: interp_mute == interp True """ if interpolation_key==None: - if 'cubic spline' in self._interpolations.keys(): + if 'cubic spline' in self._interpolations: interpolation_key = 'cubic spline' else: interpolation_key = self._interpolations.keys()[0]#will @@ -1453,7 +1454,7 @@ def interpolation(self, interpolation_key=None, verbose=True): print("Returning the interpolation associated with " + "the key '{}' ".format(interpolation_key) + "by default...") - elif interpolation_key not in self._interpolations.keys(): + elif interpolation_key not in self._interpolations: raise ValueError("No existing key " + "'{}' ".format(interpolation_key) + "referring to any interpolation.") @@ -1461,7 +1462,7 @@ def interpolation(self, interpolation_key=None, verbose=True): return self._interpolations[interpolation_key] def __call__(self, t, interpolation_key=None, - verbose=True): + verbose=False): r""" Return the image of the curve for the given value of the curve parameter, using the chosen interpolation. @@ -1472,7 +1473,7 @@ def __call__(self, t, interpolation_key=None, - ``interpolation_key`` -- (default: ``None``) key which the interpolation requested to compute the point is associated to ; a default value is chosen if none is provided - - ``verbose`` -- (default: ``True``) prints information about + - ``verbose`` -- (default: ``False``) prints information about the interpolation used OUTPUT: @@ -1491,31 +1492,29 @@ def __call__(self, t, interpolation_key=None, sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') sage: sol = c.solve(method='rk4_maxima', ....: solution_key='sol_T1', - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, - ....: verbose=False) + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) sage: interp = c.interpolate(method='cubic spline', - ....: solution_key='sol_T1', verbose=False, - ....: interpolation_key='interp_T1') + ....: solution_key='sol_T1', + ....: interpolation_key='interp_T1') sage: c(1.1, interpolation_key='my interp') Traceback (most recent call last): ... ValueError: No existing key 'my interp' referring to any interpolation. - sage: c(1.1) + sage: c(1.1, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'interp_T1' by default... [1.060743343394347, -0.2153835404373033, 1.1] - sage: pt = c(1.1, verbose=False); pt + sage: pt = c(1.1); pt [1.060743343394347, -0.2153835404373033, 1.1] """ if interpolation_key==None: - if 'cubic spline' in self._interpolations.keys(): + if 'cubic spline' in self._interpolations: interpolation_key = 'cubic spline' else: interpolation_key = self._interpolations.keys()[0]#will @@ -1524,7 +1523,7 @@ def __call__(self, t, interpolation_key=None, print("Evaluating point coordinates from the " + "interpolation associated with the key " + "'{}' by default...".format(interpolation_key)) - elif interpolation_key not in self._interpolations.keys(): + elif interpolation_key not in self._interpolations: raise ValueError("No existing key " + "'{}' ".format(interpolation_key) + "referring to any interpolation.") @@ -1541,7 +1540,7 @@ def __call__(self, t, interpolation_key=None, raise TypeError("Unexpected type of interpolation object.") def tangent_vector_eval_at(self, t, - interpolation_key=None, verbose=True): + interpolation_key=None, verbose=False): r""" Return the vector tangent to the curve at the given curve parameter with components evaluated from the given @@ -1554,7 +1553,7 @@ def tangent_vector_eval_at(self, t, - ``interpolation_key`` -- (default: ``None``) key which the interpolation requested to compute the tangent vector is associated to ; a default value is chosen if none is provided - - ``verbose`` -- (default: ``True``) prints information about + - ``verbose`` -- (default: ``False``) prints information about the interpolation used OUTPUT: @@ -1575,30 +1574,28 @@ def tangent_vector_eval_at(self, t, sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c', - ....: verbose=False) + sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') sage: sol = c.solve(method='rk4_maxima', ....: solution_key='sol_T1', - ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, - ....: verbose=False) + ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) sage: interp = c.interpolate(method='cubic spline', - ....: solution_key='sol_T1', verbose=False, - ....: interpolation_key='interp_T1') + ....: solution_key='sol_T1', + ....: interpolation_key='interp_T1') sage: tg_vec = c.tangent_vector_eval_at(1.22, ....: interpolation_key='my interp') Traceback (most recent call last): ... ValueError: No existing key 'my interp' referring to any interpolation. - sage: tg_vec = c.tangent_vector_eval_at(1.22); tg_vec + sage: tg_vec = c.tangent_vector_eval_at(1.22, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'interp_T1' by default... + sage: tg_vec Tangent vector at Point on the 3-dimensional differentiable manifold M sage: tg_vec[:] [0.7392639473853356, -0.6734182305341726, 1.0000000000000007] sage: tg_vec_mute = c.tangent_vector_eval_at(1.22, - ....: verbose=False, ....: interpolation_key='interp_T1') sage: tg_vec_mute == tg_vec True @@ -1606,7 +1603,7 @@ def tangent_vector_eval_at(self, t, """ if interpolation_key==None: - if 'cubic spline' in self._interpolations.keys(): + if 'cubic spline' in self._interpolations: interpolation_key = 'cubic spline' else: interpolation_key = self._interpolations.keys()[0]#will @@ -1615,7 +1612,7 @@ def tangent_vector_eval_at(self, t, print("Evaluating tangent vector components from the " + "interpolation associated with the key " + "'{}' by default...".format(interpolation_key)) - elif interpolation_key not in self._interpolations.keys(): + elif interpolation_key not in self._interpolations: raise ValueError("No existing key " + "'{}' ".format(interpolation_key) + "referring to any interpolation.") @@ -1646,7 +1643,7 @@ def tangent_vector_eval_at(self, t, def plot_integrated(self, chart=None, ambient_coords=None, mapping=None, prange=None, interpolation_key=None, include_end_point=(True, True), - end_point_offset=(0.001, 0.001), verbose=True, color='red', + end_point_offset=(0.001, 0.001), verbose=False, color='red', style='-', label_axes=True, display_tangent=False, color_tangent='blue', **kwds): r""" @@ -1664,7 +1661,7 @@ def plot_integrated(self, chart=None, ambient_coords=None, - ``interpolation_key`` -- (default: ``None``) key associated to the interpolation object used for the plot ; a default value is chosen if none is provided - - ``verbose`` -- (default: ``True``) prints information about + - ``verbose`` -- (default: ``False``) prints information about the interpolation object used and the plotting in progress EXAMPLE: @@ -1681,15 +1678,15 @@ def plot_integrated(self, chart=None, ambient_coords=None, sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 6), v, - ....: name='c', verbose=False) - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) + sage: c = M.integrated_curve(eqns, D, (t,0,6), v, name='c') + sage: sol = c.solve() + sage: interp = c.interpolate() sage: c_plot_2d = c.plot_integrated(ambient_coords=[x1, x2], ....: thickness=2.5, ....: display_tangent=True, plot_points=200, ....: plot_points_tangent=10, scale=0.5, - ....: color='blue', color_tangent='red') + ....: color='blue', color_tangent='red', + ....: verbose=True) Plotting from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... A tiny final offset equal to the value of @@ -1708,10 +1705,9 @@ def plot_integrated(self, chart=None, ambient_coords=None, p = M.point((0,0,0), name='p') Tp = M.tangent_space(p) v = Tp((1,0,1)) - c = M.integrated_curve(eqns, D, (t, 0, 6), v, name='c', - ....: verbose=False) - c.solve(verbose=False) - c.interpolate(verbose=False) + c = M.integrated_curve(eqns, D, (t, 0, 6), v, name='c') + c.solve() + c.interpolate() c_plot_2d_1 = c.plot_integrated(ambient_coords=[x1, x2], thickness=2.5, display_tangent=True, plot_points=200, @@ -1734,7 +1730,7 @@ def plot_integrated(self, chart=None, ambient_coords=None, # Interpolation to use # if interpolation_key==None: - if 'cubic spline' in self._interpolations.keys(): + if 'cubic spline' in self._interpolations: interpolation_key = 'cubic spline' else: interpolation_key = self._interpolations.keys()[0]#will @@ -1743,7 +1739,7 @@ def plot_integrated(self, chart=None, ambient_coords=None, print("Plotting from the interpolation associated " + "with the key '{}' ".format(interpolation_key) + "by default...") - elif interpolation_key not in self._interpolations.keys(): + elif interpolation_key not in self._interpolations: raise ValueError("No existing key " + "'{}' ".format(interpolation_key) + "referring to any interpolation.") @@ -1909,7 +1905,7 @@ def plot_integrated(self, chart=None, ambient_coords=None, # The coordinate expressions of the mapping and the # coordinates involved # - for chart_pair in mapping._coord_expression.keys(): + for chart_pair in mapping._coord_expression: subs=(chart_pair[0]._subcharts,chart_pair[1]._subcharts) # 'subs' declared only for the line below to be shorter if self._chart in subs[0] and chart in subs[1]: @@ -2050,18 +2046,17 @@ def plot_integrated(self, chart=None, ambient_coords=None, class IntegratedAutoparallelCurve(IntegratedCurve): r""" - Numerical autoparallel curve on the manifold with - respect to a given affine connection. + Autoparallel curve on the manifold w.r.t. a given affine connection. INPUT: - ``parent`` -- - :class:`~sage.manifolds.differentiable.manifold_homset.DifferentiableCurveSet` - the set of curves `\mathrm{Hom}(I, M)` to which the curve belongs + :class:`~sage.manifolds.differentiable.manifold_homset.IntegratedAutoparallelCurveSet` + the set of curves `\mathrm{Hom_{autoparallel}}(I, M)` to which the + curve belongs - ``affine_connection`` -- :class:`~sage.manifolds.differentiable.affine_connection.AffineConnection` - affine connection with respect to which the curve is - autoparallel + affine connection w.r.t. which the curve is autoparallel - ``curve_parameter`` -- symbolic expression to be used as the parameter of the curve (the equations defining an instance of IntegratedAutoparallelCurve are such that ``t`` will actually be @@ -2093,9 +2088,9 @@ class IntegratedAutoparallelCurve(IntegratedCurve): for more details about Mercator projection On the Mercator projection, the lines of longitude all appear - vertical and then all parallel w.r.t each others. + vertical and then all parallel w.r.t. each others. Likewise, all the lines of latitude appear horizontal and parallel - w.r.t each others. + w.r.t. each others. These curves may be recovered as autoparallel curves of a certain connection :MATH:`\nabla` to be made explicit. @@ -2120,8 +2115,8 @@ class IntegratedAutoparallelCurve(IntegratedCurve): Likewise, :MATH:`\hat{e}_{\phi}` is normalized and tangent to the line of latitude. - Now, set an affine connection with respect to which such fields are - parallely transported in all directions, that is: + Now, set an affine connection w.r.t. which such fields are parallely + transported in all directions, that is: :MATH:`\nabla \hat{e}_{\theta} = \nabla \hat{e}_{\phi} = 0`. This is equivalent to setting all the connection coefficients to zero w.r.t. this frame:: @@ -2154,17 +2149,17 @@ class IntegratedAutoparallelCurve(IntegratedCurve): sage: Tp = S2.tangent_space(p) sage: v = Tp((v_th0, v_ph0), basis=epolar_ON.at(p)) - Note here that the components ``(v\_th0, v\_ph0)`` of the initial + Note here that the components ``(v_th0, v_ph0)`` of the initial tangent vector ``v`` refer to the basis - ``epolar\_ON`` :MATH:` = (\hat{e}_{\theta}, \hat{e}_{\phi})` + ``epolar_ON`` :MATH:`= (\hat{e}_{\theta}, \hat{e}_{\phi})` and not the coordinate basis - ``epolar`` :MATH:` = (e_{\theta}, e_{\phi})`. + ``epolar`` :MATH:`= (e_{\theta}, e_{\phi})`. This is merely to help picture the aspect of the tangent vector in the usual embedding of :MATH:`\mathbb{S}^{2}` in :MATH:`\mathbb{R}^{3}` thanks to using an orthonormal frame, since providing the components w.r.t. the coordinate basis would - require mutliplying the second component (i.e. in :MATH:`\phi`) in - order to picture the vector in the same way. + require mutliplying the second component (i.e. the :MATH:`\phi` + component) in order to picture the vector in the same way. This subtlety will need to be taken into account later when the numerical curve will be compared to the analytical solution. @@ -2173,8 +2168,8 @@ class IntegratedAutoparallelCurve(IntegratedCurve): sage: [t, tmin, tmax] = var('t tmin tmax') sage: c = S2.integrated_autoparallel_curve(nab, (t, tmin, tmax), - ....: v, chart=polar, name='c', verbose=False) - sage: sys = c.system() + ....: v, chart=polar, name='c') + sage: sys = c.system(verbose=True) Autoparallel curve c in the 2-dimensional differentiable manifold S^2 equipped with Affine connection nab on the 2-dimensional differentiable manifold S^2, and integrated over @@ -2210,7 +2205,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): Ask for the identity map in terms of these charts in order to add this coordinate change to its dictionnary of expressions. This is - required to plot the curve w.r.t the Mercator chart:: + required to plot the curve w.r.t. the Mercator chart:: sage: identity = S2.identity_map() sage: identity.coord_functions(polar, mercator) @@ -2221,20 +2216,19 @@ class IntegratedAutoparallelCurve(IntegratedCurve): corresponding to the two initial conditions previously set:: sage: graph2D_mercator = Graphics() - sage: for key in dict_params.keys(): + sage: for key in dict_params: ....: sol = c.solve(solution_key='sol-'+key, - ....: parameters_values=dict_params[key], verbose=False) + ....: parameters_values=dict_params[key]) ....: interp = c.interpolate(solution_key='sol-'+key, - ....: interpolation_key='interp-'+key, verbose=False) + ....: interpolation_key='interp-'+key) ....: graph2D_mercator+=c.plot_integrated(interpolation_key='interp-'+key, - ....: chart=mercator, thickness=2, - ....: verbose=False) + ....: chart=mercator, thickness=2) Prepare a grid of Mercator coordinates lines, and plot the curves over it:: sage: graph2D_mercator_coords=mercator.plot(chart=mercator, - ....: number_values=8,color='yellow') + ....: number_values=8,color='yellow') sage: (graph2D_mercator + graph2D_mercator_coords).show() .. PLOT:: @@ -2262,13 +2256,13 @@ class IntegratedAutoparallelCurve(IntegratedCurve): identity = S2.identity_map() identity.coord_functions(polar, mercator) graph2D_mercator = Graphics() - for key in dict_params.keys(): + for key in dict_params: c.solve(solution_key='sol-'+key, - parameters_values=dict_params[key], verbose=False) + parameters_values=dict_params[key]) c.interpolate(solution_key='sol-'+key, - interpolation_key='interp-'+key, verbose=False) + interpolation_key='interp-'+key) graph2D_mercator += c.plot_integrated(interpolation_key='interp-'+key, - chart=mercator, thickness=2, verbose=False) + chart=mercator, thickness=2) graph2D_mercator_coords = mercator.plot(chart=mercator, number_values=8, color='yellow') sphinx_plot(graph2D_mercator + graph2D_mercator_coords) @@ -2289,11 +2283,10 @@ class IntegratedAutoparallelCurve(IntegratedCurve): :MATH:`\mathbb{S}^{2}`:: sage: graph3D_embedded_curves = Graphics() - sage: for key in dict_params.keys(): + sage: for key in dict_params: ....: graph3D_embedded_curves+=c.plot_integrated(interpolation_key='interp-'+key, ....: mapping=euclid_embedding, thickness=5, - ....: display_tangent=True, scale=0.4, width_tangent=0.5, - ....: verbose=False) + ....: display_tangent=True, scale=0.4, width_tangent=0.5) sage: graph3D_embedded_polar_coords = polar.plot(chart=cart, ....: mapping=euclid_embedding, ....: number_values=15, color='yellow') @@ -2325,22 +2318,21 @@ class IntegratedAutoparallelCurve(IntegratedCurve): euclid_embedding = S2.diff_map(R3, {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) graph3D_embedded_curves = Graphics() - for key in dict_params.keys(): + for key in dict_params: c.solve(solution_key='sol-'+key, - parameters_values=dict_params[key], verbose=False) + parameters_values=dict_params[key]) c.interpolate(solution_key='sol-'+key, - interpolation_key='interp-'+key, verbose=False) + interpolation_key='interp-'+key) graph3D_embedded_curves+=c.plot_integrated(interpolation_key='interp-'+key, - mapping=euclid_embedding, thickness=5, - display_tangent=True, scale=0.4, width_tangent=0.5, - verbose=False) + mapping=euclid_embedding, thickness=5, + display_tangent=True, scale=0.4, width_tangent=0.5) graph3D_embedded_polar_coords = polar.plot(chart=cart, mapping=euclid_embedding, number_values=15, color='yellow') graph = graph3D_embedded_curves+graph3D_embedded_polar_coords sphinx_plot(graph) - Finally, one may plot a general autoparallel curve w.r.t + Finally, one may plot a general autoparallel curve w.r.t. :MATH:`\nabla` that is neither a line of latitude or longitude. The vectors tangent to such a curve make an angle different from 0 or :MATH:`\pi/2` with the lines of latitude and longitude. @@ -2348,17 +2340,16 @@ class IntegratedAutoparallelCurve(IntegratedCurve): tangent vectors are non zero:: sage: sol = c.solve(solution_key='sol-angle', - ....: parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, - ....: verbose=False) + ....: parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}) sage: interp = c.interpolate(solution_key='sol-angle', - ....: interpolation_key='interp-angle', verbose=False) + ....: interpolation_key='interp-angle') Plot the resulting curve in the Mercator plane. This generates a straight line, as expected:: sage: graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', ....: chart=mercator, thickness=1, display_tangent=True, - ....: scale=0.2, width_tangent=0.2, verbose=False) + ....: scale=0.2, width_tangent=0.2) sage: graph2D_mercator_angle_curve.show() .. PLOT:: @@ -2384,21 +2375,19 @@ class IntegratedAutoparallelCurve(IntegratedCurve): identity = S2.identity_map() identity.coord_functions(polar, mercator) sol = c.solve(solution_key='sol-angle', - parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, - verbose=False) + parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}) interp = c.interpolate(solution_key='sol-angle', - interpolation_key='interp-angle', verbose=False) + interpolation_key='interp-angle') graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', chart=mercator, thickness=1, display_tangent=True, - scale=0.2, width_tangent=0.2, verbose=False) + scale=0.2, width_tangent=0.2) sphinx_plot(graph2D_mercator_angle_curve) One may eventually plot such a curve on :MATH:`\mathbb{S}^{2}`:: sage: graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle', ....: mapping=euclid_embedding, thickness=5, - ....: display_tangent=True, scale=0.1, width_tangent=0.5, - ....: verbose=False) + ....: display_tangent=True, scale=0.1, width_tangent=0.5) sage: graph=graph3D_embedded_angle_curve+graph3D_embedded_polar_coords sage: graph.show() @@ -2410,7 +2399,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): epolar = polar.frame() ch_basis = S2.automorphism_field() ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) - epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON) + epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') nab.set_coef(epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') @@ -2425,13 +2414,12 @@ class IntegratedAutoparallelCurve(IntegratedCurve): euclid_embedding = S2.diff_map(R3, {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) sol = c.solve(solution_key='sol-angle', - parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, - verbose=False) + parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}) interp = c.interpolate(solution_key='sol-angle', - interpolation_key='interp-angle', verbose=False) + interpolation_key='interp-angle') graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle', mapping=euclid_embedding, thickness=5, display_tangent=True, - scale=0.1, width_tangent=0.5, verbose=False) + scale=0.1, width_tangent=0.5) graph3D_embedded_polar_coords = polar.plot(chart=cart, mapping=euclid_embedding, number_values=15, color='yellow') graph=graph3D_embedded_angle_curve+graph3D_embedded_polar_coords @@ -2456,21 +2444,21 @@ class IntegratedAutoparallelCurve(IntegratedCurve): In order to use these expressions to compare with the result provided by the numerical integration, remember that the components - ``(v\_th0, v\_ph0)`` of the initial + ``(v_th0, v_ph0)`` of the initial tangent vector ``v`` refer to the basis - ``epolar\_ON`` :MATH:`= (\hat{e}_{\theta}, \hat{e}_{\phi})` and not the + ``epolar_ON`` :MATH:`= (\hat{e}_{\theta}, \hat{e}_{\phi})` and not the coordinate basis ``epolar`` :MATH:`= (e_{\theta}, e_{\phi})`. Therefore, the following relations hold: - ``v\_ph0`` = :MATH:`\dot{\phi}_{0} \sin \theta_{0}` (and not merely - :MATH:`\dot{\phi}_{0}`), while ``v\_th0`` clearly is + ``v_ph0`` = :MATH:`\dot{\phi}_{0} \sin \theta_{0}` (and not merely + :MATH:`\dot{\phi}_{0}`), while ``v_th0`` clearly is :MATH:`\dot{\theta}_{0}`. With this in mind, plot an analytical curve to compare with a numerical solution:: sage: graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', - ....: chart=mercator, thickness=1, verbose=False) + ....: chart=mercator, thickness=1) sage: expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2))) sage: c_loxo = S2.curve({polar:[th0+v_th0*t, expr_ph]}, (t,0,2), ....: name='c_loxo') @@ -2482,7 +2470,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): arbitrary variable ``expr_mercator``, which will never be used again. But adding the expression to the dictionnary is required to plot the - curve w.r.t the Mercator chart:: + curve w.r.t. the Mercator chart:: sage: expr_mercator = c_loxo.expression(chart2=mercator) @@ -2518,12 +2506,11 @@ class IntegratedAutoparallelCurve(IntegratedCurve): identity = S2.identity_map() identity.coord_functions(polar, mercator) sol = c.solve(solution_key='sol-angle', - parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, - verbose=False) + parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}) interp = c.interpolate(solution_key='sol-angle', - interpolation_key='interp-angle', verbose=False) + interpolation_key='interp-angle') graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', - chart=mercator, thickness=1, verbose=False) + chart=mercator, thickness=1) expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2))) c_loxo = S2.curve({polar:[th0+v_th0*t, expr_ph]}, (t,0,2), name='c_loxo') @@ -2537,7 +2524,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): One may eventually compare these curves on :MATH:`\mathbb{S}^{2}`:: sage: graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle', - ....: mapping=euclid_embedding, thickness=3, verbose=False) + ....: mapping=euclid_embedding, thickness=3) sage: graph3D_embedded_loxo = c_loxo.plot(mapping=euclid_embedding, ....: parameters={th0:pi/4+2*pi/180, ph0:0.1, v_th0:1, v_ph0:8}, ....: thickness=3, color = 'blue') @@ -2569,12 +2556,11 @@ class IntegratedAutoparallelCurve(IntegratedCurve): euclid_embedding = S2.diff_map(R3, {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) sol = c.solve(solution_key='sol-angle', - parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}, - verbose=False) + parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}) interp = c.interpolate(solution_key='sol-angle', - interpolation_key='interp-angle', verbose=False) + interpolation_key='interp-angle') graph3D_embedded_angle_curve=c.plot_integrated(interpolation_key='interp-angle', - mapping=euclid_embedding, thickness=3, verbose=False) + mapping=euclid_embedding, thickness=3) expr_ph = ph0+v_ph0/v_th0*(ln(tan((v_th0*t+th0)/2))-ln(tan(th0/2))) c_loxo = S2.curve({polar:[th0+v_th0*t, expr_ph]}, (t,0,2), name='c_loxo') @@ -2592,9 +2578,10 @@ class IntegratedAutoparallelCurve(IntegratedCurve): def __init__(self, parent, affine_connection, curve_parameter, initial_tangent_vector, chart=None, name=None, latex_name=None, is_isomorphism=False, - is_identity=False, verbose=True): - r"""Construct an autoparallel curve with respect to the given - affine connection with the given initial tangent vector. + is_identity=False, verbose=False): + r""" + Construct an autoparallel curve w.r.t. the given affine + connection with the given initial tangent vector. TESTS:: @@ -2607,7 +2594,7 @@ def __init__(self, parent, affine_connection, curve_parameter, sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, - ....: name='c', verbose=False) ; c + ....: name='c') ; c Integrated autoparallel curve c in the 3-dimensional differentiable manifold M sage: TestSuite(c).run() @@ -2663,12 +2650,11 @@ def _repr_(self): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_autoparallel_curve(nab, (t,0,5), v, - ....: verbose=False) ; c + sage: c = M.integrated_autoparallel_curve(nab, (t,0,5), v); c Integrated autoparallel curve in the 3-dimensional differentiable manifold M sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, - ....: name='c', verbose=False) ; c + ....: name='c') ; c Integrated autoparallel curve c in the 3-dimensional differentiable manifold M @@ -2695,7 +2681,7 @@ def __reduce__(self): sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, - ....: name='c', verbose=False) + ....: name='c') sage: c.__reduce__() (, (Set of Morphisms from Real interval (0, 5) to @@ -2706,7 +2692,8 @@ def __reduce__(self): Affine connection nabla on the 3-dimensional differentiable manifold M, t, - Tangent vector at Point p on the 3-dimensional differentiable manifold M, + Tangent vector at Point p on the 3-dimensional + differentiable manifold M, Chart (M, (x1, x2, x3)), 'c', 'c', @@ -2725,7 +2712,7 @@ def __reduce__(self): self._chart, self._name, self._latex_name, self._is_isomorphism, self._is_identity)) - def system(self, verbose=True): + def system(self, verbose=False): r""" Provide a detailed description of the system defining the autoparallel curve and returns the system defining it: chart, @@ -2733,7 +2720,7 @@ def system(self, verbose=True): INPUT: - - ``verbose`` -- (default: ``True``) prints a detailed + - ``verbose`` -- (default: ``False``) prints a detailed description of the curve OUTPUT: @@ -2753,9 +2740,8 @@ def system(self, verbose=True): sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, - ....: verbose=False) - sage: sys = c.system() + sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v) + sage: sys = c.system(verbose=True) Autoparallel curve in the 3-dimensional differentiable manifold M equipped with Affine connection nabla on the 3-dimensional differentiable manifold M, and integrated @@ -2777,7 +2763,7 @@ def system(self, verbose=True): d(Dx2)/dt = 0 d(Dx3)/dt = -B*Dx2*Dx3*x2*x3 - sage: sys_bis = c.system(verbose=False) + sage: sys_bis = c.system() sage: sys_bis == sys True @@ -2839,17 +2825,17 @@ def system(self, verbose=True): class IntegratedGeodesic(IntegratedAutoparallelCurve): r""" - Numerical geodesic on the manifold with respect to a - given metric. + Geodesic on the manifold w.r.t. a given metric. INPUT: - ``parent`` -- - :class:`~sage.manifolds.differentiable.manifold_homset.DifferentiableCurveSet` - the set of curves `\mathrm{Hom}(I, M)` to which the curve belongs + :class:`~sage.manifolds.differentiable.manifold_homset.IntegratedGeodesicSet` + the set of curves `\mathrm{Hom_{geodesic}}(I, M)` to which the + curve belongs - ``metric`` -- :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric` - metric with respect to which the curve is a geodesic + metric w.r.t. which the curve is a geodesic - ``curve_parameter`` -- symbolic expression to be used as the parameter of the curve (the equations defining an instance of IntegratedGeodesic are such that ``t`` will actually be an affine @@ -2899,8 +2885,8 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): sage: [t, tmin, tmax] = var('t tmin tmax') sage: c = S2.integrated_geodesic(g, (t, tmin, tmax), v, - ....: chart=polar, name='c', verbose=False) - sage: sys = c.system() + ....: chart=polar, name='c') + sage: sys = c.system(verbose=True) Geodesic c in the 2-dimensional differentiable manifold S^2 equipped with Riemannian metric g on the 2-dimensional differentiable manifold S^2, and integrated over the Real @@ -2940,15 +2926,15 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): corresponding to the three initial conditions previously set:: sage: graph3D_embedded_geods = Graphics() - sage: for key in dict_params.keys(): + sage: for key in dict_params: ....: sol = c.solve(solution_key='sol-'+key, - ....: parameters_values=dict_params[key], verbose=False) + ....: parameters_values=dict_params[key]) ....: interp = c.interpolate(solution_key='sol-'+key, - ....: interpolation_key='interp-'+key, verbose=False) + ....: interpolation_key='interp-'+key) ....: graph3D_embedded_geods+=c.plot_integrated(interpolation_key='interp-'+key, ....: mapping=euclid_embedding, thickness=5, ....: display_tangent=True, scale=0.3, - ....: width_tangent=0.5, verbose=False) + ....: width_tangent=0.5) Plot the resulting geodesics on the grid of polar coordinates lines on :MATH:`\mathbb{S}^{2}` and check that these are great circles:: @@ -2982,15 +2968,15 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): euclid_embedding = S2.diff_map(R3, {(polar, cart):[sin(th)*cos(ph),sin(th)*sin(ph),cos(th)]}) graph3D_embedded_geods = Graphics() - for key in dict_params.keys(): + for key in dict_params: sol = c.solve(solution_key='sol-'+key, - parameters_values=dict_params[key], verbose=False) + parameters_values=dict_params[key]) interp = c.interpolate(solution_key='sol-'+key, - interpolation_key='interp-'+key, verbose=False) + interpolation_key='interp-'+key) graph3D_embedded_geods+=c.plot_integrated(interpolation_key='interp-'+key, mapping=euclid_embedding, thickness=5, display_tangent=True, scale=0.3, - width_tangent=0.5, verbose=False) + width_tangent=0.5) graph3D_embedded_polar_coords = polar.plot(chart=cart, mapping=euclid_embedding, number_values=15, color='yellow') @@ -3002,10 +2988,11 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): def __init__(self, parent, metric, curve_parameter, initial_tangent_vector, chart=None, name=None, latex_name=None, is_isomorphism=False, - is_identity=False, verbose=True): + is_identity=False, verbose=False): - r"""Construct a geodesic curve with respect to the given metric - with the given initial tangent vector. + r""" + Construct a geodesic curve w.r.t. the given metric with the + given initial tangent vector. TESTS:: @@ -3019,8 +3006,7 @@ def __init__(self, parent, metric, curve_parameter, sage: p = S2.point((pi/2,0), name='p') sage: Tp = S2.tangent_space(p) sage: v = Tp((1/sqrt(2),1/sqrt(2))) - sage: c = S2.integrated_geodesic(g, (t,0,pi), v, name='c', - ....: verbose=False) ; c + sage: c = S2.integrated_geodesic(g, (t,0,pi), v, name='c'); c Integrated geodesic c in the 2-dimensional differentiable manifold S^2 sage: TestSuite(c).run() @@ -3058,12 +3044,10 @@ def _repr_(self): sage: p = S2.point((pi/2,0), name='p') sage: Tp = S2.tangent_space(p) sage: v = Tp((1/sqrt(2),1/sqrt(2))) - sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, - ....: verbose=False) ; c + sage: c = S2.integrated_geodesic(g, (t, 0, pi), v) ; c Integrated geodesic in the 2-dimensional differentiable manifold S^2 - sage: c = S2.integrated_geodesic(g, (t,0,pi), v, - ....: name='c', verbose=False) ; c + sage: c = S2.integrated_geodesic(g, (t,0,pi), v, name='c'); c Integrated geodesic c in the 2-dimensional differentiable manifold S^2 @@ -3091,18 +3075,17 @@ def __reduce__(self): sage: p = S2.point((pi/2,0), name='p') sage: Tp = S2.tangent_space(p) sage: v = Tp((1/sqrt(2),1/sqrt(2))) - sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', - ....: verbose=False) + sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c') sage: c.__reduce__() (, (Set of Morphisms from Real interval (0, pi) to 2-dimensional differentiable manifold S^2 in Category of homsets of subobjects of sets and topological spaces which actually are integrated geodesics w.r.t a certain metric, - Riemannian metric g on the 2-dimensional differentiable - manifold S^2, + Riemannian metric g on the 2-dimensional differentiable manifold S^2, t, - Tangent vector at Point p on the 2-dimensional differentiable manifold S^2, + Tangent vector at Point p on the 2-dimensional + differentiable manifold S^2, Chart (S^2, (theta, phi)), 'c', 'c', @@ -3121,14 +3104,14 @@ def __reduce__(self): self._chart, self._name, self._latex_name, self._is_isomorphism, self._is_identity)) - def system(self, verbose=True): + def system(self, verbose=False): r""" Return the system defining the geodesic : chart, equations and initial conditions INPUT: - - ``verbose`` -- (default: ``True``) prints a detailed + - ``verbose`` -- (default: ``False``) prints a detailed description of the curve OUTPUT: @@ -3150,9 +3133,8 @@ def system(self, verbose=True): sage: p = S2.point((pi/2,0), name='p') sage: Tp = S2.tangent_space(p) sage: v = Tp((1/sqrt(2),1/sqrt(2))) - sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c', - ....: verbose=False) - sage: sys = c.system() + sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c') + sage: sys = c.system(verbose=True) Geodesic c in the 2-dimensional differentiable manifold S^2 equipped with Riemannian metric g on the 2-dimensional differentiable manifold S^2, and integrated over the Real @@ -3172,7 +3154,7 @@ def system(self, verbose=True): d(Dtheta)/dt = Dphi^2*cos(theta)*sin(theta) d(Dphi)/dt = -2*Dphi*Dtheta*cos(theta)/sin(theta) - sage: sys_bis = c.system(verbose=False) + sage: sys_bis = c.system() sage: sys_bis == sys True diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 1d133574f2d..2210e3e6920 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -2572,9 +2572,9 @@ def curve(self, coord_expression, param, chart=None, def integrated_curve(self, equations_rhs, velocities, curve_param, initial_tangent_vector, chart=None, name=None, - latex_name=None, verbose=True): + latex_name=None, verbose=False): r""" - Construct a numerical curve defined by a system of second order + Construct a curve defined by a system of second order differential equations in the coordinate functions. .. SEEALSO:: @@ -2632,12 +2632,10 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, sage: p = M.point((0,0,0), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((1,0,1)) - sage: c = M.integrated_curve(eqns, D, (t, 0, 6), v, - ....: name='c', verbose=False) - sage: c + sage: c=M.integrated_curve(eqns, D, (t,0,6), v, name='c'); c Integrated curve c in the 3-dimensional differentiable manifold M - sage: sys = c.system() + sage: sys = c.system(verbose=True) Curve c in the 3-dimensional differentiable manifold M integrated over the Real interval (0, 6) as a solution to the following system, written w.r.t. @@ -2657,14 +2655,14 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, d(Dx2)/dt = -Dx1 d(Dx3)/dt = 0 - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1.3) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1.3, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... [0.9635581155730744, -0.7325010457963622, 1.3] - sage: tgt_vec = c.tangent_vector_eval_at(3.7) + sage: tgt_vec = c.tangent_vector_eval_at(3.7, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... @@ -2692,10 +2690,10 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, def integrated_autoparallel_curve(self, affine_connection, curve_param, initial_tangent_vector, chart=None, - name=None, latex_name=None, verbose=True): + name=None, latex_name=None, verbose=False): r""" - Construct a numerical autoparallel curve on the manifold with - respect to a given affine connection. + Construct an autoparallel curve on the manifold w.r.t. a given + affine connection. .. SEEALSO:: @@ -2706,8 +2704,7 @@ def integrated_autoparallel_curve(self, affine_connection, - ``affine_connection`` -- :class:`~sage.manifolds.differentiable.affine_connection.AffineConnection` - affine connection with respect to which the curve is - autoparallel + affine connection w.r.t. which the curve is autoparallel - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, where @@ -2774,8 +2771,8 @@ def integrated_autoparallel_curve(self, affine_connection, sage: v = Tp((1,1), basis=epolar_ON.at(p)) sage: t = var('t') sage: c = S2.integrated_autoparallel_curve(nab, (t, 0, 6), - ....: v, chart=polar, name='c', verbose=False) - sage: sys = c.system() + ....: v, chart=polar, name='c') + sage: sys = c.system(verbose=True) Autoparallel curve c in the 2-dimensional differentiable manifold S^2 equipped with Affine connection nab on the 2-dimensional differentiable manifold S^2, and integrated @@ -2795,14 +2792,14 @@ def integrated_autoparallel_curve(self, affine_connection, d(Dth)/dt = 0 d(Dph)/dt = -Dph*Dth*cos(th)/sin(th) - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1.3) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1.3, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... [2.085398163397449, 1.4203172015958863] - sage: tgt_vec = c.tangent_vector_eval_at(3.7) + sage: tgt_vec = c.tangent_vector_eval_at(3.7, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... @@ -2830,10 +2827,9 @@ def integrated_autoparallel_curve(self, affine_connection, def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, chart=None, - name=None, latex_name=None, verbose=True): + name=None, latex_name=None, verbose=False): r""" - Construct a numerical geodesic on the manifold with respect to - a given metric. + Construct a geodesic on the manifold w.r.t. a given metric. .. SEEALSO:: @@ -2844,7 +2840,7 @@ def integrated_geodesic(self, metric, curve_param, - ``metric`` -- :class:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric` - metric with respect to which the curve is a geodesic + metric w.r.t. which the curve is a geodesic - ``curve_param`` -- a tuple of the type ``(t, t_min, t_max)``, where @@ -2899,8 +2895,8 @@ def integrated_geodesic(self, metric, curve_param, sage: v = Tp((1, 1), basis=epolar.at(p)) sage: t = var('t') sage: c = S2.integrated_geodesic(g, (t, 0, 6), v, - ....: chart=polar, name='c', verbose=False) - sage: sys = c.system() + ....: chart=polar, name='c') + sage: sys = c.system(verbose=True) Geodesic c in the 2-dimensional differentiable manifold S^2 equipped with Riemannian metric g on the 2-dimensional differentiable manifold S^2, and integrated over the Real @@ -2919,14 +2915,14 @@ def integrated_geodesic(self, metric, curve_param, d(Dth)/dt = Dph^2*cos(th)*sin(th) d(Dph)/dt = -2*Dph*Dth*cos(th)/sin(th) - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1.3) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1.3, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... [2.2047444794514663, 0.7986609561213334] - sage: tgt_vec = c.tangent_vector_eval_at(3.7) + sage: tgt_vec = c.tangent_vector_eval_at(3.7, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... diff --git a/src/sage/manifolds/differentiable/manifold_homset.py b/src/sage/manifolds/differentiable/manifold_homset.py index dcccce4b94d..5d283e594e8 100644 --- a/src/sage/manifolds/differentiable/manifold_homset.py +++ b/src/sage/manifolds/differentiable/manifold_homset.py @@ -504,7 +504,6 @@ def _an_element_(self): coord_expression = {chart2: target_coord} return self.element_class(self, coord_expression) - #****************************************************************************** class IntegratedCurveSet(DifferentiableCurveSet): @@ -527,23 +526,13 @@ class IntegratedCurveSet(DifferentiableCurveSet): denote the set of integrated curves; if ``None``, `\mathrm{Hom_{integrated}}(I,M)` will be used - EXAMPLES: - - - - - - TO DO ! - - - - + EXAMPLE: This parent class needs to be imported:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet - Integrated curves are only allowed to defined on interval with + Integrated curves are only allowed to be defined on an interval with finite bounds. This forbids to define an instance of this parent class whose domain has infinite bounds:: @@ -562,7 +551,7 @@ class IntegratedCurveSet(DifferentiableCurveSet): sage: I = R.open_interval(-1, 2) sage: H = IntegratedCurveSet(I, M) ; H - Set of Morphisms from Real interval (-1, 2) to 3-dimensional + Set of Morphisms from Real interval (-1, 2) to 2-dimensional differentiable manifold M in Category of homsets of subobjects of sets and topological spaces which actually are integrated curves @@ -576,11 +565,11 @@ class IntegratedCurveSet(DifferentiableCurveSet): Integrated curve c in the 2-dimensional differentiable manifold M - An element of ``H`` is a curve in ``M``:: + A "typical" element of ``H`` is a curve in ``M``:: sage: d = H.an_element(); d Integrated curve in the 2-dimensional differentiable manifold M - sage: sys = d.system() + sage: sys = d.system(verbose=True) Curve in the 2-dimensional differentiable manifold M integrated over the Real interval (-1, 2) as a solution to the following system, written w.r.t. Chart (M, (x, y)): @@ -612,11 +601,11 @@ class IntegratedCurveSet(DifferentiableCurveSet): of sets and topological spaces which actually are integrated curves - An element of ``H`` is a curve in ``M``:: + A "typical" element of ``H`` is a curve in ``M``:: sage: f = H.an_element(); f Integrated curve in the 2-dimensional differentiable manifold M - sage: sys = f.system() + sage: sys = f.system(verbose=True) Curve in the 2-dimensional differentiable manifold M integrated over the Real interval (a, b) as a solution to the following system, written w.r.t. Chart (M, (x, y)): @@ -660,12 +649,28 @@ class IntegratedCurveSet(DifferentiableCurveSet): The identity element of the monoid is a numerical version of the identity map of `J`:: + sage: e = H.one() ; e + Integrated curve Id_(a, b) in the Real interval (a, b) - COMPLETE ! + A "typical" element of the monoid:: - """ + sage: g = H.an_element() ; g + Integrated curve in the Real interval (a, b) + sage: sys = g.system(verbose=True) + Curve in the Real interval (a, b) integrated over the Real interval (a, b) as a solution to the following system, written w.r.t. Chart ((a, b), (t,)): + + Initial point: Point on the Real number line R with coordinates [0] w.r.t. Chart ((a, b), (t,)) + Initial tangent vector: Tangent vector at Point on the Real number line R with components [1/4] w.r.t. Chart ((a, b), (t,)) + + d(t)/ds = Dt + d(Dt)/ds = -1/4*sin(-a + s) + + The test suite is passed:: + sage: TestSuite(H).run() + + """ Element = IntegratedCurve @@ -753,7 +758,7 @@ def _repr_(self): def _element_constructor_(self, equations_rhs, velocities, curve_parameter, initial_tangent_vector, chart=None, name=None, latex_name=None, is_isomorphism=False, - is_identity=False, verbose=True): + is_identity=False, verbose=False): r""" Construct an element of ``self``, i.e. an integrated curve `I \to M`, where `I` is a real interval and `M` some @@ -808,7 +813,7 @@ def _an_element_(self): sage: c = H._an_element_() ; c Integrated curve in the 2-dimensional differentiable manifold M - sage: sys = c.system() + sage: sys = c.system(verbose=True) Curve in the 2-dimensional differentiable manifold M integrated over the Real interval (-1, 2) as a solution to the following system, written w.r.t. Chart (M, (x, y)): @@ -824,14 +829,14 @@ def _an_element_(self): d(Dx)/dt = -1/4*sin(t + 1) d(Dy)/dt = 0 - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1, verbose=False) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1) [0.22732435599328793, 0.0] sage: H = IntegratedCurveSet(I, I) sage: c = H._an_element_() ; c Integrated curve in the Real interval (-1, 2) - sage: sys = c.system() + sage: sys = c.system(verbose=True) Curve in the Real interval (-1, 2) integrated over the Real interval (-1, 2) as a solution to the following system, written w.r.t. Chart ((-1, 2), (t,)): @@ -844,9 +849,9 @@ def _an_element_(self): d(t)/ds = Dt d(Dt)/ds = -3/8*sin(s + 1) - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1, verbose=False) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1) [0.840986533989932] """ @@ -935,7 +940,7 @@ def one(self): - the identity map of `M`, as an instance of :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` - EXAMPLES: + EXAMPLE:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet sage: M = Manifold(3, 'M') @@ -993,14 +998,120 @@ class IntegratedAutoparallelCurveSet(IntegratedCurveSet): differentiable manifold `M` (codomain of the morphisms) - ``name`` -- (default: ``None``) string; name given to the set of integrated autoparallel curves; if ``None``, - ``Hom_integrated_autoparallel(I, M)`` will be used + ``Hom_autoparallel(I, M)`` will be used - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the set of integrated autoparallel curves; if ``None``, - `\mathrm{Hom_{integrated autoparallel}}(I,M)` will be used + `\mathrm{Hom_{autoparallel}}(I,M)` will be used - EXAMPLES: + EXAMPLE: + + This parent class needs to be imported:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet + + Integrated autoparallel curves are only allowed to be defined on an + interval with finite bounds. + This forbids to define an instance of this parent class whose domain + has infinite bounds:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: H = IntegratedAutoparallelCurveSet(R, M) + Traceback (most recent call last): + ... + ValueError: Both boundaries of the interval defining the domain + of a Homset of integrated autoparallel curves need to be finite. + + An instance whose domain is an interval with finite bounds allows to + build a curve that is autoparallel w.r.t a connection defined on the + codomain:: + + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedAutoparallelCurveSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 2-dimensional + differentiable manifold M in Category of homsets of subobjects + of sets and topological spaces which actually are integrated + autoparallel curves w.r.t a certain affine connection + sage: nab = M.affine_connection('nabla') + sage: nab[0,1,0], nab[0,0,1] = 1,2 + sage: nab.torsion()[:] + [[[0, -1], [1, 0]], [[0, 0], [0, 0]]] + sage: t = var('t') + sage: p = M.point((3,4)) + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,2)) + sage: c = H(nab, t, v, name='c') ; c + Integrated autoparallel curve c in the 2-dimensional + differentiable manifold M + + A "typical" element of ``H`` is an autoparallel curve in ``M``:: + + sage: d = H.an_element(); d + Integrated autoparallel curve in the 2-dimensional + differentiable manifold M + sage: sys = d.system(verbose=True) + Autoparallel curve in the 2-dimensional differentiable manifold + M equipped with Affine connection nab on the Open subset U of + the 2-dimensional differentiable manifold M, and integrated + over the Real interval (-1, 2) as a solution to the following + equations, written w.r.t. Chart (M, (x, y)): + + Initial point: Point on the 2-dimensional differentiable + manifold M with coordinates [0.0312500000000000, -1/2] w.r.t. + Chart (M, (x, y)) + Initial tangent vector: Tangent vector at Point on the + 2-dimensional differentiable manifold M with components + [0.156250000000000, 2.07698341289763] w.r.t. Chart (M, (x, y)) + + d(x)/dt = Dx + d(y)/dt = Dy + d(Dx)/dt = 0 + d(Dy)/dt = -Dx*Dy*cos(x)/sin(x) + + + The test suite is passed:: + + sage: TestSuite(H).run() + + For any open interval `J` with finite bounds `(a,b)`, all curves are + autoparallel w.r.t any connection. + Therefore, the set of autoparallel curves `J \longrightarrow J` is a set of + numerical (manifold) endomorphisms that is a monoid for the law of + morphism composition:: + + sage: [a,b] = var('a b') + sage: J = R.open_interval(a, b) + sage: H = IntegratedAutoparallelCurveSet(J, J); H + Set of Morphisms from Real interval (a, b) to Real interval + (a, b) in Category of endsets of subobjects of sets and + topological spaces which actually are integrated autoparallel + curves w.r.t a certain affine connection + sage: H.category() + Category of endsets of subobjects of sets and topological spaces + sage: H in Monoids() + True + sage: e = H.one() ; e + Integrated autoparallel curve Id_(a, b) in the Real + interval (a, b) + + A "typical" element of the monoid:: + + sage: g = H.an_element() ; g + Integrated autoparallel curve in the Real interval (a, b) + sage: sys = g.system(verbose=True) + Autoparallel curve in the Real interval (a, b) equipped with Affine connection nab on the Open subset U of the Real number line R, and integrated over the Real interval (a, b) as a solution to the following equations, written w.r.t. Chart ((a, b), (t,)): + + Initial point: Point on the Real number line R with coordinates [1/200] w.r.t. Chart ((a, b), (t,)) + Initial tangent vector: Tangent vector at Point on the Real number line R with components [-9999/400/(a - b)] w.r.t. Chart ((a, b), (t,)) + + d(t)/ds = Dt + d(Dt)/ds = -Dt^2/t + + + The test suite is passed:: - TO DO ! + sage: TestSuite(H).run() """ @@ -1053,12 +1164,12 @@ def __init__(self, domain, codomain, name=None, latex_name=None): "be finite.") if name is None: - self._name = "Hom_integrated_autoparallel" + self._name = "Hom_autoparallel" self._name += "({},{})".format(domain._name, codomain._name) else: self._name = name if latex_name is None: - self._latex_name=r"\mathrm{{Hom}_{integrated autoparallel}}" + self._latex_name=r"\mathrm{{Hom}_{autoparallel}}" self._latex_name+= r"\left({},{}\right)".format( domain._latex_name, codomain._latex_name) else: @@ -1095,7 +1206,7 @@ def _repr_(self): def _element_constructor_(self, affine_connection, curve_parameter, initial_tangent_vector, chart=None, name=None, latex_name=None, is_isomorphism=False, - is_identity=False, verbose=True): + is_identity=False, verbose=False): r""" Construct an element of ``self``, i.e. an integrated autoparallel curve `I \to M`, where `I` is a real interval and @@ -1152,7 +1263,7 @@ def _an_element_(self): sage: c = H._an_element_() ; c Integrated autoparallel curve in the 2-dimensional differentiable manifold M - sage: sys = c.system() + sage: sys = c.system(verbose=True) Autoparallel curve in the 2-dimensional differentiable manifold M equipped with Affine connection nab on the Open subset U of the 2-dimensional differentiable manifold M, @@ -1173,14 +1284,14 @@ def _an_element_(self): d(Dx)/dt = 0 d(Dy)/dt = -Dx*Dy*cos(x)/sin(x) - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1, verbose=False) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1) [0.34375000000000056, 0.49999813529956155] sage: H = IntegratedAutoparallelCurveSet(I, I) sage: c = H._an_element_() ; c Integrated autoparallel curve in the Real interval (-1, 2) - sage: sys = c.system() + sage: sys = c.system(verbose=True) Autoparallel curve in the Real interval (-1, 2) equipped with Affine connection nab on the Open subset U of the Real number line R, and integrated over the Real @@ -1196,9 +1307,9 @@ def _an_element_(self): d(t)/ds = Dt d(Dt)/ds = -Dt^2/t - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1, verbose=False) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1) [1.0606601601128764] """ @@ -1383,7 +1494,7 @@ def one(self): - the identity map of `M`, as an instance of :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` - EXAMPLES: + EXAMPLE:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet sage: M = Manifold(3, 'M') @@ -1453,14 +1564,122 @@ class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): :class:`~sage.manifolds.differentiable.manifold.DifferentiableManifold`; differentiable manifold `M` (codomain of the morphisms) - ``name`` -- (default: ``None``) string; name given to the set of - integrated geodesics; if ``None``, ``Hom(I, M)`` will be used + integrated geodesics; if ``None``, ``Hom_geodesic(I, M)`` will be used - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the set of integrated geodesics; if ``None``, `\mathrm{Hom_{geodesic}}(I,M)` will be used - EXAMPLES: + EXAMPLE: + + This parent class needs to be imported:: + + sage: from sage.manifolds.differentiable.manifold_homset import IntegratedGeodesicSet + + Integrated geodesics are only allowed to be defined on an interval + with finite bounds. + This forbids to define an instance of this parent class whose domain + has infinite bounds:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: R. = RealLine() + sage: H = IntegratedGeodesicSet(R, M) + Traceback (most recent call last): + ... + ValueError: Both boundaries of the interval defining the domain + of a Homset of integrated geodesics need to be finite. + + An instance whose domain is an interval with finite bounds allows to + build a geodesic w.r.t. a metric defined on the codomain:: - TO DO ! + sage: I = R.open_interval(-1, 2) + sage: H = IntegratedGeodesicSet(I, M) ; H + Set of Morphisms from Real interval (-1, 2) to 2-dimensional + differentiable manifold M in Category of homsets of subobjects + of sets and topological spaces which actually are integrated + geodesics w.r.t a certain metric + sage: g = M.metric('g') + sage: g[0,0], g[1,1], g[0,1] = 1, 1, 2 + sage: t = var('t') + sage: p = M.point((3,4)) + sage: Tp = M.tangent_space(p) + sage: v = Tp((1,2)) + sage: c = H(g, t, v, name='c') ; c + Integrated geodesic c in the 2-dimensional differentiable + manifold M + + A "typical" element of ``H`` is a geodesic in ``M``:: + + sage: d = H.an_element(); d + Integrated geodesic in the 2-dimensional differentiable + manifold M + sage: sys = d.system(verbose=True) + Geodesic in the 2-dimensional differentiable manifold M equipped + with Riemannian metric g on the 2-dimensional differentiable + manifold M, and integrated over the Real interval (-1, 2) as a + solution to the following geodesic equations, written w.r.t. + Chart (M, (x, y)): + + Initial point: Point on the 2-dimensional differentiable + manifold M with coordinates [0.0312500000000000, 0] w.r.t. + Chart (M, (x, y)) + Initial tangent vector: Tangent vector at Point on the + 2-dimensional differentiable manifold M with components + [0.156250000000000, 0] w.r.t. Chart (M, (x, y)) + + d(x)/dt = Dx + d(y)/dt = Dy + d(Dx)/dt = Dy^2*cos(x)*sin(x) + d(Dy)/dt = -2*Dx*Dy*cos(x)/sin(x) + + The test suite is passed:: + + sage: TestSuite(H).run() + + For any open interval `J` with finite bounds `(a,b)`, all curves are + geodesics w.r.t any metric. + Therefore, the set of geodesics `J \longrightarrow J` is a set of + numerical (manifold) endomorphisms that is a monoid for the law of + morphism composition:: + + sage: [a,b] = var('a b') + sage: J = R.open_interval(a, b) + sage: H = IntegratedGeodesicSet(J, J); H + Set of Morphisms from Real interval (a, b) to Real interval + (a, b) in Category of endsets of subobjects of sets and + topological spaces which actually are integrated geodesics + w.r.t a certain metric + sage: H.category() + Category of endsets of subobjects of sets and topological spaces + sage: H in Monoids() + True + sage: e = H.one() ; e + Integrated geodesic Id_(a, b) in the Real interval (a, b) + + A "typical" element of the monoid:: + + sage: g = H.an_element() ; g + Integrated geodesic in the Real interval (a, b) + sage: sys = g.system(verbose=True) + Geodesic in the Real interval (a, b) equipped with Riemannian + metric g on the Open subset U of the Real number line R, and + integrated over the Real interval (a, b) as a solution to the + following geodesic equations, written w.r.t. + Chart ((a, b), (t,)): + + Initial point: Point on the Real number line R with coordinates + [1/200] w.r.t. Chart ((a, b), (t,)) + Initial tangent vector: Tangent vector at Point on the Real + number line R with components [-9999999999/1000/(a - b)] w.r.t. + Chart ((a, b), (t,)) + + d(t)/ds = Dt + d(Dt)/ds = -4*Dt^2/t + + + The test suite is passed:: + + sage: TestSuite(H).run() """ @@ -1551,7 +1770,7 @@ def _repr_(self): def _element_constructor_(self, metric, curve_parameter, initial_tangent_vector, chart=None, name=None, latex_name=None, is_isomorphism=False, - is_identity=False, verbose=True): + is_identity=False, verbose=False): r""" Construct an element of ``self``, i.e. an integrated geodesic `I \to M`, where `I` is a real interval and @@ -1605,7 +1824,7 @@ def _an_element_(self): sage: c = H._an_element_() ; c Integrated geodesic in the 4-dimensional differentiable manifold M - sage: sys = c.system() + sage: sys = c.system(verbose=True) Geodesic in the 4-dimensional differentiable manifold M equipped with Riemannian metric g on the 4-dimensional differentiable manifold M, and integrated over the Real @@ -1628,14 +1847,14 @@ def _an_element_(self): d(Dy)/dt = 0 d(Dz)/dt = 0 - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1, verbose=False) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1) [0.34375000000000056, 0.0, 0.0, 0.0] sage: H = IntegratedGeodesicSet(I, I) sage: c = H._an_element_() ; c Integrated geodesic in the Real interval (-1, 2) - sage: sys = c.system() + sage: sys = c.system(verbose=True) Geodesic in the Real interval (-1, 2) equipped with Riemannian metric g on the Open subset U of the Real number line R, and integrated over the Real interval (-1, 2) as a @@ -1651,9 +1870,9 @@ def _an_element_(self): d(t)/ds = Dt d(Dt)/ds = -4*Dt^2/t - sage: sol = c.solve(verbose=False) - sage: interp = c.interpolate(verbose=False) - sage: c(1, verbose=False) + sage: sol = c.solve() + sage: interp = c.interpolate() + sage: c(1) [1.1495758053215723] """ From 9f9a4994037de2265f08913afff5345be86556a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 27 Jun 2017 18:51:31 -0500 Subject: [PATCH 048/184] A ring embeds into its fraction field --- src/sage/rings/fraction_field.py | 254 +++++++++++++++++- .../rings/function_field/function_field.py | 6 +- 2 files changed, 243 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 375525177ce..c761e188d5a 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -1,4 +1,5 @@ -""" +# -*- coding: utf-8 -*- +r""" Fraction Field of Integral Domains AUTHORS: @@ -8,6 +9,9 @@ - Burcin Erocal +- Julian Rüth (2017-06-27): embedding into the field of fractions and its + section + EXAMPLES: Quotienting is a constructor for an element of the fraction field:: @@ -54,20 +58,13 @@ """ #***************************************************************************** -# -# Sage: System for Algebra and Geometry Experimentation -# # Copyright (C) 2005 William Stein +# 2017 Julian Rüth # -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# +# 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 __future__ import absolute_import @@ -80,8 +77,10 @@ from sage.misc.cachefunc import cached_method from sage.structure.parent import Parent -from sage.structure.coerce_maps import CallableConvertMap +from sage.structure.coerce_maps import CallableConvertMap, DefaultConvertMap_unique from sage.categories.basic import QuotientFields +from sage.categories.morphism import Morphism +from sage.categories.map import Section def FractionField(R, names=None): """ @@ -289,6 +288,10 @@ def _coerce_map_from_(self, S): from sage.rings.polynomial.laurent_polynomial_ring import \ LaurentPolynomialRing_generic + if S is self._R: + parent = self._R.Hom(self) + return parent.__make_element_class__(FractionFieldEmbedding)(self._R, self, category=parent.homset_category()) + # The case ``S`` being `\QQ` requires special handling since `\QQ` is # not implemented as a ``FractionField_generic``. if S is QQ and self._R.has_coerce_map_from(ZZ): @@ -779,3 +782,226 @@ def class_number(self): 1 """ return 1 + + +class FractionFieldEmbedding(DefaultConvertMap_unique): + r""" + The embedding of an integral domain into its field of fractions. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = R.fraction_field().coerce_map_from(R); f + Coercion map: + From: Univariate Polynomial Ring in x over Rational Field + To: Fraction Field of Univariate Polynomial Ring in x over Rational Field + + TESTS:: + + sage: from sage.rings.fraction_field import FractionFieldEmbedding + sage: isinstance(f, FractionFieldEmbedding) + True + sage: TestSuite(f).run() + + Check that :trac:`23185` has been resolved:: + + sage: R. = QQ[] + sage: K. = FunctionField(QQ) + sage: R.is_subring(K) + True + sage: R.is_subring(R.fraction_field()) + True + + """ + def is_surjective(self): + r""" + Return whether this map is surjective. + + EXAMPLES:: + + sage: R. = QQ[] + sage: R.fraction_field().coerce_map_from(R).is_surjective() + False + + """ + return self.domain().is_field() + + def is_injective(self): + r""" + Return whether this map is injective. + + EXAMPLES: + + The map from an integral domain to its fraction field is always + injective: + + sage: R. = QQ[] + sage: R.fraction_field().coerce_map_from(R).is_injective() + True + + """ + return True + + def section(self): + r""" + Return a section of this map. + + EXAMPLES:: + + sage: R. = QQ[] + sage: R.fraction_field().coerce_map_from(R).section() + Section map: + From: Fraction Field of Univariate Polynomial Ring in x over Rational Field + To: Univariate Polynomial Ring in x over Rational Field + + """ + from sage.categories.sets_with_partial_maps import SetsWithPartialMaps + from sage.all import Hom + parent = Hom(self.codomain(), self.domain(), SetsWithPartialMaps()) + return parent.__make_element_class__(FractionFieldEmbeddingSection)(self) + + def __eq__(self, other): + r""" + Return whether ``other`` is the same embedding into a fraction field. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = R.fraction_field().coerce_map_from(R) + sage: S. = GF(2)[] + sage: g = S.fraction_field().coerce_map_from(S) + + sage: f == g + False + sage: f == f + True + + """ + return isinstance(other, FractionFieldEmbedding) and other.domain() is self.domain() and other.codomain() is self.codomain() + + def __neq__(self, other): + r""" + Return whether ``other`` is not the same embedding into a fraction field. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = R.fraction_field().coerce_map_from(R) + sage: S. = GF(2)[] + sage: g = S.fraction_field().coerce_map_from(S) + + sage: f != g + True + sage: f != f + False + + """ + return not (self == other) + + def __hash__(self): + r""" + Return a hash value for this embedding. + + EXAMPLES:: + + sage: R. = QQ[] + sage: hash(R.fraction_field().coerce_map_from(R)) == hash(R.fraction_field().coerce_map_from(R)) + True + + """ + return hash((type(self), self.codomain())) + + +class FractionFieldEmbeddingSection(Section): + r""" + The section of the embedding of an integral domain into its field of + fractions. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = R.fraction_field().coerce_map_from(R).section(); f + Section map: + From: Fraction Field of Univariate Polynomial Ring in x over Rational Field + To: Univariate Polynomial Ring in x over Rational Field + + TESTS:: + + sage: from sage.rings.fraction_field import FractionFieldEmbeddingSection + sage: isinstance(f, FractionFieldEmbeddingSection) + True + sage: TestSuite(f).run() + + """ + def _call_(self, x): + r""" + Evaluate this map at ``x``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: K = R.fraction_field() + sage: x = K.gen() + sage: f = K.coerce_map_from(R).section() + sage: f(x) + x + sage: f(1/x) + Traceback (most recent call last): + ... + ValueError: fraction must have unit denominator + + """ + if not x.denominator().is_unit(): + raise ValueError("fraction must have unit denominator") + return x.numerator() * x.denominator().inverse_of_unit() + + def __eq__(self, other): + r""" + Return whether ``other`` is the same section. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = R.fraction_field().coerce_map_from(R).section() + sage: S. = GF(2)[] + sage: g = S.fraction_field().coerce_map_from(S).section() + + sage: f == g + False + sage: f == f + True + + """ + return isinstance(other, FractionFieldEmbeddingSection) and other.domain() is self.domain() and other.codomain() is self.codomain() + + def __neq__(self, other): + r""" + Return whether ``other`` is not the same section. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = R.fraction_field().coerce_map_from(R).section() + sage: S. = GF(2)[] + sage: g = S.fraction_field().coerce_map_from(S).section() + + sage: f != g + True + sage: f != f + False + + """ + return not (self == other) + + def __hash__(self): + r""" + Return a hash value for this section. + + EXAMPLES:: + + sage: R. = QQ[] + sage: hash(R.fraction_field().coerce_map_from(R).section()) == hash(R.fraction_field().coerce_map_from(R).section()) + True + + """ + return hash((type(self), self.domain())) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index f9dcb2e4c46..3b04cc66419 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -372,11 +372,11 @@ def _coerce_map_from_(self, source): from .function_field_order import FunctionFieldOrder if isinstance(source, FunctionFieldOrder): K = source.fraction_field() - source_to_K = K._generic_convert_map(source) if K is self: - return source_to_K + return self._generic_coerce_map(source) + source_to_K = K.coerce_map_from(source) K_to_self = self.coerce_map_from(K) - if K_to_self: + if source_to_K and K_to_self: return K_to_self * source_to_K from sage.categories.function_fields import FunctionFields if source in FunctionFields(): From 543088a49d66f2927741c086f3271f30c4ff86a8 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 28 Jun 2017 01:15:13 +0000 Subject: [PATCH 049/184] Fix broken printing when changing p in a p-adic extension, raise error when creating an extension with precision larger than the precision of the defining polynomial --- src/sage/rings/padics/factory.py | 17 ++++---- src/sage/rings/padics/local_generic.py | 56 ++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index a12ae1f8c8c..69deaf09d50 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -2489,10 +2489,11 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = #elif not isinstance(base.ground_ring_of_tower(), (pAdicRingLazy, pAdicFieldLazy)): # halt = None halt = None + prec_cap = min([c.precision_absolute() for c in modulus.list()] + [base.precision_cap()]) if prec is None: - prec = min([c.precision_absolute() for c in modulus.list()] + [base.precision_cap()]) - else: - prec = min([c.precision_absolute() for c in modulus.list()] + [base.precision_cap()] + [prec]) + prec = prec_cap + elif prec > prec_cap and base._prec_type() in ('capped-rel', 'capped-abs'): + raise ValueError("Not enough precision in defining polynomial") shift_seed = None modulus = truncate_to_prec(modulus, prec) elif is_eisenstein(modulus): @@ -2541,12 +2542,12 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = # give up on getting more precision shift_seed = -preseed.change_ring(base) shift_seed /= base.uniformizer() - if prec is None: - prec = min([c.precision_absolute() for c in shift_seed.list() if not c._is_exact_zero()] + + prec_cap = min([c.precision_absolute() for c in shift_seed.list() if not c._is_exact_zero()] + [modulus.leading_coefficient().precision_absolute()] + [base.precision_cap()]) * e - else: - prec = min([c.precision_absolute() * e for c in shift_seed.list() if not c._is_exact_zero()] + - [modulus.leading_coefficient().precision_absolute() * e] + [base.precision_cap() * e] + [prec]) + if prec is None: + prec = prec_cap + elif prec > prec_cap and base._prec_type() in ('capped-rel', 'capped-abs'): + raise ValueError("Not enough precision in defining polynomial") modulus = truncate_to_prec(modulus, (prec/e).ceil() + 1) else: if unram_name is None: diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 9f2db98e5e2..34e8137259b 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -295,6 +295,32 @@ def change(self, **kwds): 2 sage: Kdown.base_ring() 5-adic Field with floating precision 4 + + Changing the prime works for extensions as long as the defining polynomial is exact:: + + sage: x = polygen(ZZ) + sage: R. = Zp(5).extension(x^2 + 2) + sage: S = R.change(p=7) + sage: S.defining_polynomial() + (1 + O(7^20))*x^2 + (O(7^20))*x + (2 + O(7^20)) + sage: A. = Zp(5)[] + sage: R. = Zp(5).extension(y^2 + 2) + sage: S = R.change(p=7) + Traceback (most recent call last): + ... + NotImplementedError: conversion between padic extensions not implemented + + Moreover, you cannot currently increase the precision above the limit + imposed by the defining polynomial:: + + sage: R. = Zq(5^3) + sage: S = R.change(prec=50) + Traceback (most recent call last): + ... + ValueError: Not enough precision in defining polynomial + + Both of these issues will be partially alleviated with a switch to exact + defining polynomials in `trac`:23331 """ # We support both print_* and * for *=mode, pos, sep, alphabet for atr in ('print_mode', 'print_pos', 'print_sep', 'print_alphabet'): @@ -347,11 +373,12 @@ def get_unramified_modulus(q, res_name): raise TypeError('You must specify the type explicitly') elif ring.is_field(): ring = ring.ring_of_integers() + p = kwds.get('p', functor.p) + curpstr = str(self.prime()) # If we are switching to 'digits', or changing p, need to ensure a large enough alphabet. if 'alphabet' not in kwds and (kwds.get('mode') == 'digits' or (functor.extras['print_mode'].get('mode') == 'digits' and - kwds.get('p', functor.p) > functor.p)): - p = kwds.get('p', functor.p) + p > functor.p)): from .padic_printing import _printer_defaults kwds['alphabet'] = _printer_defaults.alphabet()[:p] for atr in ('p', 'prec', 'type'): @@ -365,10 +392,19 @@ def get_unramified_modulus(q, res_name): res_name = kwds.pop('res_name', names + '0') modulus = kwds.pop('modulus', get_unramified_modulus(q, res_name)) implementation = kwds.pop('implementation', 'FLINT') - for atr in ('names', 'check'): + # We have to change the way p prints in the default case + if 'names' in kwds: + functor.extras['names'] = kwds.pop('names') + elif functor.extras['names'][0] == curpstr: + functor.extras['names'] = (str(p),) + for atr in ('ram_name', 'var_name'): if atr in kwds: - functor.extras[atr] = kwds.pop(atr) - for atr in ('mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet', 'show_prec'): + functor.extras['print_mode'][atr] = kwds.pop(atr) + elif functor.extras['print_mode'].get(atr) == curpstr: + functor.extras['print_mode'][atr] = str(p) + if 'check' in kwds: + functor.extras['check'] = kwds.pop('check') + for atr in ('mode', 'pos', 'unram_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet', 'show_prec'): if atr in kwds: functor.extras['print_mode'][atr] = kwds.pop(atr) if kwds: @@ -413,9 +449,13 @@ def get_unramified_modulus(q, res_name): if q is not None and self.f() == 1: kwds['q'] = q ring = ring.change(**kwds) - if modulus is not None: - # the following should change after we switch to exact defining polynomials - functor.polys = [modulus.change_ring(ring)] + if modulus is None: + if len(functor.polys) != 1: + raise RuntimeError("Unexpected number of defining polynomials") + modulus = functor.polys[0] + if isinstance(modulus.base_ring(), pAdicBaseGeneric): + modulus.change_ring(ring) + functor.polys = [modulus] return functor(ring) def precision_cap(self): From b643d244032f32ef68692aa43e805ca9bc0eb206 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 28 Jun 2017 01:36:30 +0000 Subject: [PATCH 050/184] Delay substitution of DEFAULT_PREC in padics factory --- src/sage/rings/padics/factory.py | 46 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 69deaf09d50..c5ba26b31e6 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -109,6 +109,8 @@ def get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, pr sage: get_key_base(12, 5, 'capped-rel', 'digits', 0, None, None, None, None, None, None, True, False, ['capped-rel']) (12, 5, 'capped-rel', 'digits', '12', True, '|', ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B'), -1, True) """ + if prec is None: + prec = DEFAULT_PREC if check: if not isinstance(p, Integer): p = Integer(p) @@ -526,7 +528,7 @@ class Qp_class(UniqueFactory): sage: K = Qp(15, check=False); a = K(999); a 9 + 6*15 + 4*15^2 + O(15^20) """ - def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = None, + def create_key(self, p, prec = None, type = 'capped-rel', print_mode = None, halt = DEFAULT_HALT, names = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_terms = None, show_prec=None, check = True): """ @@ -601,7 +603,7 @@ def create_object(self, version, key): # Qq -- unramified extensions ###################################################### -def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, +def Qq(q, prec = None, type = 'capped-rel', modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, res_name = None, print_pos = None, print_sep = None, print_max_ram_terms = None, print_max_unram_terms = None, print_max_terse_terms = None, show_prec=None, check = True, implementation = 'FLINT'): @@ -1095,7 +1097,7 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, if not p.is_prime() or k <=0: raise ValueError("q must be a prime power") - if not isinstance(prec, Integer): prec = Integer(prec) + if prec is not None and not isinstance(prec, Integer): prec = Integer(prec) if not isinstance(halt, Integer): halt = Integer(halt) base = Qp(p=p, prec=prec, type=type, print_mode=print_mode, halt=halt, names=ram_name, print_pos=print_pos, @@ -1130,7 +1132,7 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, # Short constructor names for different types ###################################################### -def QpCR(p, prec = DEFAULT_PREC, *args, **kwds): +def QpCR(p, prec = None, *args, **kwds): """ A shortcut function to create capped relative `p`-adic fields. @@ -1144,7 +1146,7 @@ def QpCR(p, prec = DEFAULT_PREC, *args, **kwds): """ return Qp(p, prec, 'capped-rel', *args, **kwds) -def QpFP(p, prec = DEFAULT_PREC, *args, **kwds): +def QpFP(p, prec = None, *args, **kwds): """ A shortcut function to create floating point `p`-adic fields. @@ -1158,7 +1160,7 @@ def QpFP(p, prec = DEFAULT_PREC, *args, **kwds): """ return Qp(p, prec, 'floating-point', *args, **kwds) -#def QpL(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, +#def QpL(p, prec = None, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_terms = None, check=True): # """ # A shortcut function to create lazy p-adic fields. @@ -1173,7 +1175,7 @@ def QpFP(p, prec = DEFAULT_PREC, *args, **kwds): # type = 'lazy') -def QqCR(q, prec = DEFAULT_PREC, *args, **kwds): +def QqCR(q, prec = None, *args, **kwds): """ A shortcut function to create capped relative unramified `p`-adic fields. @@ -1188,7 +1190,7 @@ def QqCR(q, prec = DEFAULT_PREC, *args, **kwds): """ return Qq(q, prec, 'capped-rel', *args, **kwds) -def QqFP(q, prec = DEFAULT_PREC, *args, **kwds): +def QqFP(q, prec = None, *args, **kwds): """ A shortcut function to create floating point unramified `p`-adic fields. @@ -1203,7 +1205,7 @@ def QqFP(q, prec = DEFAULT_PREC, *args, **kwds): """ return Qq(q, prec, 'floating-point', *args, **kwds) -#def QqL(q, prec = DEFAULT_PREC, modulus = None, names=None, +#def QqL(q, prec = None, modulus = None, names=None, # print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_ram_terms = None, # print_max_unram_terms = None, print_max_terse_terms = None, check = True): @@ -1612,7 +1614,7 @@ class Zp_class(UniqueFactory): sage: a + b 1 + 5 + O(5^10) """ - def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = None, halt = DEFAULT_HALT, + def create_key(self, p, prec = None, type = 'capped-rel', print_mode = None, halt = DEFAULT_HALT, names = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_terms = None, show_prec = None, check = True): """ @@ -1688,7 +1690,7 @@ def create_object(self, version, key): # Zq -- unramified extensions ###################################################### -def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, +def Zq(q, prec = None, type = 'capped-rel', modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, res_name = None, print_pos = None, print_sep = None, print_max_ram_terms = None, print_max_unram_terms = None, print_max_terse_terms = None, show_prec = None, check = True, implementation = 'FLINT'): @@ -2198,7 +2200,7 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, F = q.factor() if len(F) != 1: raise ValueError("q must be a prime power") - if not isinstance(prec, Integer): + if prec is not None and not isinstance(prec, Integer): prec = Integer(prec) if not isinstance(halt, Integer): halt = Integer(halt) @@ -2242,7 +2244,7 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, # Short constructor names for different types ###################################################### -def ZpCR(p, prec = DEFAULT_PREC, *args, **kwds): +def ZpCR(p, prec = None, *args, **kwds): """ A shortcut function to create capped relative `p`-adic rings. @@ -2256,7 +2258,7 @@ def ZpCR(p, prec = DEFAULT_PREC, *args, **kwds): """ return Zp(p, prec, 'capped-rel', *args, **kwds) -def ZpCA(p, prec = DEFAULT_PREC, *args, **kwds): +def ZpCA(p, prec = None, *args, **kwds): """ A shortcut function to create capped absolute `p`-adic rings. @@ -2269,7 +2271,7 @@ def ZpCA(p, prec = DEFAULT_PREC, *args, **kwds): """ return Zp(p, prec, 'capped-abs', *args, **kwds) -def ZpFM(p, prec = DEFAULT_PREC, *args, **kwds): +def ZpFM(p, prec = None, *args, **kwds): """ A shortcut function to create fixed modulus `p`-adic rings. @@ -2282,7 +2284,7 @@ def ZpFM(p, prec = DEFAULT_PREC, *args, **kwds): """ return Zp(p, prec, 'fixed-mod', *args, **kwds) -def ZpFP(p, prec = DEFAULT_PREC, *args, **kwds): +def ZpFP(p, prec = None, *args, **kwds): """ A shortcut function to create floating point `p`-adic rings. @@ -2296,7 +2298,7 @@ def ZpFP(p, prec = DEFAULT_PREC, *args, **kwds): """ return Zp(p, prec, 'floating-point', *args, **kwds) -#def ZpL(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, +#def ZpL(p, prec = None, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_terms = None, check=True): # """ # A shortcut function to create lazy `p`-adic rings. @@ -2310,7 +2312,7 @@ def ZpFP(p, prec = DEFAULT_PREC, *args, **kwds): # print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, # type = 'lazy') -def ZqCR(q, prec = DEFAULT_PREC, *args, **kwds): +def ZqCR(q, prec = None, *args, **kwds): """ A shortcut function to create capped relative unramified `p`-adic rings. @@ -2324,7 +2326,7 @@ def ZqCR(q, prec = DEFAULT_PREC, *args, **kwds): """ return Zq(q, prec, 'capped-rel', *args, **kwds) -def ZqCA(q, prec = DEFAULT_PREC, *args, **kwds): +def ZqCA(q, prec = None, *args, **kwds): """ A shortcut function to create capped absolute unramified `p`-adic rings. @@ -2337,7 +2339,7 @@ def ZqCA(q, prec = DEFAULT_PREC, *args, **kwds): """ return Zq(q, prec, 'capped-abs', *args, **kwds) -def ZqFM(q, prec = DEFAULT_PREC, *args, **kwds): +def ZqFM(q, prec = None, *args, **kwds): """ A shortcut function to create fixed modulus unramified `p`-adic rings. @@ -2350,7 +2352,7 @@ def ZqFM(q, prec = DEFAULT_PREC, *args, **kwds): """ return Zq(q, prec, 'fixed-mod', *args, **kwds) -def ZqFP(q, prec = DEFAULT_PREC, *args, **kwds): +def ZqFP(q, prec = None, *args, **kwds): """ A shortcut function to create floating point unramified `p`-adic rings. @@ -2364,7 +2366,7 @@ def ZqFP(q, prec = DEFAULT_PREC, *args, **kwds): """ return Zq(q, prec, 'floating-point', *args, **kwds) -#def ZqL(q, prec = DEFAULT_PREC, modulus = None, names=None, +#def ZqL(q, prec = None, modulus = None, names=None, # print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_ram_terms = None, # print_max_unram_terms = None, print_max_terse_terms = None, check = True): From e935ca9f842d729f349b34f058e647a55068d0f1 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 28 Jun 2017 02:05:12 +0000 Subject: [PATCH 051/184] Change Zq and Qq to be defined by an exact polynomial --- src/sage/rings/padics/factory.py | 4 ++-- src/sage/rings/padics/local_generic.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index c5ba26b31e6..07ef191458b 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -1120,7 +1120,7 @@ def Qq(q, prec = None, type = 'capped-rel', modulus = None, names=None, if modulus is None: from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF - modulus = PolynomialRing(base, 'x')(GF(p**k, res_name).modulus().change_ring(ZZ)) + modulus = GF(p**k, res_name).modulus().change_ring(ZZ) return ExtensionFactory(base=base, premodulus=modulus, prec=prec, print_mode=print_mode, halt=halt, names=names, res_name=res_name, ram_name=ram_name, print_pos=print_pos, print_sep=print_sep, print_max_ram_terms=print_max_ram_terms, @@ -2232,7 +2232,7 @@ def Zq(q, prec = None, type = 'capped-rel', modulus = None, names=None, from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF if ram_name is None: ram_name = str(F[0][0]) - modulus = PolynomialRing(base, 'x')(GF(q, res_name).modulus().change_ring(ZZ)) + modulus = GF(q, res_name).modulus().change_ring(ZZ) return ExtensionFactory(base=base, premodulus=modulus, prec=prec, print_mode=print_mode, halt=halt, names=names, res_name=res_name, ram_name=ram_name, print_pos=print_pos, print_sep=print_sep, print_max_ram_terms=print_max_ram_terms, diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 34e8137259b..cabeb678f4c 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -310,17 +310,21 @@ def change(self, **kwds): ... NotImplementedError: conversion between padic extensions not implemented - Moreover, you cannot currently increase the precision above the limit - imposed by the defining polynomial:: + If the defining polynomial is exact, you can raise the precision at will:: sage: R. = Zq(5^3) sage: S = R.change(prec=50) + sage: S.defining_polynomial() + (1 + O(5^50))*x^3 + (O(5^50))*x^2 + (3 + O(5^50))*x + (3 + O(5^50)) + + However, you cannot increase the precision above the limit imposed by + an inexact defining polynomial:: + + sage: R. = Zp(5).extension(y^2 + 2) + sage: S = R.change(prec=50) Traceback (most recent call last): ... ValueError: Not enough precision in defining polynomial - - Both of these issues will be partially alleviated with a switch to exact - defining polynomials in `trac`:23331 """ # We support both print_* and * for *=mode, pos, sep, alphabet for atr in ('print_mode', 'print_pos', 'print_sep', 'print_alphabet'): From 547b66d756630f5e68b01b3f31b460ea4b3d9f13 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 28 Jun 2017 02:20:30 +0000 Subject: [PATCH 052/184] Fix error in check=False constructor for Qq --- src/sage/rings/padics/factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 07ef191458b..6225828dfff 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -2474,7 +2474,7 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = if not isinstance(names, str): names = str(names) else: - modulus = premodulus + modulus = premodulus.change_ring(base) # We now decide on the extension class: unramified, Eisenstein, two-step or general if unram or is_unramified(modulus): From bacdac11f54c8afc23ff265c1fc98cba64a5f113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 27 Jun 2017 21:23:00 -0500 Subject: [PATCH 053/184] simplify equality checks --- src/sage/rings/fraction_field.py | 67 ++++++++++---------------------- 1 file changed, 21 insertions(+), 46 deletions(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index c761e188d5a..3ba12ff9192 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -76,6 +76,7 @@ import sage.misc.latex as latex from sage.misc.cachefunc import cached_method +from sage.structure.richcmp import richcmp_not_equal, op_EQ, op_NE from sage.structure.parent import Parent from sage.structure.coerce_maps import CallableConvertMap, DefaultConvertMap_unique from sage.categories.basic import QuotientFields @@ -860,9 +861,9 @@ def section(self): parent = Hom(self.codomain(), self.domain(), SetsWithPartialMaps()) return parent.__make_element_class__(FractionFieldEmbeddingSection)(self) - def __eq__(self, other): + def _richcmp_(self, other, op): r""" - Return whether ``other`` is the same embedding into a fraction field. + Compare this element to ``other`` with respect to ``op``. EXAMPLES:: @@ -871,32 +872,19 @@ def __eq__(self, other): sage: S. = GF(2)[] sage: g = S.fraction_field().coerce_map_from(S) - sage: f == g + sage: f == g # indirect doctest False sage: f == f True """ - return isinstance(other, FractionFieldEmbedding) and other.domain() is self.domain() and other.codomain() is self.codomain() - - def __neq__(self, other): - r""" - Return whether ``other`` is not the same embedding into a fraction field. - - EXAMPLES:: - - sage: R. = QQ[] - sage: f = R.fraction_field().coerce_map_from(R) - sage: S. = GF(2)[] - sage: g = S.fraction_field().coerce_map_from(S) - - sage: f != g - True - sage: f != f - False - - """ - return not (self == other) + if isinstance(other, FractionFieldEmbedding) and other.domain() is self.domain() and other.codomain() is self.codomain(): + if op == op_EQ: + return True + if op == op_NE: + return False + else: + return richcmp_not_equal(self, other, op) def __hash__(self): r""" @@ -955,9 +943,9 @@ def _call_(self, x): raise ValueError("fraction must have unit denominator") return x.numerator() * x.denominator().inverse_of_unit() - def __eq__(self, other): + def _richcmp_(self, other, op): r""" - Return whether ``other`` is the same section. + Compare this element to ``other`` with respect to ``op``. EXAMPLES:: @@ -966,32 +954,19 @@ def __eq__(self, other): sage: S. = GF(2)[] sage: g = S.fraction_field().coerce_map_from(S).section() - sage: f == g + sage: f == g # indirect doctest False sage: f == f True """ - return isinstance(other, FractionFieldEmbeddingSection) and other.domain() is self.domain() and other.codomain() is self.codomain() - - def __neq__(self, other): - r""" - Return whether ``other`` is not the same section. - - EXAMPLES:: - - sage: R. = QQ[] - sage: f = R.fraction_field().coerce_map_from(R).section() - sage: S. = GF(2)[] - sage: g = S.fraction_field().coerce_map_from(S).section() - - sage: f != g - True - sage: f != f - False - - """ - return not (self == other) + if isinstance(other, FractionFieldEmbeddingSection) and other.domain() is self.domain() and other.codomain() is self.codomain(): + if op == op_EQ: + return True + if op == op_NE: + return False + else: + return richcmp_not_equal(self, other, op) def __hash__(self): r""" From bf8d7606b21ecbbcdebaf9a96e3f5f2befe1756c Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 28 Jun 2017 22:59:02 +0000 Subject: [PATCH 054/184] Fix formatting errors in change docstring, allow specifying unram_name rather than names in changing q on Zp or Qp --- src/sage/rings/padics/local_generic.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index cabeb678f4c..ef3a3240956 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -216,23 +216,23 @@ def change(self, **kwds): - ``res_name`` -- string - ``unram_name`` -- string - ``ram_name`` -- string - -- ``names`` -- string - -- ``modulus`` -- polynomial + - ``names`` -- string + - ``modulus`` -- polynomial The following arguments have special behavior: - -- ``prec`` -- integer. If the precision is increased on an extension ring, + - ``prec`` -- integer. If the precision is increased on an extension ring, the precision on the base is increased as necessary (respecting ramification). If the precision is decreased, the precision of the base is unchanged. - -- ``field`` -- bool. If True, switch to a tower of fields via the fraction field. + - ``field`` -- bool. If True, switch to a tower of fields via the fraction field. If False, switch to a tower of rings of integers. - -- ``q`` -- prime power. Replace the initial unramified extension of `\Qp` or `\Zp` + - ``q`` -- prime power. Replace the initial unramified extension of `\Qp` or `\Zp` with an unramified extension of residue cardinality `q`. If the initial extension is ramified, add in an unramified extension. - -- ``base`` -- ring or field. Use a specific base ring instead of recursively + - ``base`` -- ring or field. Use a specific base ring instead of recursively calling :meth:`change` down the tower. See the :mod:`constructors ` for more details on the @@ -391,6 +391,8 @@ def get_unramified_modulus(q, res_name): if q is not None: if 'names' in kwds: names = kwds.pop('names') + elif 'unram_name' in kwds: + names = kwds.pop('unram_name') else: raise TypeError("You must specify the name of the generator") res_name = kwds.pop('res_name', names + '0') From 2494590fa91a61e4bde7b1306c2acf7000203a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 29 Jun 2017 21:22:23 +0300 Subject: [PATCH 055/184] Unify test -> tests. --- src/sage/algebras/free_algebra.py | 2 +- .../algebras/letterplace/free_algebra_letterplace.pyx | 6 +++--- src/sage/categories/map.pyx | 4 ++-- src/sage/categories/pushout.py | 4 ++-- src/sage/combinat/e_one_star.py | 8 ++++---- src/sage/combinat/finite_state_machine.py | 4 ++-- src/sage/combinat/subword.py | 2 +- src/sage/dynamics/interval_exchanges/iet.py | 2 +- src/sage/games/sudoku.py | 4 ++-- src/sage/games/sudoku_backtrack.pyx | 2 +- src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py | 6 +++--- src/sage/graphs/asteroidal_triples.pyx | 2 +- src/sage/graphs/base/c_graph.pyx | 2 +- src/sage/graphs/distances_all_pairs.pyx | 4 ++-- src/sage/graphs/generators/intersection.py | 2 +- src/sage/graphs/generic_graph.py | 2 +- src/sage/graphs/graph.py | 4 ++-- .../graphs/graph_decompositions/vertex_separation.pyx | 8 ++++---- src/sage/graphs/graph_latex.py | 2 +- src/sage/interfaces/expect.py | 2 +- src/sage/interfaces/fricas.py | 2 +- src/sage/interfaces/lisp.py | 4 ++-- src/sage/interfaces/polymake.py | 2 +- src/sage/libs/ntl/ntl_ZZ_pEX.pyx | 6 +++--- src/sage/libs/ntl/ntl_lzz_p.pyx | 2 +- src/sage/libs/singular/function.pyx | 2 +- src/sage/matrix/matrix_integer_dense.pyx | 2 +- src/sage/matrix/matrix_integer_sparse.pyx | 2 +- src/sage/matrix/matrix_space.py | 2 +- src/sage/matrix/misc.pyx | 4 ++-- src/sage/misc/sageinspect.py | 2 +- src/sage/numerical/backends/cplex_backend.pyx | 2 +- src/sage/numerical/mip.pyx | 2 +- src/sage/rings/finite_rings/element_givaro.pyx | 2 +- src/sage/rings/fraction_field.py | 2 +- src/sage/rings/fraction_field_FpT.pyx | 2 +- src/sage/rings/number_field/number_field.py | 2 +- .../rings/number_field/number_field_element_quadratic.pyx | 2 +- src/sage/rings/polynomial/multi_polynomial.pyx | 2 +- src/sage/rings/polynomial/multi_polynomial_ideal.py | 6 +++--- .../rings/polynomial/multi_polynomial_libsingular.pyx | 2 +- src/sage/rings/polynomial/polynomial_quotient_ring.py | 2 +- src/sage/rings/polynomial/polynomial_template.pxi | 2 +- src/sage/rings/polynomial/term_order.py | 6 +++--- src/sage/rings/real_lazy.pyx | 2 +- src/sage/structure/category_object.pyx | 2 +- 46 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index de58a4e65c9..d3bb0a8fc78 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -410,7 +410,7 @@ def __init__(self, R, n, names): sage: F. = FreeAlgebra(QQ, 3); F # indirect doctet Free Algebra on 3 generators (x, y, z) over Rational Field - TEST: + TESTS: Note that the following is *not* the recommended way to create a free algebra:: diff --git a/src/sage/algebras/letterplace/free_algebra_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_letterplace.pyx index 595b62ddd4c..22ba2494f4f 100644 --- a/src/sage/algebras/letterplace/free_algebra_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_letterplace.pyx @@ -140,7 +140,7 @@ cdef MPolynomialRing_libsingular make_letterplace_ring(base_ring,blocks): variable names of the `n`-th block (`n>0`) ending with ``"_%d"%n``. - TEST: + TESTS: Note that, since the algebras are cached, we need to choose a different base ring, since other doctests could have a @@ -726,7 +726,7 @@ cdef class FreeAlgebra_letterplace(Algebra): generators are equal, and the base ring of ``R`` coerces into the base ring of self. - TEST: + TESTS: Coercion from the base ring:: @@ -813,7 +813,7 @@ cdef class FreeAlgebra_letterplace(Algebra): This is forwarded to the initialisation of :class:`~sage.algebras.letterplace.free_algebra_element_letterplace.FreeAlgebraElement_letterplace`. - TEST: + TESTS: This method applied to the dictionary of any element must return the same element. This must hold true even if the diff --git a/src/sage/categories/map.pyx b/src/sage/categories/map.pyx index 77d204c7556..1055b247fe7 100644 --- a/src/sage/categories/map.pyx +++ b/src/sage/categories/map.pyx @@ -699,7 +699,7 @@ cdef class Map(Element): sage: phi(I) Ideal (y, x) of Multivariate Polynomial Ring in x, y over Rational Field - TEST: + TESTS: We test that the map can be applied to something that converts (but not coerces) into the domain and can *not* be dealt with @@ -1002,7 +1002,7 @@ cdef class Map(Element): sage: phi_xz.category_for() Category of monoids - TEST: + TESTS: This illustrates that it is not tested whether the maps can actually be composed, i.e., whether codomain and domain match. diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 0fca565cb00..612f3c89a55 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -1564,7 +1564,7 @@ def _apply_functor(self, R): """ Apply the functor to an object of ``self``'s domain. - TEST: + TESTS: The following is a test against a bug discussed at :trac:`8800`:: @@ -2585,7 +2585,7 @@ def commutes(self,other): sage: F1.commutes(F2) True - TEST: + TESTS: The fraction field ``R`` in the example below has no completion method. But completion commutes with the fraction field functor, diff --git a/src/sage/combinat/e_one_star.py b/src/sage/combinat/e_one_star.py index 5857f5bd560..4c3d78d3316 100644 --- a/src/sage/combinat/e_one_star.py +++ b/src/sage/combinat/e_one_star.py @@ -273,7 +273,7 @@ def __init__(self, v, t, color=None): sage: f.type() 3 - TEST: + TESTS: We test that types can be given by an int (see :trac:`10699`):: @@ -593,7 +593,7 @@ def __init__(self, faces, face_contour=None): sage: P Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] - TEST: + TESTS: We test that colors are not anymore mixed up between Patches (see :trac:`11255`):: @@ -670,7 +670,7 @@ def __hash__(self): sage: hash(P) #random -4839605361791007520 - TEST: + TESTS: We test that two equal patches have the same hash (see :trac:`11255`):: @@ -1484,7 +1484,7 @@ def __call__(self, patch, iterations=1): sage: E(P, iterations=4) Patch of 31 faces - TEST: + TESTS: We test that iterations=0 works (see :trac:`10699`):: diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 1dd58caa480..f9fdbc7ff06 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -4328,7 +4328,7 @@ def format_transition_label_reversed(self, word): \path[->] (v0) edge[loop above] node {$0\mid 3 2 1$} (); \end{tikzpicture} - TEST: + TESTS: Check that :trac:`16357` is fixed:: @@ -4409,7 +4409,7 @@ def default_format_transition_label(self, word): \path[->] (v0) edge node[rotate=360.00, anchor=south] {$t$} (v1); \end{tikzpicture} - TEST: + TESTS: Check that :trac:`16357` is fixed:: diff --git a/src/sage/combinat/subword.py b/src/sage/combinat/subword.py index 0fcad680b57..6d941c4c414 100644 --- a/src/sage/combinat/subword.py +++ b/src/sage/combinat/subword.py @@ -523,7 +523,7 @@ def smallest_positions(word, subword, pos=0): sage: sage.combinat.subword.smallest_positions([1,3,3,5,4,5,3,5],[3,5,3],3) False - TEST: + TESTS: We check for :trac:`5534`:: diff --git a/src/sage/dynamics/interval_exchanges/iet.py b/src/sage/dynamics/interval_exchanges/iet.py index 7ab82674607..a6e7f780be4 100644 --- a/src/sage/dynamics/interval_exchanges/iet.py +++ b/src/sage/dynamics/interval_exchanges/iet.py @@ -526,7 +526,7 @@ def in_which_interval(self, x, interval=0): label -- a label corresponding to an interval - TEST: + TESTS: :: diff --git a/src/sage/games/sudoku.py b/src/sage/games/sudoku.py index dd1cbc8fb23..d1b37c663d6 100644 --- a/src/sage/games/sudoku.py +++ b/src/sage/games/sudoku.py @@ -366,7 +366,7 @@ def to_list(self): sage: s.to_list() [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 9, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 5, 0, 9, 0, 3, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 8, 5, 0, 0, 4, 0, 7, 0, 0, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 9, 0, 8, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1] - TEST: + TESTS: This tests the input and output of Sudoku puzzles as lists. :: @@ -389,7 +389,7 @@ def to_matrix(self): [0 0 1 4] [0 0 3 0] - TEST: + TESTS: This tests the input and output of Sudoku puzzles as matrices over `\ZZ`. :: diff --git a/src/sage/games/sudoku_backtrack.pyx b/src/sage/games/sudoku_backtrack.pyx index e660ff4430c..ad28e31c150 100644 --- a/src/sage/games/sudoku_backtrack.pyx +++ b/src/sage/games/sudoku_backtrack.pyx @@ -20,7 +20,7 @@ def backtrack_all(n, puzzle): A list of solutions, where each solution is a (1-based) list similar to ``puzzle``. - TEST: + TESTS: This is just a cursory test here, since eventually this code will move. See the `backtrack` method of the `Sudoku` class in the diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index b88ae461ad5..1ea867ad453 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -724,7 +724,7 @@ def complete(self): sage: g.is_complete() True - TEST: + TESTS: Check that floating points remain floating points through this method:: @@ -975,7 +975,7 @@ def dist(self, other): sage: g.dist(p) +Infinity - TEST: + TESTS: Check that floating points remain floating points in :meth:`dist` :: @@ -1646,7 +1646,7 @@ def _to_std_geod(self, p): sage: bool(abs(A(e).coordinates()) > 10**9) True - TEST: + TESTS: Check that floating points remain floating points through this method:: diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index 73d2f9899eb..38e87a2c6fe 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -127,7 +127,7 @@ def is_asteroidal_triple_free(G, certificate=False): sage: is_asteroidal_triple_free(G, certificate=True) (False, [0, 2, 6]) - TEST: + TESTS: Giving anything else than a Graph:: diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index d81b59debad..13737577bd5 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -2257,7 +2257,7 @@ cdef class CGraphBackend(GenericGraphBackend): sage: G.shortest_path_length(1, 3, weight_function=lambda e:e[2]['weight']) 2 - TEST: + TESTS: Bugfix from :trac:`7673` :: diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 5965dd18fa0..032e1057289 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -833,7 +833,7 @@ def eccentricity(G, algorithm="standard"): sage: eccentricity(g) [2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - TEST: + TESTS: All algorithms are valid:: @@ -1337,7 +1337,7 @@ def diameter(G, algorithm='iFUB', source=None): sage: lbm = diameter(G, algorithm='multi-sweep') sage: if not (lb2<=lbm and lbm<=d3): print("Something goes wrong!") - TEST: + TESTS: This was causing a segfault. Fixed in :trac:`17873` :: diff --git a/src/sage/graphs/generators/intersection.py b/src/sage/graphs/generators/intersection.py index 69d5a0c27e9..a81c4a641fb 100644 --- a/src/sage/graphs/generators/intersection.py +++ b/src/sage/graphs/generators/intersection.py @@ -333,7 +333,7 @@ def ToleranceGraph(tolrep): sage: g.is_isomorphic(graphs.PathGraph(3)) True - TEST: + TESTS: Giving negative third value:: diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index b3adb0aa3d1..332fa179b1e 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -16392,7 +16392,7 @@ def average_distance(self, by_weight=False, algorithm=None, results in the theory of the Wiener number. *Indian Journal of Chemistry*, 32A:651--661, 1993. - TEST: + TESTS: Giving an empty graph:: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index b63135d8e64..04b49a70969 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1360,7 +1360,7 @@ def sparse6_string(self): sage: Graph(':?',data_structure="sparse") == G True - TEST: + TESTS: Check that :trac:`18445` is fixed:: @@ -3010,7 +3010,7 @@ def is_perfect(self, certificate = False): sage: g.is_perfect(certificate = True) Subgraph of (Petersen graph): Graph on 5 vertices - TEST: + TESTS: Check that :trac:`13546` has been fixed:: diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index 4683b242a17..773d49a5b90 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -324,7 +324,7 @@ def lower_bound(G): sage: lower_bound(g) 1 - TEST: + TESTS: Given anything else than a Graph or a DiGraph:: @@ -669,7 +669,7 @@ def path_decomposition(G, algorithm = "BAB", cut_off=None, upper_bound=None, ver sage: pw, L = path_decomposition(g, algorithm = "MILP"); pw 2 - TEST: + TESTS: Given anything else than a Graph:: @@ -918,7 +918,7 @@ def vertex_separation_exp(G, verbose = False): sage: vertex_separation_exp(g) (1, [0, 1, 2, 3, 4, 5]) - TEST: + TESTS: Given anything else than a Graph or a DiGraph:: @@ -1105,7 +1105,7 @@ def is_valid_ordering(G, L): sage: vertex_separation.is_valid_ordering(G, [1,2]) False - TEST: + TESTS: Giving anything else than a Graph or a DiGraph:: diff --git a/src/sage/graphs/graph_latex.py b/src/sage/graphs/graph_latex.py index 6cb6dd76239..442a064de79 100644 --- a/src/sage/graphs/graph_latex.py +++ b/src/sage/graphs/graph_latex.py @@ -221,7 +221,7 @@ \end{tikzpicture} -TEST: +TESTS: This graph will look horrible, but it illustrates (and tests) a great variety of the possible options available through Sage's diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index b5de3868d8e..929f1ce0904 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -650,7 +650,7 @@ def _local_tmpfile(self): A string that provides a temporary filename and is unique for the given interface. - TEST: + TESTS: The filename is cached:: diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index ca794cee56d..3b368721554 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -850,7 +850,7 @@ def __getitem__(self, n): - can we somehow implement negative arguments? - TEST: + TESTS: sage: fricas("[1,2,3]")[0] # optional - fricas 1 diff --git a/src/sage/interfaces/lisp.py b/src/sage/interfaces/lisp.py index 3abec1cf7ea..1913191fb1c 100644 --- a/src/sage/interfaces/lisp.py +++ b/src/sage/interfaces/lisp.py @@ -113,7 +113,7 @@ def eval(self, code, strip=True, **kwds): sage: lisp.eval('(+ 2 2)') '4' - TEST: + TESTS: Verify that it works when input == output:: @@ -158,7 +158,7 @@ def set(self, var, value): sage: lisp.get('x') '2' - TEST: + TESTS: It must also be possible to eval the variable by name:: diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 30e51394be1..e028f3a14f5 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -411,7 +411,7 @@ def _keyboard_interrupt(self): """ Interrupt a computation with - TEST: + TESTS: For reasons that are not clear to the author, the following test is very flaky. Therefore, this test is marked as "not tested". diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx index af48ddaf12c..5ba292c9163 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx @@ -138,7 +138,7 @@ cdef class ntl_ZZ_pEX(object): def __reduce__(self): """ - TEST: + TESTS: sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) sage: a = ntl.ZZ_pE([3,2], c) sage: b = ntl.ZZ_pE([1,2], c) @@ -152,7 +152,7 @@ cdef class ntl_ZZ_pEX(object): """ Returns a string representation of self. - TEST: + TESTS: sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) sage: a = ntl.ZZ_pE([3,2], c) sage: b = ntl.ZZ_pE([1,2], c) @@ -168,7 +168,7 @@ cdef class ntl_ZZ_pEX(object): """ Return a copy of self. - TEST: + TESTS: sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) sage: a = ntl.ZZ_pE([3,2], c) sage: b = ntl.ZZ_pE([1,2], c) diff --git a/src/sage/libs/ntl/ntl_lzz_p.pyx b/src/sage/libs/ntl/ntl_lzz_p.pyx index c0998a4343f..1c957a10fe3 100644 --- a/src/sage/libs/ntl/ntl_lzz_p.pyx +++ b/src/sage/libs/ntl/ntl_lzz_p.pyx @@ -407,7 +407,7 @@ def make_zz_p(val, context): """ For unpickling. - TEST: + TESTS: sage: f = ntl.zz_p(1, 12) sage: loads(dumps(f)) == f True diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index ae6737445ea..91dffc4d852 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -929,7 +929,7 @@ cdef class Converter(SageObject): - ``to_convert`` - a Singular ``leftv`` - TEST: + TESTS: Check that negative integers come through unscathed:: diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 783a170e051..d3db7138373 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -3271,7 +3271,7 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: B.rational_reconstruction(389) == A/3 True - TEST: + TESTS: Check that :trac:`9345` is fixed:: diff --git a/src/sage/matrix/matrix_integer_sparse.pyx b/src/sage/matrix/matrix_integer_sparse.pyx index dc7df89907b..9ef0928d1dd 100644 --- a/src/sage/matrix/matrix_integer_sparse.pyx +++ b/src/sage/matrix/matrix_integer_sparse.pyx @@ -383,7 +383,7 @@ cdef class Matrix_integer_sparse(Matrix_sparse): [ 7 2 2 3] [ 4 3 4 5/7] - TEST: + TESTS: Check that :trac:`9345` is fixed:: diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index deab170315d..4c37d6cf1bb 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -233,7 +233,7 @@ def __init__(self, base_ring, sparse=False, implementation='flint'): """ - TEST: + TESTS: We test that in the real or complex double dense case, conversion from the base ring is done by a call morphism. diff --git a/src/sage/matrix/misc.pyx b/src/sage/matrix/misc.pyx index 5eacdbe3a92..1c17c4595c3 100644 --- a/src/sage/matrix/misc.pyx +++ b/src/sage/matrix/misc.pyx @@ -67,7 +67,7 @@ def matrix_integer_dense_rational_reconstruction(Matrix_integer_dense A, Integer [ 7/3 2/3 6 1] [ 4/3 1 4/3 5/3] - TEST: + TESTS: Check that :trac:`9345` is fixed:: @@ -154,7 +154,7 @@ def matrix_integer_sparse_rational_reconstruction(Matrix_integer_sparse A, Integ [ 7 2 2 3] [ 4 3 4 5/7] - TEST: + TESTS: Check that :trac:`9345` is fixed:: diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index dbcc3e92aca..13fd29fc50a 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -840,7 +840,7 @@ def _split_syntactical_unit(s): sage: _split_syntactical_unit('123') ('1', '23') - TEST: + TESTS: The following was fixed in :trac:`16309`:: diff --git a/src/sage/numerical/backends/cplex_backend.pyx b/src/sage/numerical/backends/cplex_backend.pyx index ca7cb7d745f..d134d5b6214 100644 --- a/src/sage/numerical/backends/cplex_backend.pyx +++ b/src/sage/numerical/backends/cplex_backend.pyx @@ -1535,7 +1535,7 @@ cdef class CPLEXBackend(GenericBackend): sage: p.solver_parameter("logfile") # optional - CPLEX '' - TEST: + TESTS: Print the logfile's content (through :class:`MixedIntegerLinearProgram`):: diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index a0553ee14b1..170ce526e05 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -611,7 +611,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: cp.solve() 6.0 - TEST: + TESTS: Test that `deepcopy` makes actual copies but preserves identities:: diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index c9dc9a1dac0..422b948b3aa 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -1199,7 +1199,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): sage: ~a*a 1 - TEST: + TESTS: Check that trying to invert zero raises an error (see :trac:`12217`):: diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 375525177ce..3f65ff62f69 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -338,7 +338,7 @@ def _number_field_to_frac_of_ring_of_integers(self, x): - Element of ``self`` - TEST: + TESTS: We demonstrate that :trac:`7958` is resolved in the case of number fields:: diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index 685d97b1650..9adab38c977 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -1100,7 +1100,7 @@ cdef class Polyring_FpT_coerce(RingHomomorphism_coercion): sage: f(2*t + 2, 2, reduce=False) (2*t + 2)/2 - TEST: + TESTS: Check that :trac:`12217` and :trac:`16811` are fixed:: diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index c5cfe2c731f..c3475df906d 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -3904,7 +3904,7 @@ def S_units(self, S, proof=True): sage: p[0].parent() Number Field in alpha with defining polynomial x^3 + x + 1 - TEST: + TESTS: This checks that the multiple entries issue at :trac:`9341` is fixed:: diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 478cd79a09f..2a2e945d694 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -324,7 +324,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): """ Used for pickling. - TEST: + TESTS: sage: K. = NumberField(x^2-13) sage: loads(dumps(a)) == a diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 2fa0e2f4205..3c81b2e31f4 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -1236,7 +1236,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: f.sylvester_matrix(g, x).determinant() == f.resultant(g, x) True - TEST: + TESTS: The variable is optional:: diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 21f60c0b7d3..1ff1017d87b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2033,7 +2033,7 @@ def quotient(self, J): sage: x * (y*z + x) in I True - TEST: + TESTS: This example checks :trac:`16301`:: @@ -3110,7 +3110,7 @@ def __lt__(self, other): sage: I < J True - TEST: + TESTS: We test to make sure that pickling works with the cached Groebner basis:: @@ -3303,7 +3303,7 @@ def __eq__(self, other): sage: I == J True - TEST: + TESTS: This example checks :trac:`12802`:: diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 570037458ac..df3d54bb865 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -353,7 +353,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): sage: P(2^32-1) 4294967295 - TEST: + TESTS: Make sure that a faster coercion map from the base ring is used; see :trac:`9944`:: diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 74fc51ed63f..31bba287aa3 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -348,7 +348,7 @@ def __init__(self, ring, polynomial, name=None, category=None): def __reduce__(self): """ - TEST: + TESTS: Note the polynomial quotient rings are not unique parent structures:: diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi index adf529cbd6d..3929be1755e 100644 --- a/src/sage/rings/polynomial/polynomial_template.pxi +++ b/src/sage/rings/polynomial/polynomial_template.pxi @@ -209,7 +209,7 @@ cdef class Polynomial_template(Polynomial): sage: P. = GF(2)[] sage: del x - TEST: + TESTS: The following has been a problem in a preliminary version of :trac:`12313`:: diff --git a/src/sage/rings/polynomial/term_order.py b/src/sage/rings/polynomial/term_order.py index c169f3332f8..9b393ad5242 100644 --- a/src/sage/rings/polynomial/term_order.py +++ b/src/sage/rings/polynomial/term_order.py @@ -628,7 +628,7 @@ def __init__(self, name='lex', n=0, force=False): constructed. However, it is useful if block orders are to be constructed from this ``TermOrder`` object later. - TEST: + TESTS: We demonstrate that non-positive weights are refused and non-integral weights are converted to integers (and potentially rounded):: @@ -1766,7 +1766,7 @@ def singular_str(self): // : names x8 x9 // block 4 : ordering C - TEST: + TESTS: The 'degneglex' ordering is somehow special, it looks like a block ordering in SINGULAR. @@ -1820,7 +1820,7 @@ def singular_moreblocks(self): sage: T.singular_moreblocks() 2 - TEST: + TESTS: The 'degneglex' ordering is somehow special: SINGULAR handles it using an extra weight vector block. diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index 6c366e87314..2ba4fb7088a 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -1192,7 +1192,7 @@ cdef class LazyBinop(LazyFieldElement): """ For pickling. - TEST: + TESTS: sage: from sage.rings.real_lazy import LazyBinop sage: a = LazyBinop(CLF, 3, 2, operator.div) sage: loads(dumps(a)) == a diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index d710683330b..10a9cb94468 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -483,7 +483,7 @@ cdef class CategoryObject(SageObject): """ This is used by the variable names context manager. - TEST: + TESTS: In an old version, it was impossible to temporarily change the names if no names were previously assigned. But if one From 80eb4690bd57eb744218ea7f8805d34c8f036156 Mon Sep 17 00:00:00 2001 From: Karim Van Aelst Date: Fri, 7 Jul 2017 11:34:08 +0200 Subject: [PATCH 056/184] doctests failures reported in last review corrected, method __call__ now returns a manifold point, syntax of error messages corrected, print_function imported, implementation relative to arguments is_identity and is_isomorphism removed (in particular, identity map not implemented by method one of parent classes), documentation completed as suggested in last review, slight corrections in method 'solve' --- .../differentiable/integrated_curve.py | 612 +++++++++++------- src/sage/manifolds/differentiable/manifold.py | 29 +- .../differentiable/manifold_homset.py | 339 +++++----- 3 files changed, 568 insertions(+), 412 deletions(-) diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index 0e065de7c44..83a593e1006 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -1,5 +1,6 @@ +# -*- coding: utf-8 -*- r""" -Integrated Curves in Manifolds +Integrated Curves and Geodesics in Manifolds Given a differentiable manifold `M`, an *integrated curve* curve in `M` is a differentiable curve constructed as a solution to a system of @@ -9,6 +10,44 @@ classes :class:`IntegratedAutoparallelCurve` and :class:`IntegratedGeodesic` inherit. +.. RUBRIC:: Example: A geodesic in hyperbolic Poincaré half-plane + +First declare a chart over the Poincaré half-plane:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart('x y:(0,+oo)') + +Then declare the hyperbolic Poicaré metric:: + + sage: g = M.metric('g') + sage: g[0,0], g[1,1] = 1/y^2, 1/y^2 + sage: g.display() + g = y^(-2) dx*dx + y^(-2) dy*dy + +Pick an initial point and an initial tangent vector:: + + sage: p = M((0,1), name='p') + sage: v = M.tangent_space(p)((1,3/2)) + sage: v.display() + d/dx + 3/2 d/dy + +Declare a geodesic with such initial conditions, denoting ``t`` the +corresponding affine parameter:: + + sage: t = var('t') + sage: c = M.integrated_geodesic(g, (t, 0, 10), v, name='c') + +Numerically integrate the geodesic:: + + sage: sol = c.solve() + +Plot the geodesic after interpolating the solution ``sol``, since it is +required to plot:: + + sage: interp = c.interpolate() + sage: graph = c.plot_integrated() + sage: graph.show() + AUTHORS: - Karim Van Aelst (2017): initial version @@ -24,6 +63,7 @@ # http://www.gnu.org/licenses/ #*********************************************************************** +from __future__ import print_function from sage.symbolic.expression import Expression from sage.rings.infinity import Infinity from sage.calculus.desolvers import desolve_system_rk4 @@ -67,12 +107,6 @@ class IntegratedCurve(DifferentiableCurve): - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - - ``is_isomorphism`` -- (default: ``False``) determines whether the - constructed object is a diffeomorphism; if set to ``True``, - then `M` must have dimension one - - ``is_identity`` -- (default: ``False``) determines whether the - constructed object is the identity map; if set to ``True``, - then `M` must coincide with the domain of the curve EXAMPLE: @@ -119,7 +153,7 @@ class IntegratedCurve(DifferentiableCurve): ....: verbose=True) The curve was correctly set. Parameters appearing in the differential system defining the - curve are [q, B_0, m, T, L]. + curve are [B_0, L, T, q, m]. sage: c Integrated curve c in the 3-dimensional differentiable manifold M @@ -170,10 +204,13 @@ class IntegratedCurve(DifferentiableCurve): Such an interpolation is required to evaluate the curve and the vector tangent to the curve for any value of the curve parameter:: - sage: c(1.9, verbose=True) + sage: p = c(1.9, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'interp 1' by default... - [1.3776707219621374, -0.9000776970132945, 1.9] + sage: p + Point on the 3-dimensional differentiable manifold M + sage: p.coordinates() + (1.3776707219621374, -0.9000776970132945, 1.9) sage: v = c.tangent_vector_eval_at(4.3, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'interp 1' by default... @@ -193,9 +230,9 @@ class IntegratedCurve(DifferentiableCurve): ....: plot_points_tangent=10, scale=0.5, ....: color='blue', color_tangent='red', ....: verbose=True) - A tiny final offset equal to the value of 'end_point_offset[1]' - (= 0.001) was introduced in order to safely compute the last - tangent vector from the interpolation. + A tiny final offset equal to 0.000251256281407035 was introduced + for the last point in order to safely compute it from the + interpolation. sage: c_plot_2d_1.show() .. PLOT:: @@ -279,8 +316,7 @@ class IntegratedCurve(DifferentiableCurve): def __init__(self, parent, equations_rhs, velocities, curve_parameter, initial_tangent_vector, chart=None, - name=None, latex_name=None, - is_isomorphism=False, is_identity=False, verbose=False): + name=None, latex_name=None, verbose=False): r""" Construct a curve defined by a system of second order differential equations in the coordinate functions. @@ -300,29 +336,29 @@ def __init__(self, parent, equations_rhs, velocities, ....: name='c') Traceback (most recent call last): ... - ValueError: Number of equations should equal codomain - dimension. + ValueError: number of equations should equal codomain + dimension sage: c = M.integrated_curve(eqns, D + [x1], (t, 0, 5), v, ....: name='c') Traceback (most recent call last): ... - ValueError: Number of velocities should equal codomain - dimension. + ValueError: number of velocities should equal codomain + dimension sage: c = M.integrated_curve(eqns, D,(t,-oo,5), v, name='c') Traceback (most recent call last): ... - ValueError: Both boundaries of the interval defining the - domain of a Homset of integrated curves need to be finite. + ValueError: both boundaries of the interval defining the + domain of a Homset of integrated curves need to be finite sage: c = M.integrated_curve(eqns, D, (t,0,5), x1, name='c') Traceback (most recent call last): ... - TypeError: x1 should be a tangent vector. + TypeError: x1 should be a tangent vector sage: c = M.integrated_curve(eqns, D, (x1,0,5), v, name='c') Traceback (most recent call last): ... ValueError: x1 should not be used as the curve parameter - since it also denotes a coordinate or a velocity. + since it also denotes a coordinate or a velocity sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c'); c Integrated curve c in the 3-dimensional differentiable manifold M @@ -336,8 +372,7 @@ def __init__(self, parent, equations_rhs, velocities, # start with parent class method to initialize the four last # arguments: DifferentiableCurve.__init__(self, parent, name=name, - latex_name=latex_name, is_isomorphism=is_isomorphism, - is_identity=is_identity) # (coord_expression=None) + latex_name=latex_name) # check argument 'parent': 't_min' and 't_max' below are only # allowed to be either expressions of finite real values: @@ -345,39 +380,16 @@ def __init__(self, parent, equations_rhs, velocities, t_min = domain.lower_bound() t_max = domain.upper_bound() if t_min == -Infinity or t_max == +Infinity: - raise ValueError("Both boundaries of the interval " + - "need to be finite.") + raise ValueError("both boundaries of the interval " + + "need to be finite") codomain = self.codomain() - if self._is_identity: # init of DifferentiableCurve should have - # set this attribute to 'True' if argument is_identity of this - # __init__ was set to 'True'. - # In this case, init of DifferentiableCurve has checked that the - # domain and codomain coincide. - # Then, at this stage, they necessarily are a certain finite - # real interval. - # Arguments 'equations_rhs', 'initial_tangent_vector' and - # 'chart' are then modified whatever their values were in order - # to construct an integrated version of the identity map on the - # finite interval. - equations_rhs = [0] - chart = codomain.default_chart() - p = codomain.point([t_min + (t_max-t_min)/10**(6)])#slightly - # shifting the initial point inside the interval is required - # since the interval is open - Tp = codomain.tangent_space(p) - initial_tangent_vector = Tp([1 - 10**(-6)]) # taking a - # derivative slightly less than 1 is required for the last - # point to be inside the open interval as well (this - # obviously damages the approximation of the identity - # provided by 'self') - # check argument 'equations_rhs': dim = codomain.dim() if len(equations_rhs) != dim: - raise ValueError("Number of equations should equal " + - "codomain dimension.") + raise ValueError("number of equations should equal " + + "codomain dimension") # check the chart: if chart is not None: @@ -389,28 +401,28 @@ def __init__(self, parent, equations_rhs, velocities, # check argument 'velocities': if len(velocities) != dim: - raise ValueError("Number of velocities should equal " + - "codomain dimension.") + raise ValueError("number of velocities should equal " + + "codomain dimension") # in particular, check that no velocity coincides with a # coordinate: for vel in velocities: if vel in chart[:]: str_error = "{} should not be used as a ".format(vel) str_error += "velocity since it also denotes " - str_error += "a coordinate." + str_error += "a coordinate" raise ValueError(str_error) # check argument 'curve_parameter': if not isinstance(curve_parameter, Expression): raise TypeError("{} should be ".format(curve_parameter) + - "a symbolic expression.") + "a symbolic expression") # in particular, check that it does not coincide with a # coordinate or a velocity: coords_vels = list(chart[:]) + list(velocities) if curve_parameter in coords_vels: str_error = "{} should not be used ".format(curve_parameter) str_error += "as the curve parameter since it also denotes " - str_error += "a coordinate or a velocity." + str_error += "a coordinate or a velocity" raise ValueError(str_error) # the various algorithms called in 'solve' method are in charge # of raising errors about possibly remaining problems regarding @@ -419,7 +431,7 @@ def __init__(self, parent, equations_rhs, velocities, # check argument 'initial_tangent_vector': if not isinstance(initial_tangent_vector, TangentVector): raise TypeError("{} ".format(initial_tangent_vector) + - "should be a tangent vector.") + "should be a tangent vector") # in particular, check that its base point sits in the domain # of the chart (if its coordinates are explicitly given): initial_pt = initial_tangent_vector.parent().base_point() @@ -433,8 +445,8 @@ def __init__(self, parent, equations_rhs, velocities, coord_min = chart.coord_bounds(i+i0)[0][0] coord_max = chart.coord_bounds(i+i0)[1][0] if coord_value <= coord_min or coord_value >= coord_max: - raise ValueError("Initial point should be in the " + - "domain of the chart.") + raise ValueError("initial point should be in the " + + "domain of the chart") # prepare attribute '_parameters': announced_variables = set(coords_vels + [curve_parameter]) @@ -480,7 +492,7 @@ def __init__(self, parent, equations_rhs, velocities, str_error = "{} should not be used ".format(param) str_error += "as a parameter since it also denotes " str_error += "a coordinate, a velocity or the " - str_error += "curve parameter." + str_error += "curve parameter" raise ValueError(str_error) # define all attributes @@ -561,7 +573,7 @@ def __reduce__(self): sage: v = Tp((1,0,1)) sage: c = M.integrated_curve(eqns, D, (t,0,5), v, name='c') sage: c.__reduce__() - (, + (, (Set of Morphisms from Real interval (0, 5) to 3-dimensional differentiable manifold M in Category of homsets of subobjects of sets and topological spaces which @@ -575,9 +587,7 @@ def __reduce__(self): differentiable manifold M, Chart (M, (x1, x2, x3)), 'c', - 'c', - False, - False)) + 'c')) Test of pickling:: @@ -590,8 +600,7 @@ def __reduce__(self): return (type(self), (self.parent(), self._equations_rhs, self._velocities, self._curve_parameter, self._initial_tangent_vector, self._chart, - self._name, self._latex_name, self._is_isomorphism, - self._is_identity)) + self._name, self._latex_name)) def system(self, verbose=False): r""" @@ -897,14 +906,14 @@ def solve(self, step=None, method=None, solution_key=None, sage: sol = c.solve(parameters_values={m:1, q:1, L:10, T:1}) Traceback (most recent call last): ... - ValueError: Numerical values should be provided for each of - the parameters [q, B_0, m, T, L]. + ValueError: numerical values should be provided for each of + the parameters [B_0, L, T, q, m] sage: sol = c.solve(method='my method', ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}) Traceback (most recent call last): ... - ValueError: No available method of integration referred to - as 'my method'. + ValueError: no available method of integration referred to + as 'my method' sage: sol = c.solve( ....: parameters_values={B_0:1, m:1, q:1, L:10, T:1}, ....: verbose=True) @@ -945,7 +954,22 @@ def solve(self, step=None, method=None, solution_key=None, t_min = self.domain().lower_bound() t_max = self.domain().upper_bound() - eqns_rhs = self._equations_rhs + eqns_num = [eq for eq in self._equations_rhs] + # 'self._equations_rhs' needs not to be modified ever, because we + # want to keep track of the most general form of the equations + # defining self, since those may contain parameters (which, for + # instance, we want to display as their original expressions + # when calling 'system' method with option 'verbose', and not + # substituted with some numerical values). + # This is why 'eqns_num' is declared: it will contain copies of + # the equations of 'self._equations_rhs' in which the parameters + # will be substituted with numerical values. + # It was then important to declare it as above, in order to make + # independent copies of each equations of 'self._equations_rhs', + # rather than declaring 'eqns_num = self._equations_rhs', in which + # case making substitutions in 'eqns_num' would have meant making + # the same substitutions in the original equations of + # 'self._equations_rhs' v0 = self._initial_tangent_vector chart = self._chart @@ -966,10 +990,10 @@ def solve(self, step=None, method=None, solution_key=None, if len(self._parameters) != 0: if parameters_values is None or len(parameters_values)!=len(self._parameters): - raise ValueError("Numerical values should be " + + raise ValueError("numerical values should be " + "provided for each of the " + "parameters " - "{}.".format(sorted(self._parameters))) + "{}".format(sorted(self._parameters))) for key in parameters_values: parameters_values[key]=numerical_approx(parameters_values[key])#gets # numerical values in case some parameters values @@ -979,21 +1003,21 @@ def solve(self, step=None, method=None, solution_key=None, if isinstance(t_min, Expression): t_min = parameters_values[t_min] if t_min == -Infinity or t_min == +Infinity: - raise ValueError("Both boundaries of the " + - "interval need to be finite.") + raise ValueError("both boundaries of the " + + "interval need to be finite") if isinstance(t_max, Expression): t_max = parameters_values[t_max] if t_max == -Infinity or t_max == +Infinity: - raise ValueError("Both boundaries of the " + - "interval need to be finite.") + raise ValueError("both boundaries of the " + + "interval need to be finite") for i in range(dim): - if isinstance(eqns_rhs[i], Expression): # some right + if isinstance(eqns_num[i], Expression): # some right # hand sides might merely be real numbers and not # expressions, so that they do not contain any variable, - # and method 'variables' could not be called on them - eqns_rhs[i]=eqns_rhs[i].substitute(parameters_values) + # and hence no substitution is required + eqns_num[i]=eqns_num[i].substitute(parameters_values) for i in range(dim): if isinstance(initial_pt_coords[i],Expression): @@ -1011,11 +1035,11 @@ def solve(self, step=None, method=None, solution_key=None, t_max = numerical_approx(t_max) for i in range(dim): - if not isinstance(eqns_rhs[i], Expression): # in case of a + if not isinstance(eqns_num[i], Expression): # in case of a # right hand side that is not an Expression (and then is a # number), it is needed to be converted to an Expression # since some solvers called below require only expressions - eqns_rhs[i] = SR(eqns_rhs[i]) + eqns_num[i] = SR(eqns_num[i]) if step is None: step = (t_max - t_min)/100 @@ -1032,14 +1056,14 @@ def solve(self, step=None, method=None, solution_key=None, # RealNumber if not chart.valid_coordinates(*initial_pt_coords): - raise ValueError("Initial point should be in the " + - "domain of the chart.") + raise ValueError("initial point should be in the " + + "domain of the chart") ode_solver_methods = ["rk2","rk4","rkf45","rkck","rk8pd"] ode_solver_methods+= ["rk2imp","rk4imp","gear1","gear2","bsimp"] if method == 'rk4_maxima': - des = self._velocities + eqns_rhs + des = self._velocities + eqns_num dvars = list(chart[:]) + self._velocities ics = [t_min] + initial_pt_coords + initial_tgt_vec_comps @@ -1048,8 +1072,33 @@ def solve(self, step=None, method=None, solution_key=None, ics=ics, end_points=[t_min, t_max], step=step) + + # Let q = (t_max-t_min)/step; then, the value of the + # curve parameter of the q+1-th point computed is very close + # to 't_max'. Yet, due to numerical rounding, it is very + # often a bit smaller than 't_max'. And it seems that, if + # the solver considers this value not to be close enough to + # 't_max', then the solver computes one more point, + # evaluated for a curve parameter exactly equal to 't_max'. + # Therefore, the difference between the curve parameter + # corresponding to this last point and that corresponding + # to the previous one is strictly less than 'step'. If this + # difference is too small (that is, if the solver considered + # that it did not reach 't_max', and hence computed one more + # point, although it was already very close to 't_max'), + # problems arise when using an interpolation of this + # solution (such as getting points with coordinates 'nan'). + # As a result, we choose to remove the last point of a + # solution when it is a point that was added by the solver + # and threatens to be too close to the previous one + # (arbitrarily, we consider two points to be too close if + # their curve parameters are separated by less than half a + # step). + if len(sol) > 1 and abs(sol[-1][0] - sol[-2][0]) < 0.5*step: + del sol[-1] + elif method == "ode_int": - des = self._velocities + eqns_rhs + des = self._velocities + eqns_num ics = initial_pt_coords + initial_tgt_vec_comps times = srange(t_min, t_max, step, include_endpoint=True) dvars = list(chart[:]) + self._velocities @@ -1070,7 +1119,7 @@ def solve(self, step=None, method=None, solution_key=None, if T is None: def system(t,y): - syst = self._velocities + eqns_rhs + syst = self._velocities + eqns_num par = self._curve_parameter for i in range(dim): vel = self._velocities[i] @@ -1113,8 +1162,8 @@ def jacobian(t,y): # '[:]' on 'chart' to avoid problems due # to non zero starting index (i0) vel = self._velocities[j] - AUX = eqns_rhs[i].derivative(coord) - AUX2 = eqns_rhs[i].derivative(vel) + AUX = eqns_num[i].derivative(coord) + AUX2 = eqns_num[i].derivative(vel) AUX = AUX.substitute({par:t}) AUX2 = AUX2.substitute({par:t}) for k in range(dim): @@ -1134,7 +1183,7 @@ def jacobian(t,y): last_semi_row_coords = [0 for j in range(dim)] last_semi_row_vels = [] for j in range(dim): - AUX3 = eqns_rhs[j].derivative(par) + AUX3 = eqns_num[j].derivative(par) AUX3 = AUX3.substitute({par:t}) for m in range(dim): coordin = chart[:][m] # important to use @@ -1165,8 +1214,8 @@ def jacobian(t,y): # all methods else: - raise ValueError("No available method of integration " + - "referred to as '{}'.".format(method)) + raise ValueError("no available method of integration " + + "referred to as '{}'".format(method)) # eventually, extract the time and corresponding coordinate # values from each point of the solution computed (thus removing @@ -1186,13 +1235,13 @@ def jacobian(t,y): n += 1 if n < N: - raise ValueError("The {}th point ".format(n) + + raise ValueError("the {}th point ".format(n) + "of the numerical solution (obtained at " + "time {}) is out ".format(t_min + n*step) + - "of the chart domain. A curve with a " + + "of the chart domain; a curve with a " + "smaller maximal value of the curve " + "parameter, or a smaller initial tangent "+ - "vector might be considered.") + "vector might be considered") else: self._solutions[solution_key] = coords_sol if verbose: @@ -1261,9 +1310,9 @@ def solution(self, solution_key=None, verbose=False): "with the key '{}' ".format(solution_key) + "by default...") elif solution_key not in self._solutions: - raise ValueError("No existing key " + + raise ValueError("no existing key " + "'{}' ".format(solution_key) + - "referring to any numerical solution.") + "referring to any numerical solution") return self._solutions[solution_key] @@ -1314,14 +1363,14 @@ def interpolate(self, solution_key=None, method=None, sage: interp = c.interpolate(solution_key='my solution') Traceback (most recent call last): ... - ValueError: No existing key 'my solution' referring to any - numerical solution. + ValueError: no existing key 'my solution' referring to any + numerical solution sage: interp = c.interpolate(solution_key='sol_T1', ....: method='my method') Traceback (most recent call last): ... - ValueError: No available method of interpolation referred to - as 'my method'. + ValueError: no available method of interpolation referred to + as 'my method' sage: interp = c.interpolate(method='cubic spline', ....: solution_key='sol_T1', ....: interpolation_key='interp_T1', @@ -1354,9 +1403,9 @@ def interpolate(self, solution_key=None, method=None, "'{}' ".format(solution_key) + "by default...") elif solution_key not in self._solutions: - raise ValueError("No existing key " + + raise ValueError("no existing key " + "'{}' ".format(solution_key) + - "referring to any numerical solution.") + "referring to any numerical solution") if method is None: method = 'cubic spline' @@ -1381,8 +1430,8 @@ def interpolate(self, solution_key=None, method=None, coordinate_curve += [[point[0], point[i+1]]] self._interpolations[interpolation_key]+=[Spline(coordinate_curve)] else: - raise ValueError("No available method of interpolation " + - "referred to as '{}'.".format(method)) + raise ValueError("no available method of interpolation " + + "referred to as '{}'".format(method)) if verbose: print("Interpolation completed and associated with the " + @@ -1431,8 +1480,8 @@ def interpolation(self, interpolation_key=None, verbose=False): sage: c.interpolation(interpolation_key='my interp') Traceback (most recent call last): ... - ValueError: No existing key 'my interp' referring to any - interpolation. + ValueError: no existing key 'my interp' referring to any + interpolation sage: default_interp = c.interpolation(verbose=True) Returning the interpolation associated with the key 'interp_T1' by default... @@ -1455,9 +1504,9 @@ def interpolation(self, interpolation_key=None, verbose=False): "the key '{}' ".format(interpolation_key) + "by default...") elif interpolation_key not in self._interpolations: - raise ValueError("No existing key " + + raise ValueError("no existing key " + "'{}' ".format(interpolation_key) + - "referring to any interpolation.") + "referring to any interpolation") return self._interpolations[interpolation_key] @@ -1478,7 +1527,8 @@ def __call__(self, t, interpolation_key=None, OUTPUT: - - list of the numerical coordinates of the point + - :class:`~sage.manifolds.point.ManifoldPoint` point on a + manifold (codomain) with numerical coordinates TESTS:: @@ -1502,14 +1552,13 @@ def __call__(self, t, interpolation_key=None, sage: c(1.1, interpolation_key='my interp') Traceback (most recent call last): ... - ValueError: No existing key 'my interp' referring to any - interpolation. - sage: c(1.1, verbose=True) + ValueError: no existing key 'my interp' referring to any + interpolation + sage: p = c(1.1, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'interp_T1' by default... - [1.060743343394347, -0.2153835404373033, 1.1] - sage: pt = c(1.1); pt - [1.060743343394347, -0.2153835404373033, 1.1] + sage: p.coordinates() + (1.060743343394347, -0.2153835404373033, 1.1) """ @@ -1524,9 +1573,9 @@ def __call__(self, t, interpolation_key=None, "interpolation associated with the key " + "'{}' by default...".format(interpolation_key)) elif interpolation_key not in self._interpolations: - raise ValueError("No existing key " + + raise ValueError("no existing key " + "'{}' ".format(interpolation_key) + - "referring to any interpolation.") + "referring to any interpolation") interpolation = self._interpolations[interpolation_key] @@ -1535,9 +1584,10 @@ def __call__(self, t, interpolation_key=None, # of the Spline class interpolated_coordinates=[coordinate_curve_spline(t) for coordinate_curve_spline in interpolation] - return interpolated_coordinates + return self.codomain().point(coords=interpolated_coordinates, + chart=self._chart) - raise TypeError("Unexpected type of interpolation object.") + raise TypeError("unexpected type of interpolation object") def tangent_vector_eval_at(self, t, interpolation_key=None, verbose=False): @@ -1585,8 +1635,8 @@ def tangent_vector_eval_at(self, t, ....: interpolation_key='my interp') Traceback (most recent call last): ... - ValueError: No existing key 'my interp' referring to any - interpolation. + ValueError: no existing key 'my interp' referring to any + interpolation sage: tg_vec = c.tangent_vector_eval_at(1.22, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'interp_T1' by default... @@ -1613,9 +1663,9 @@ def tangent_vector_eval_at(self, t, "interpolation associated with the key " + "'{}' by default...".format(interpolation_key)) elif interpolation_key not in self._interpolations: - raise ValueError("No existing key " + + raise ValueError("no existing key " + "'{}' ".format(interpolation_key) + - "referring to any interpolation.") + "referring to any interpolation") interpolation = self._interpolations[interpolation_key] @@ -1636,10 +1686,10 @@ def tangent_vector_eval_at(self, t, v = Tp(evaluated_tgt_vec_comp, basis=basis) return v - raise TypeError("Unexpected type of interpolation object.") + raise TypeError("unexpected type of interpolation object") - @options(thickness=1, width_tangent=1, plot_points=75, - aspect_ratio='automatic', plot_points_tangent=10, scale=1) + @options(thickness=1, plot_points=75, aspect_ratio='automatic', + plot_points_tangent=10, width_tangent=1, scale=1) def plot_integrated(self, chart=None, ambient_coords=None, mapping=None, prange=None, interpolation_key=None, include_end_point=(True, True), @@ -1663,6 +1713,16 @@ def plot_integrated(self, chart=None, ambient_coords=None, is chosen if none is provided - ``verbose`` -- (default: ``False``) prints information about the interpolation object used and the plotting in progress + - ``display_tangent`` -- (default: ``False``) determines whether + some tangent vectors should also be plotted + - ``color_tangent`` -- (default: ``blue``) color of the tangent + vectors when these are plotted + - ``plot_points_tangent`` -- (default: 75) number of tangent + vectors to display when these are plotted + - ``width_tangent`` -- (default: 1) sets the width of the arrows + representing the tangent vectors + - ``scale`` -- (default: 1) scale applied to the tangent vectors + before displaying them EXAMPLE: @@ -1689,10 +1749,9 @@ def plot_integrated(self, chart=None, ambient_coords=None, ....: verbose=True) Plotting from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... - A tiny final offset equal to the value of - 'end_point_offset[1]' (= 0.001) was introduced in order to - safely compute the last tangent vector from the - interpolation. + A tiny final offset equal to 0.000301507537688442 was + introduced for the last point in order to safely compute it + from the interpolation. sage: c_plot_2d.show() .. PLOT:: @@ -1740,9 +1799,9 @@ def plot_integrated(self, chart=None, ambient_coords=None, "with the key '{}' ".format(interpolation_key) + "by default...") elif interpolation_key not in self._interpolations: - raise ValueError("No existing key " + + raise ValueError("no existing key " + "'{}' ".format(interpolation_key) + - "referring to any interpolation.") + "referring to any interpolation") interpolation = self._interpolations[interpolation_key] @@ -1793,7 +1852,12 @@ def plot_integrated(self, chart=None, ambient_coords=None, # interpolation # param_min = interpolation[0][0][0] - param_max = interpolation[0][-1][0] + param_max = interpolation[0][-1][0] # these two lines are the + # only general way to get the maximal parameter range since, at + # this point, there is no clue about the solution from which + # 'interpolation' was build, and it would be an obvious error to + # declare param_min=self.domain().lower_bound() for instance, + # since this might be an expression if prange is None: prange = (param_min, param_max) @@ -1806,9 +1870,9 @@ def plot_integrated(self, chart=None, ambient_coords=None, else: p = prange #'p' declared only for the line below to be shorter if p[0]param_max or p[1]param_max: - raise ValueError("Parameter range should be a " + + raise ValueError("parameter range should be a " + "subinterval of the curve domain " + - "({}).".format(self.domain())) + "({})".format(self.domain())) tmin = numerical_approx(prange[0]) tmax = numerical_approx(prange[1]) @@ -1832,7 +1896,48 @@ def plot_integrated(self, chart=None, ambient_coords=None, t = tmin for k in range(plot_points): + if k==0 and t < param_min: # this might happen for + # the first point (i.e. k = 0) when prange[0], and + # hence tmin, should equal param_min; but mere + # numerical rounding coming from having taken + # tmin = numerical_approx(prange[0]) might + # raise errors from trying to evaluate the + # interpolation at a time smaller than + # self.domain.lower_bound(), hence the line below + t = param_min + 0.01*dt # add 1% of the step to + # compute even more safely the first point + if verbose: + print("A tiny initial offset equal to " + + "{} ".format(0.01*dt)+ + "was introduced for the first point "+ + "only, in order to safely compute " + + "it from the interpolation.") + + if k==plot_points-1 and t > param_max: # this might + # happen for the last point (i.e. k = plot_points-1) + # when prange[1], and hence tmax, should equal + # param_max; but mere numerical rounding coming from + # having taken tmax = numerical_approx(prange[1) + # might raise errors from trying to evaluate the + # interpolation at a time greater than + # self.domain.upper_bound(), hence the line below + t = param_max - 0.01*dt # subtract 1% of the + # step to compute even more safely the last + # point + if verbose: + print("A tiny final offset equal to " + + "{} ".format(0.01*dt)+ + "was introduced for the last point "+ + "in order to safely compute " + + "it from the interpolation.") + plot_curve.append([interpolation[j-i0](t) for j in ind_pc]) + + if k==0 and t > tmin: # in case an initial offset + # was earlier added to 'tmin' in order to avoid + # errors, it is now needed to cancel this offset for + # the next steps + t=tmin t += dt if display_tangent: @@ -1844,23 +1949,47 @@ def plot_integrated(self, chart=None, ambient_coords=None, plot_points_tangent=kwds.pop('plot_points_tangent') width_tangent = kwds.pop('width_tangent') - if not tmax < param_max: - tmax=numerical_approx(tmax- end_point_offset[1]) - if verbose: - print("A tiny final offset equal to " + - "the value of " + - "'end_point_offset[1]' " + - "(= {}) ".format(end_point_offset[1])+ - "was introduced " + - "in order to safely compute the " + - "last tangent vector from the " + - "interpolation.") - plot_vectors = Graphics() dt = (tmax - tmin) / (plot_points_tangent - 1) t = tmin for k in range(plot_points_tangent): + if k==0 and t < param_min: # this might happen for + # the first point (i.e. k = 0) when prange[0], and + # hence tmin, should equal param_min; but mere + # numerical rounding coming from having taken + # tmin = numerical_approx(prange[0]) might + # raise errors from trying to evaluate the + # interpolation at a time smaller than + # self.domain.lower_bound(), hence the line below + t = param_min + 0.01*dt # add 1% of the step to + # compute even more safely the first point + if verbose: + print("A tiny initial offset equal to " + + "{} ".format(0.01*dt)+ + "was introduced for the first point "+ + "only, in order to safely compute " + + "it from the interpolation.") + + if k==plot_points_tangent-1 and t > param_max: + # this might happen for the last point + # (i.e. k = plot_points_tangent-1) when + # prange[1], and hence tmax, should equal + # param_max; but mere numerical rounding coming from + # having taken tmax = numerical_approx(prange[1) + # might raise errors from trying to evaluate the + # interpolation at a time greater than + # self.domain.upper_bound(), hence the line below + t = param_max - 0.01*dt # subtract 1% of the + # step to compute even more safely the last + # point + if verbose: + print("A tiny final offset equal to " + + "{} ".format(0.01*dt)+ + "was introduced for the last point "+ + "in order to safely compute " + + "it from the interpolation.") + # interpolated ambient coordinates: xp = [interpolation[j-i0](t) for j in ind_pc] @@ -1884,6 +2013,12 @@ def plot_integrated(self, chart=None, ambient_coords=None, coord_head, color=color_tangent, width=width_tangent) + + if k==0 and t > tmin: # in case an initial offset + # was earlier added to 'tmin' in order to avoid + # errors, it is now needed to cancel this offset for + # the next steps + t=tmin t += dt return plot_vectors+DifferentiableCurve._graphics(self, @@ -1899,7 +2034,7 @@ def plot_integrated(self, chart=None, ambient_coords=None, aspect_ratio=aspect_ratio, color=color, style=style, label_axes=label_axes) - raise TypeError("Unexpected type of interpolation object.") + raise TypeError("unexpected type of interpolation object") else: # # The coordinate expressions of the mapping and the @@ -1921,7 +2056,7 @@ def plot_integrated(self, chart=None, ambient_coords=None, required_coords=required_coords.union(AUX2) break else: - raise ValueError("No expression has been found for " + + raise ValueError("no expression has been found for " + "{} in terms of {}".format(self,chart)) if isinstance(interpolation[0], Spline): # partial test, in @@ -1937,6 +2072,41 @@ def plot_integrated(self, chart=None, ambient_coords=None, required_coords_values = {} for k in range(plot_points): + if k==0 and t < param_min: # this might happen for + # the first point (i.e. k = 0) when prange[0], and + # hence tmin, should equal param_min; but mere + # numerical rounding coming from having taken + # tmin = numerical_approx(prange[0]) might + # raise errors from trying to evaluate the + # interpolation at a time smaller than + # self.domain.lower_bound(), hence the line below + t = param_min + 0.01*dt # add 1% of the step to + # compute even more safely the first point + if verbose: + print("A tiny initial offset equal to " + + "{} ".format(0.01*dt)+ + "was introduced for the first point "+ + "only, in order to safely compute " + + "it from the interpolation.") + + if k==plot_points-1 and t > param_max: # this might + # happen for the last point (i.e. k = plot_points-1) + # when prange[1], and hence tmax, should equal + # param_max; but mere numerical rounding coming from + # having taken tmax = numerical_approx(prange[1) + # might raise errors from trying to evaluate the + # interpolation at a time greater than + # self.domain.upper_bound(), hence the line below + t = param_max - 0.01*dt # subtract 1% of the + # step to compute even more safely the last + # point + if verbose: + print("A tiny final offset equal to " + + "{} ".format(0.01*dt)+ + "was introduced for the last point "+ + "in order to safely compute " + + "it from the interpolation.") + for coord in required_coords: i = self._chart[:].index(coord) required_coords_values[coord]=interpolation[i](t) @@ -1951,6 +2121,13 @@ def plot_integrated(self, chart=None, ambient_coords=None, xp+=[numerical_approx(AUX)] plot_curve.append(xp) + + if k==0 and t > tmin: # in case an initial offset + # was earlier added to 'tmin' in order to avoid + # errors, it is now needed to cancel this offset for + # the next steps + t=tmin + t += dt if display_tangent: @@ -1962,18 +2139,6 @@ def plot_integrated(self, chart=None, ambient_coords=None, plot_points_tangent=kwds.pop('plot_points_tangent') width_tangent = kwds.pop('width_tangent') - if not tmax < param_max: - tmax=numerical_approx(tmax- end_point_offset[1]) - if verbose: - print("A tiny final offset equal to " + - "the value of " + - "'end_point_offset[1]' " + - "(= {}) ".format(end_point_offset[1])+ - "was introduced " + - "in order to safely compute the " + - "last tangent vector from the " + - "interpolation.") - plot_vectors = Graphics() dt = (tmax - tmin) / (plot_points_tangent - 1) t = tmin @@ -1986,6 +2151,42 @@ def plot_integrated(self, chart=None, ambient_coords=None, Dpc_Dcoord[pc][coord]=transf[pc].derivative(coord) for k in range(plot_points_tangent): + if k==0 and t < param_min: # this might happen for + # the first point (i.e. k = 0) when prange[0], and + # hence tmin, should equal param_min; but mere + # numerical rounding coming from having taken + # tmin = numerical_approx(prange[0]) might + # raise errors from trying to evaluate the + # interpolation at a time smaller than + # self.domain.lower_bound(), hence the line below + t = param_min + 0.01*dt # add 1% of the step to + # compute even more safely the first point + if verbose: + print("A tiny initial offset equal to " + + "{} ".format(0.01*dt)+ + "was introduced for the first point "+ + "only, in order to safely compute " + + "it from the interpolation.") + + if k==plot_points_tangent-1 and t > param_max: + # this might happen for the last point + # (i.e. k = plot_points_tangent-1) when + # when prange[1], and hence tmax, should equal + # param_max; but mere numerical rounding coming from + # having taken tmax = numerical_approx(prange[1) + # might raise errors from trying to evaluate the + # interpolation at a time greater than + # self.domain.upper_bound(), hence the line below + t = param_max - 0.01*dt # subtract 1% of the + # step to compute even more safely the last + # point + if verbose: + print("A tiny final offset equal to " + + "{} ".format(0.01*dt)+ + "was introduced for the last point "+ + "in order to safely compute " + + "it from the interpolation.") + for coord in required_coords: i = self._chart[:].index(coord) AUX = interpolation[i] # 'AUX' only used @@ -2027,6 +2228,13 @@ def plot_integrated(self, chart=None, ambient_coords=None, coord_head, color=color_tangent, width=width_tangent) + + if k==0 and t > tmin: # in case an initial offset + # was earlier added to 'tmin' in order to avoid + # errors, it is now needed to cancel this offset for + # the next steps + t=tmin + t += dt return plot_vectors+DifferentiableCurve._graphics(self, @@ -2042,7 +2250,7 @@ def plot_integrated(self, chart=None, ambient_coords=None, aspect_ratio=aspect_ratio, color=color, style=style, label_axes=label_axes) - raise TypeError("Unexpected type of interpolation object.") + raise TypeError("unexpected type of interpolation object") class IntegratedAutoparallelCurve(IntegratedCurve): r""" @@ -2070,12 +2278,6 @@ class IntegratedAutoparallelCurve(IntegratedCurve): - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - - ``is_isomorphism`` -- (default: ``False``) determines whether the - constructed object is a diffeomorphism; if set to ``True``, - then `M` must have dimension one - - ``is_identity`` -- (default: ``False``) determines whether the - constructed object is the identity map; if set to ``True``, - then `M` must coincide with the domain of the curve EXAMPLE: @@ -2378,7 +2580,8 @@ class IntegratedAutoparallelCurve(IntegratedCurve): parameters_values={tmin:0,tmax:2,th0:pi/4,ph0:0.1,v_th0:1,v_ph0:8}) interp = c.interpolate(solution_key='sol-angle', interpolation_key='interp-angle') - graph2D_mercator_angle_curve=c.plot_integrated(interpolation_key='interp-angle', + graph2D_mercator_angle_curve=c.plot_integrated( + interpolation_key='interp-angle', chart=mercator, thickness=1, display_tangent=True, scale=0.2, width_tangent=0.2) sphinx_plot(graph2D_mercator_angle_curve) @@ -2577,8 +2780,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): def __init__(self, parent, affine_connection, curve_parameter, initial_tangent_vector, chart=None, name=None, - latex_name=None, is_isomorphism=False, - is_identity=False, verbose=False): + latex_name=None, verbose=False): r""" Construct an autoparallel curve w.r.t. the given affine connection with the given initial tangent vector. @@ -2608,30 +2810,26 @@ def __init__(self, parent, affine_connection, curve_parameter, coordinate_functions = chart[:] velocities = chart.symbolic_velocities() - if is_identity: - equations_rhs = None - else: - dim = parent.codomain().dim() - i0 = parent.codomain().start_index() - equations_rhs = [] - gamma = affine_connection.coef() + dim = parent.codomain().dim() + i0 = parent.codomain().start_index() + equations_rhs = [] + + gamma = affine_connection.coef() - for alpha in range(dim): - rhs = 0 - for mu in range(dim): - for nu in range(dim): - AUX = velocities[mu] * velocities[nu] - rhs-= gamma[alpha+i0, mu+i0, nu+i0].expr() * AUX - # 'AUX' only used for the line above to be shorter - equations_rhs += [rhs.simplify_full()] + for alpha in range(dim): + rhs = 0 + for mu in range(dim): + for nu in range(dim): + AUX = velocities[mu] * velocities[nu] + rhs-= gamma[alpha+i0, mu+i0, nu+i0].expr() * AUX + # 'AUX' only used for the line above to be shorter + equations_rhs += [rhs.simplify_full()] IntegratedCurve.__init__(self, parent, equations_rhs, velocities, curve_parameter, initial_tangent_vector, chart=chart, name=name, latex_name=latex_name, - is_isomorphism=is_isomorphism, - is_identity=is_identity, verbose=verbose) self._affine_connection = affine_connection @@ -2683,7 +2881,7 @@ def __reduce__(self): sage: c = M.integrated_autoparallel_curve(nab, (t, 0, 5), v, ....: name='c') sage: c.__reduce__() - (, + (, (Set of Morphisms from Real interval (0, 5) to 3-dimensional differentiable manifold M in Category of homsets of subobjects of sets and topological spaces which @@ -2696,9 +2894,7 @@ def __reduce__(self): differentiable manifold M, Chart (M, (x1, x2, x3)), 'c', - 'c', - False, - False)) + 'c')) Test of pickling:: @@ -2709,8 +2905,7 @@ def __reduce__(self): return (type(self), (self.parent(), self._affine_connection, self._curve_parameter, self._initial_tangent_vector, - self._chart, self._name, self._latex_name, - self._is_isomorphism, self._is_identity)) + self._chart, self._name, self._latex_name)) def system(self, verbose=False): r""" @@ -2849,12 +3044,6 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - - ``is_isomorphism`` -- (default: ``False``) determines whether the - constructed object is a diffeomorphism; if set to ``True``, - then `M` must have dimension one - - ``is_identity`` -- (default: ``False``) determines whether the - constructed object is the identity map; if set to ``True``, - then `M` must coincide with the domain of the curve EXAMPLE: @@ -2987,8 +3176,7 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): def __init__(self, parent, metric, curve_parameter, initial_tangent_vector, chart=None, name=None, - latex_name=None, is_isomorphism=False, - is_identity=False, verbose=False): + latex_name=None, verbose=False): r""" Construct a geodesic curve w.r.t. the given metric with the @@ -3013,17 +3201,12 @@ def __init__(self, parent, metric, curve_parameter, """ - if is_identity: - affine_connection = None - else: - affine_connection = metric.connection() + affine_connection = metric.connection() IntegratedAutoparallelCurve.__init__(self, parent, affine_connection, curve_parameter, initial_tangent_vector, chart=chart, name=name, latex_name=latex_name, - is_isomorphism=is_isomorphism, - is_identity=is_identity, verbose=verbose) self._metric = metric @@ -3077,7 +3260,7 @@ def __reduce__(self): sage: v = Tp((1/sqrt(2),1/sqrt(2))) sage: c = S2.integrated_geodesic(g, (t, 0, pi), v, name='c') sage: c.__reduce__() - (, + (, (Set of Morphisms from Real interval (0, pi) to 2-dimensional differentiable manifold S^2 in Category of homsets of subobjects of sets and topological spaces which @@ -3088,9 +3271,7 @@ def __reduce__(self): differentiable manifold S^2, Chart (S^2, (theta, phi)), 'c', - 'c', - False, - False)) + 'c')) Test of pickling:: @@ -3101,8 +3282,7 @@ def __reduce__(self): return (type(self), (self.parent(), self._metric, self._curve_parameter, self._initial_tangent_vector, - self._chart, self._name, self._latex_name, - self._is_isomorphism, self._is_identity)) + self._chart, self._name, self._latex_name)) def system(self, verbose=False): r""" diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 2210e3e6920..412ca055671 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -2657,11 +2657,14 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1.3, verbose=True) + sage: p = c(1.3, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... - [0.9635581155730744, -0.7325010457963622, 1.3] + sage: p + Point on the 3-dimensional differentiable manifold M + sage: p.coordinates() + (0.9635581155730744, -0.7325010457963622, 1.3) sage: tgt_vec = c.tangent_vector_eval_at(3.7, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' @@ -2685,8 +2688,8 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, integrated_curve_set = IntegratedCurveSet(interval, self) # not # possible to use Hom(interval, self) return integrated_curve_set(equations_rhs, velocities, t, - initial_tangent_vector, chart=chart, name=name, - latex_name=latex_name, verbose=verbose) + initial_tangent_vector, chart=chart, name=name, + latex_name=latex_name, verbose=verbose) def integrated_autoparallel_curve(self, affine_connection, curve_param, initial_tangent_vector, chart=None, @@ -2794,11 +2797,14 @@ def integrated_autoparallel_curve(self, affine_connection, sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1.3, verbose=True) + sage: p = c(1.3, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... - [2.085398163397449, 1.4203172015958863] + sage: p + Point on the 2-dimensional differentiable manifold S^2 + sage: p.coordinates() + (2.085398163397449, 1.4203172015958863) sage: tgt_vec = c.tangent_vector_eval_at(3.7, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' @@ -2822,8 +2828,8 @@ def integrated_autoparallel_curve(self, affine_connection, autoparallel_curve_set=IntegratedAutoparallelCurveSet(interval,self) # not possible to use Hom(interval, self) return autoparallel_curve_set(affine_connection, t, - initial_tangent_vector, chart=chart, - name=name, latex_name=latex_name, verbose=verbose) + initial_tangent_vector, chart=chart, name=name, + latex_name=latex_name, verbose=verbose) def integrated_geodesic(self, metric, curve_param, initial_tangent_vector, chart=None, @@ -2917,11 +2923,14 @@ def integrated_geodesic(self, metric, curve_param, sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1.3, verbose=True) + sage: p = c(1.3, verbose=True) Evaluating point coordinates from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' by default... - [2.2047444794514663, 0.7986609561213334] + sage: p + Point on the 2-dimensional differentiable manifold S^2 + sage: p.coordinates() + (2.2047444794514663, 0.7986609561213334) sage: tgt_vec = c.tangent_vector_eval_at(3.7, verbose=True) Evaluating tangent vector components from the interpolation associated with the key 'cubic spline-interp-rk4_maxima' diff --git a/src/sage/manifolds/differentiable/manifold_homset.py b/src/sage/manifolds/differentiable/manifold_homset.py index 5d283e594e8..e1953b7b0b1 100644 --- a/src/sage/manifolds/differentiable/manifold_homset.py +++ b/src/sage/manifolds/differentiable/manifold_homset.py @@ -10,6 +10,17 @@ differential curves, i.e. morphisms whose domain is an open interval of `\RR`. +The subclass :class:`IntegratedCurveSet` is devoted to differentiable +curves that are defined as a solution to a system of second order +differential equations. + +The subclass :class:`IntegratedAutoparallelCurveSet` is devoted to +differentiable curves that are defined as autoparallel curves w.r.t a +certain affine connection. + +The subclass :class:`IntegratedGeodesicSet` is devoted to differentiable +curves that are defined as geodesics w.r.t to a certain metric. + AUTHORS: - Eric Gourgoulhon (2015): initial version @@ -543,8 +554,8 @@ class IntegratedCurveSet(DifferentiableCurveSet): sage: H = IntegratedCurveSet(R, M) Traceback (most recent call last): ... - ValueError: Both boundaries of the interval defining the domain - of a Homset of integrated curves need to be finite. + ValueError: both boundaries of the interval defining the domain + of a Homset of integrated curves need to be finite An instance whose domain is an interval with finite bounds allows to build an integrated curve defined on the interval:: @@ -629,7 +640,7 @@ class IntegratedCurveSet(DifferentiableCurveSet): sage: f.solve(parameters_values={a:-1, b:+oo}) Traceback (most recent call last): ... - ValueError: Both boundaries of the interval need to be finite. + ValueError: both boundaries of the interval need to be finite The set of integrated curves `J \longrightarrow J` is a set of numerical (manifold) endomorphisms:: @@ -646,29 +657,41 @@ class IntegratedCurveSet(DifferentiableCurveSet): sage: H in Monoids() True - The identity element of the monoid is a numerical version of the - identity map of `J`:: + Although it is a monoid, no identity map is implemented via the + 'one' method of this class or any of its subclasses. + This is justified by the lack of relevance of the identity map + within the framework of this parent class and its subclasses, whose + purpose is mainly devoted to numerical issues (therefore, the user + is left free to set a numerical version of the identity if needed):: - sage: e = H.one() ; e - Integrated curve Id_(a, b) in the Real interval (a, b) + sage: H.one() + Traceback (most recent call last): + ... + ValueError: the identity is not implemented for integrated + curves and associated subclasses A "typical" element of the monoid:: sage: g = H.an_element() ; g Integrated curve in the Real interval (a, b) sage: sys = g.system(verbose=True) - Curve in the Real interval (a, b) integrated over the Real interval (a, b) as a solution to the following system, written w.r.t. Chart ((a, b), (t,)): + Curve in the Real interval (a, b) integrated over the Real + interval (a, b) as a solution to the following system, written + w.r.t. Chart ((a, b), (t,)): - Initial point: Point on the Real number line R with coordinates [0] w.r.t. Chart ((a, b), (t,)) - Initial tangent vector: Tangent vector at Point on the Real number line R with components [1/4] w.r.t. Chart ((a, b), (t,)) + Initial point: Point on the Real number line R with coordinates + [0] w.r.t. Chart ((a, b), (t,)) + Initial tangent vector: Tangent vector at Point on the Real + number line R with components [1/4] w.r.t. Chart ((a, b), (t,)) d(t)/ds = Dt d(Dt)/ds = -1/4*sin(-a + s) - The test suite is passed:: + The test suite is passed, tests '_test_one' and '_test_prod' being + skipped for reasons mentioned above:: - sage: TestSuite(H).run() + sage: TestSuite(H).run(skip=["_test_one", "_test_prod"]) """ @@ -687,8 +710,8 @@ def __init__(self, domain, codomain, name=None, latex_name=None): sage: H = IntegratedCurveSet(R, M) Traceback (most recent call last): ... - ValueError: Both boundaries of the interval defining the - domain of a Homset of integrated curves need to be finite. + ValueError: both boundaries of the interval defining the + domain of a Homset of integrated curves need to be finite sage: I = R.open_interval(-1, 2) sage: H = IntegratedCurveSet(I, M) ; H Set of Morphisms from Real interval (-1, 2) to 3-dimensional @@ -700,7 +723,7 @@ def __init__(self, domain, codomain, name=None, latex_name=None): Set of Morphisms from Real interval (-1, 2) to Real interval (-1, 2) in Category of endsets of subobjects of sets and topological spaces which actually are integrated curves - sage: TestSuite(H).run() + sage: TestSuite(H).run(skip=["_test_one", "_test_prod"]) """ @@ -714,9 +737,9 @@ def __init__(self, domain, codomain, name=None, latex_name=None): t_min = domain.lower_bound() t_max = domain.upper_bound() if t_min == -Infinity or t_max == +Infinity: - raise ValueError("Both boundaries of the interval " + + raise ValueError("both boundaries of the interval " + "defining the domain of a Homset of " + - "integrated curves need to be finite.") + "integrated curves need to be finite") if name is None: self._name = "Hom_integrated({},{})".format(domain._name, @@ -757,8 +780,7 @@ def _repr_(self): def _element_constructor_(self, equations_rhs, velocities, curve_parameter, initial_tangent_vector, chart=None, - name=None, latex_name=None, is_isomorphism=False, - is_identity=False, verbose=False): + name=None, latex_name=None, verbose=False): r""" Construct an element of ``self``, i.e. an integrated curve `I \to M`, where `I` is a real interval and `M` some @@ -790,9 +812,7 @@ def _element_constructor_(self, equations_rhs, velocities, # Standard construction return self.element_class(self, equations_rhs, velocities, curve_parameter, initial_tangent_vector, chart=chart, - name=name, latex_name=latex_name, - is_isomorphism=is_isomorphism, is_identity=is_identity, - verbose=verbose) + name=name, latex_name=latex_name, verbose=verbose) def _an_element_(self): r""" @@ -831,8 +851,10 @@ def _an_element_(self): sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1) - [0.22732435599328793, 0.0] + sage: p = c(1) ; p + Point on the 2-dimensional differentiable manifold M + sage: p.coordinates() + (0.22732435599328793, 0.0) sage: H = IntegratedCurveSet(I, I) sage: c = H._an_element_() ; c Integrated curve in the Real interval (-1, 2) @@ -851,8 +873,10 @@ def _an_element_(self): d(Dt)/ds = -3/8*sin(s + 1) sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1) - [0.840986533989932] + sage: p = c(1) ; p + Point on the Real number line R + sage: p.coordinates() + (0.840986533989932,) """ @@ -923,63 +947,44 @@ def _an_element_(self): return self.element_class(self,eqns_rhs,vels,param,v) - @cached_method def one(self): r""" - Return the identity element of ``self`` considered as a monoid - (case of a set of endomorphisms). - - This applies only when the codomain of the homset is equal to its - domain, i.e. when the homset is of the type `\mathrm{Hom}(M,M)`. - Indeed, `\mathrm{Hom}(M,M)` equipped with the law of morphisms - composition is a monoid, whose identity element is nothing but the - identity map of `M`. + Raise an error refusing to provide the identity element. + This overrides the 'one' method of class + 'TopologicalManifoldHomset', which would actually raise an error + as well due to lack of option 'is_identity' in + 'element_constructor' method of 'self'. - OUTPUT: - - - the identity map of `M`, as an instance of - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` - - EXAMPLE:: + TESTS:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet sage: M = Manifold(3, 'M') sage: X. = M.chart() sage: R. = RealLine() sage: I = R.open_interval(-1, 2) - sage: H = IntegratedCurveSet(I, I); H - Set of Morphisms from Real interval (-1, 2) to Real interval - (-1, 2) in Category of endsets of subobjects of sets and - topological spaces which actually are integrated curves - sage: e = H.one() ; e - Integrated curve Id_(-1, 2) in the Real interval (-1, 2) + sage: H = IntegratedCurveSet(I, M) + sage: H.one() + Traceback (most recent call last): + ... + TypeError: Set of Morphisms from Real interval (-1, 2) to + 3-dimensional differentiable manifold M in Category of + homsets of subobjects of sets and topological spaces which + actually are integrated curves is not a monoid + sage: H = IntegratedCurveSet(I, I) + sage: H.one() + Traceback (most recent call last): + ... + ValueError: the identity is not implemented for integrated + curves and associated subclasses """ - from sage.symbolic.ring import var - if self.codomain() != self.domain(): raise TypeError("{} is not a monoid".format(self)) - - t = self.domain().canonical_coordinate() - # It is important to distinguish between the canonical - # coordinate, and the curve parameter since, in such a - # situation, the coordinate should not be used to denote the - # curve parameter, since it actually becomes a function of the - # curve parameter, and such a function is an unknown of the - # system defining the curve. - param = var('s') - if t == param: # the canonical coordinate of the domain - # might be the expression 's' even though it was affected - # above to the variable 't' - param = var('u') - - vel = [var("D{}".format(t))] - - # Below, set argument to 'None' where the value does not have - # any influence since argument 'is_identity' is set to 'True' - return self.element_class(self, None, vel, param, None, - is_identity=True) + else: + raise ValueError("the identity is not implemented for " + + "integrated curves and associated " + + "subclasses") #****************************************************************************** @@ -1020,8 +1025,8 @@ class IntegratedAutoparallelCurveSet(IntegratedCurveSet): sage: H = IntegratedAutoparallelCurveSet(R, M) Traceback (most recent call last): ... - ValueError: Both boundaries of the interval defining the domain - of a Homset of integrated autoparallel curves need to be finite. + ValueError: both boundaries of the interval defining the domain + of a Homset of integrated autoparallel curves need to be finite An instance whose domain is an interval with finite bounds allows to build a curve that is autoparallel w.r.t a connection defined on the @@ -1076,9 +1081,9 @@ class IntegratedAutoparallelCurveSet(IntegratedCurveSet): For any open interval `J` with finite bounds `(a,b)`, all curves are autoparallel w.r.t any connection. - Therefore, the set of autoparallel curves `J \longrightarrow J` is a set of - numerical (manifold) endomorphisms that is a monoid for the law of - morphism composition:: + Therefore, the set of autoparallel curves `J \longrightarrow J` is a + set of numerical (manifold) endomorphisms that is a monoid for the + law of morphism composition:: sage: [a,b] = var('a b') sage: J = R.open_interval(a, b) @@ -1091,27 +1096,45 @@ class IntegratedAutoparallelCurveSet(IntegratedCurveSet): Category of endsets of subobjects of sets and topological spaces sage: H in Monoids() True - sage: e = H.one() ; e - Integrated autoparallel curve Id_(a, b) in the Real - interval (a, b) + + Although it is a monoid, no identity map is implemented via the + 'one' method of this class or its subclass devoted to geodesics. + This is justified by the lack of relevance of the identity map + within the framework of this parent class and its subclass, whose + purpose is mainly devoted to numerical issues (therefore, the user + is left free to set a numerical version of the identity if needed):: + + sage: H.one() + Traceback (most recent call last): + ... + ValueError: the identity is not implemented for integrated + curves and associated subclasses A "typical" element of the monoid:: sage: g = H.an_element() ; g Integrated autoparallel curve in the Real interval (a, b) sage: sys = g.system(verbose=True) - Autoparallel curve in the Real interval (a, b) equipped with Affine connection nab on the Open subset U of the Real number line R, and integrated over the Real interval (a, b) as a solution to the following equations, written w.r.t. Chart ((a, b), (t,)): + Autoparallel curve in the Real interval (a, b) equipped with + Affine connection nab on the Open subset U of the Real number + line R, and integrated over the Real interval (a, b) as a + solution to the following equations, written w.r.t. + Chart ((a, b), (t,)): - Initial point: Point on the Real number line R with coordinates [1/200] w.r.t. Chart ((a, b), (t,)) - Initial tangent vector: Tangent vector at Point on the Real number line R with components [-9999/400/(a - b)] w.r.t. Chart ((a, b), (t,)) + Initial point: Point on the Real number line R with coordinates + [1/200] w.r.t. Chart ((a, b), (t,)) + Initial tangent vector: Tangent vector at Point on the Real + number line R with components [-9999/400/(a - b)] w.r.t. + Chart ((a, b), (t,)) d(t)/ds = Dt d(Dt)/ds = -Dt^2/t - The test suite is passed:: + The test suite is passed, tests '_test_one' and '_test_prod' being + skipped for reasons mentioned above:: - sage: TestSuite(H).run() + sage: TestSuite(H).run(skip=["_test_one", "_test_prod"]) """ @@ -1130,9 +1153,9 @@ def __init__(self, domain, codomain, name=None, latex_name=None): sage: H = IntegratedAutoparallelCurveSet(R, M) Traceback (most recent call last): ... - ValueError: Both boundaries of the interval defining the + ValueError: both boundaries of the interval defining the domain of a Homset of integrated autoparallel curves need - to be finite. + to be finite sage: I = R.open_interval(-1, 2) sage: H = IntegratedAutoparallelCurveSet(I, M) ; H Set of Morphisms from Real interval (-1, 2) to 3-dimensional @@ -1145,7 +1168,7 @@ def __init__(self, domain, codomain, name=None, latex_name=None): (-1, 2) in Category of endsets of subobjects of sets and topological spaces which actually are integrated autoparallel curves w.r.t a certain affine connection - sage: TestSuite(H).run() + sage: TestSuite(H).run(skip=["_test_one", "_test_prod"]) """ @@ -1158,10 +1181,10 @@ def __init__(self, domain, codomain, name=None, latex_name=None): t_min = domain.lower_bound() t_max = domain.upper_bound() if t_min == -Infinity or t_max == +Infinity: - raise ValueError("Both boundaries of the interval " + + raise ValueError("both boundaries of the interval " + "defining the domain of a Homset of " + "integrated autoparallel curves need to " + - "be finite.") + "be finite") if name is None: self._name = "Hom_autoparallel" @@ -1205,8 +1228,7 @@ def _repr_(self): def _element_constructor_(self, affine_connection, curve_parameter, initial_tangent_vector, chart=None, name=None, - latex_name=None, is_isomorphism=False, - is_identity=False, verbose=False): + latex_name=None, verbose=False): r""" Construct an element of ``self``, i.e. an integrated autoparallel curve `I \to M`, where `I` is a real interval and @@ -1240,9 +1262,7 @@ def _element_constructor_(self, affine_connection, curve_parameter, # Standard construction return self.element_class(self, affine_connection, curve_parameter, initial_tangent_vector, chart=chart, - name=name,latex_name=latex_name, - is_isomorphism=is_isomorphism, is_identity=is_identity, - verbose=verbose) + name=name,latex_name=latex_name, verbose=verbose) def _an_element_(self): r""" @@ -1286,8 +1306,10 @@ def _an_element_(self): sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1) - [0.34375000000000056, 0.49999813529956155] + sage: p = c(1) ; p + Point on the 2-dimensional differentiable manifold M + sage: p.coordinates() + (0.34375000000000056, 0.49999813529956155) sage: H = IntegratedAutoparallelCurveSet(I, I) sage: c = H._an_element_() ; c Integrated autoparallel curve in the Real interval (-1, 2) @@ -1309,8 +1331,10 @@ def _an_element_(self): sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1) - [1.0606601601128764] + sage: p = c(1) ; p + Point on the Real number line R + sage: p.coordinates() + (1.0606601601128764,) """ @@ -1477,77 +1501,6 @@ def _an_element_(self): return self.element_class(self, nab, param, v) - @cached_method - def one(self): - r""" - Return the identity element of ``self`` considered as a monoid - (case of a set of endomorphisms). - - This applies only when the codomain of the homset is equal to its - domain, i.e. when the homset is of the type `\mathrm{Hom}(M,M)`. - Indeed, `\mathrm{Hom}(M,M)` equipped with the law of morphisms - composition is a monoid, whose identity element is nothing but the - identity map of `M`. - - OUTPUT: - - - the identity map of `M`, as an instance of - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` - - EXAMPLE:: - - sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet - sage: M = Manifold(3, 'M') - sage: X. = M.chart() - sage: R. = RealLine() - sage: I = R.open_interval(-1, 2) - sage: H = IntegratedAutoparallelCurveSet(I, I); H - Set of Morphisms from Real interval (-1, 2) to Real interval - (-1, 2) in Category of endsets of subobjects of sets and - topological spaces which actually are integrated - autoparallel curves w.r.t a certain affine connection - sage: e = H.one() ; e - Integrated autoparallel curve Id_(-1, 2) in the Real - interval (-1, 2) - - """ - - from sage.symbolic.ring import var - - if self.codomain() != self.domain(): - raise TypeError("{} is not a monoid".format(self)) - - t = self.domain().canonical_coordinate() - # It is important to distinguish between the canonical - # coordinate, and the curve parameter since, in such a - # situation, the coordinate should not be used to denote the - # curve parameter, since it actually becomes a function of the - # curve parameter, and such a function is an unknown of the - # system defining the curve. - param = var('s') - if t == param: # the canonical coordinate of the domain - # might be the expression 's' even though it was affected - # above to the variable 't' - param = var('u') - - vel = [var("D{}".format(t))] - - # Below, set argument to 'None' where the value does not have any - # influence since argument 'is_identity' is set to 'True' - # As a result, it does not need to be overriden in - # IntegratedGeodesicSet since the signature of the 'init' - # methods of the classes IntegratedAutoparallelCurve and - # IntegratedGeodesic are the same, except argument - # 'affine_connection' of the first one is replaced by 'metric' - # in the second one. - # Since both do not have any influence on the definition of the - # curve when argument 'is_identity' is set to 'True', the class - # IntegratedGeodesicSet may use this method 'one' with arguments - # set to 'None' in the same places. - return self.element_class(self, None, param, None, - is_identity=True) - - #****************************************************************************** class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): @@ -1586,8 +1539,8 @@ class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): sage: H = IntegratedGeodesicSet(R, M) Traceback (most recent call last): ... - ValueError: Both boundaries of the interval defining the domain - of a Homset of integrated geodesics need to be finite. + ValueError: both boundaries of the interval defining the domain + of a Homset of integrated geodesics need to be finite An instance whose domain is an interval with finite bounds allows to build a geodesic w.r.t. a metric defined on the codomain:: @@ -1653,8 +1606,19 @@ class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): Category of endsets of subobjects of sets and topological spaces sage: H in Monoids() True - sage: e = H.one() ; e - Integrated geodesic Id_(a, b) in the Real interval (a, b) + + Although it is a monoid, no identity map is implemented via the + 'one' method of this class. + This is justified by the lack of relevance of the identity map + within the framework of this parent class, whose purpose is mainly + devoted to numerical issues (therefore, the user is left free to set + a numerical version of the identity if needed):: + + sage: H.one() + Traceback (most recent call last): + ... + ValueError: the identity is not implemented for integrated + curves and associated subclasses A "typical" element of the monoid:: @@ -1677,9 +1641,10 @@ class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): d(Dt)/ds = -4*Dt^2/t - The test suite is passed:: + The test suite is passed, tests '_test_one' and '_test_prod' being + skipped for reasons mentioned above:: - sage: TestSuite(H).run() + sage: TestSuite(H).run(skip=["_test_one", "_test_prod"]) """ @@ -1698,9 +1663,9 @@ def __init__(self, domain, codomain, name=None, latex_name=None): sage: H = IntegratedGeodesicSet(R, M) Traceback (most recent call last): ... - ValueError: Both boundaries of the interval defining the + ValueError: both boundaries of the interval defining the domain of a Homset of integrated geodesics need to be - finite. + finite sage: I = R.open_interval(-1, 2) sage: H = IntegratedGeodesicSet(I, M) ; H Set of Morphisms from Real interval (-1, 2) to 3-dimensional @@ -1713,7 +1678,7 @@ def __init__(self, domain, codomain, name=None, latex_name=None): (-1, 2) in Category of endsets of subobjects of sets and topological spaces which actually are integrated geodesics w.r.t a certain metric - sage: TestSuite(H).run() + sage: TestSuite(H).run(skip=["_test_one", "_test_prod"]) """ @@ -1726,9 +1691,9 @@ def __init__(self, domain, codomain, name=None, latex_name=None): t_min = domain.lower_bound() t_max = domain.upper_bound() if t_min == -Infinity or t_max == +Infinity: - raise ValueError("Both boundaries of the interval " + + raise ValueError("both boundaries of the interval " + "defining the domain of a Homset of " + - "integrated geodesics need to be finite.") + "integrated geodesics need to be finite") if name is None: self._name = "Hom_geodesic" @@ -1769,8 +1734,7 @@ def _repr_(self): def _element_constructor_(self, metric, curve_parameter, initial_tangent_vector, chart=None, name=None, - latex_name=None, is_isomorphism=False, - is_identity=False, verbose=False): + latex_name=None, verbose=False): r""" Construct an element of ``self``, i.e. an integrated geodesic `I \to M`, where `I` is a real interval and @@ -1802,8 +1766,7 @@ def _element_constructor_(self, metric, curve_parameter, # Standard construction return self.element_class(self, metric, curve_parameter, initial_tangent_vector, chart=chart, name=name, - latex_name=latex_name, is_isomorphism=is_isomorphism, - is_identity=is_identity, verbose=verbose) + latex_name=latex_name, verbose=verbose) def _an_element_(self): r""" @@ -1849,8 +1812,10 @@ def _an_element_(self): sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1) - [0.34375000000000056, 0.0, 0.0, 0.0] + sage: p = c(1) ; p + Point on the 4-dimensional differentiable manifold M + sage: p.coordinates() + (0.34375000000000056, 0.0, 0.0, 0.0) sage: H = IntegratedGeodesicSet(I, I) sage: c = H._an_element_() ; c Integrated geodesic in the Real interval (-1, 2) @@ -1872,8 +1837,10 @@ def _an_element_(self): sage: sol = c.solve() sage: interp = c.interpolate() - sage: c(1) - [1.1495758053215723] + sage: p = c(1) ; p + Point on the Real number line R + sage: p.coordinates() + (1.1495758053215723,) """ From 00dd6a40979bca3342a481272517691127301cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 10 Jul 2017 14:06:48 +0200 Subject: [PATCH 057/184] caring for repeated words (from t to z) --- src/ext/pari/dokchitser/ex-chqua | 2 +- src/sage/coding/guava.py | 2 +- src/sage/combinat/finite_state_machine.py | 4 ++-- src/sage/combinat/words/alphabet.py | 2 +- src/sage/graphs/comparability.pyx | 4 ++-- src/sage/graphs/graph.py | 2 +- src/sage/graphs/lovasz_theta.py | 6 +++--- src/sage/graphs/planarity.pyx | 6 +++--- src/sage/groups/finitely_presented.py | 2 +- .../groups/perm_gps/partn_ref/canonical_augmentation.pyx | 4 ++-- src/sage/interfaces/macaulay2.py | 4 ++-- src/sage/interfaces/singular.py | 2 +- src/sage/lfunctions/zero_sums.pyx | 2 +- .../manifolds/differentiable/levi_civita_connection.py | 2 +- src/sage/manifolds/differentiable/tensorfield.py | 2 +- src/sage/manifolds/differentiable/tensorfield_paral.py | 2 +- src/sage/matrix/matrix2.pyx | 2 +- src/sage/modular/dirichlet.py | 7 ++++--- src/sage/quivers/homspace.py | 2 +- src/sage/rings/asymptotic/asymptotic_ring.py | 2 +- src/sage/rings/number_field/number_field.py | 7 ++++--- src/sage/rings/padics/FP_template.pxi | 3 ++- src/sage/rings/polynomial/multi_polynomial.pyx | 2 +- src/sage/rings/polynomial/pbori.pyx | 4 ++-- src/sage/schemes/elliptic_curves/ell_rational_field.py | 2 +- src/sage/schemes/elliptic_curves/isogeny_class.py | 6 +++--- src/sage/schemes/elliptic_curves/padics.py | 2 +- src/sage/schemes/projective/projective_point.py | 2 +- src/sage_setup/docbuild/__init__.py | 8 +++++--- 29 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/ext/pari/dokchitser/ex-chqua b/src/ext/pari/dokchitser/ex-chqua index 445abfb459c..d969ef77301 100644 --- a/src/ext/pari/dokchitser/ex-chqua +++ b/src/ext/pari/dokchitser/ex-chqua @@ -7,7 +7,7 @@ read("computel"); \\ read the ComputeL package \\ and set the default values default(realprecision,28); \\ set working precision; used throughout -P = 37; \\ May change this to to any other odd prime +P = 37; \\ May change this to any other odd prime \\ Chi = Legendre character modulo this P \\ initialize L-function parameters diff --git a/src/sage/coding/guava.py b/src/sage/coding/guava.py index 0d1a7265881..45d77ad2ca9 100644 --- a/src/sage/coding/guava.py +++ b/src/sage/coding/guava.py @@ -16,7 +16,7 @@ - David Joyner (2008-03): removed QR, XQR, cyclic and ReedSolomon codes - David Joyner (2009-05): added "optional package" comments, fixed some - docstrings to to be sphinx compatible + docstrings to be sphinx compatible REFERENCES: diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 1dd58caa480..20220b314fa 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -11034,7 +11034,7 @@ def language(self, max_length=None, **kwargs): ``max_length`` will be considered. If ``None``, then this iterates over all possible words without length restrictions. - - ``kwargs`` -- will be passed on to to the :class:`process + - ``kwargs`` -- will be passed on to the :class:`process iterator `. See :meth:`process` for a description. @@ -12309,7 +12309,7 @@ def language(self, max_length=None, **kwargs): considered. If ``None``, then this iterates over all possible words without length restrictions. - - ``kwargs`` -- will be passed on to to the :class:`process + - ``kwargs`` -- will be passed on to the :class:`process iterator `. See :meth:`process` for a description. diff --git a/src/sage/combinat/words/alphabet.py b/src/sage/combinat/words/alphabet.py index a7fe2bc414d..bf7a06223eb 100644 --- a/src/sage/combinat/words/alphabet.py +++ b/src/sage/combinat/words/alphabet.py @@ -215,7 +215,7 @@ def build_alphabet(data=None, names=None, name=None): if name is not None and (data is not None or names is not None): raise ValueError("name cannot be specified with any other argument") - # Swap arguments if we need to to try and make sure we have "good" user input + # Swap arguments if we need to try and make sure we have "good" user input if isinstance(names, integer_types + (Integer,)) or names == Infinity \ or (data is None and names is not None): data,names = names,data diff --git a/src/sage/graphs/comparability.pyx b/src/sage/graphs/comparability.pyx index 5d7ec382f85..283c13715d9 100644 --- a/src/sage/graphs/comparability.pyx +++ b/src/sage/graphs/comparability.pyx @@ -457,8 +457,8 @@ def is_comparability_MILP(g, certificate = False): p.add_constraint(o[u,v] + o[vv,u] - o[vv,v] <= 1) p.add_constraint(o[u,vv] + o[v,u] - o[v,vv] <= 1) - # If there is no edge there there are only two - # orientation possible (see the module's documentation + # If there is no edge, there are only two + # orientations possible (see the module's documentation # about edges which imply each other) else: p.add_constraint(o[u,v] + o[vv,u] <= 1) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index b63135d8e64..8ff3792197f 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -7656,7 +7656,7 @@ def has_perfect_matching(self, algorithm="Edmonds", solver=None, verbose=0): find a matching of maximal cardinality, then check whether this cardinality is half the number of vertices of the graph. - - ``"LP_matching"`` uses uses a Linear Program to find a matching of + - ``"LP_matching"`` uses a Linear Program to find a matching of maximal cardinality, then check whether this cardinality is half the number of vertices of the graph. diff --git a/src/sage/graphs/lovasz_theta.py b/src/sage/graphs/lovasz_theta.py index 39c6525bf1f..f150dca25f4 100644 --- a/src/sage/graphs/lovasz_theta.py +++ b/src/sage/graphs/lovasz_theta.py @@ -37,11 +37,11 @@ def lovasz_theta(graph): .. NOTE:: - - Implemented for undirected graphs only. Use to_undirected to convert a - digraph to an undirected graph. + - Implemented for undirected graphs only. Use ``to_undirected`` + to convert a digraph to an undirected graph. - This function requires the optional package ``csdp``, which you can - install with with ``sage -i csdp``. + install with ``sage -i csdp``. EXAMPLES:: diff --git a/src/sage/graphs/planarity.pyx b/src/sage/graphs/planarity.pyx index 7c6cd34a0a3..6ef8c1278ca 100644 --- a/src/sage/graphs/planarity.pyx +++ b/src/sage/graphs/planarity.pyx @@ -76,10 +76,10 @@ def is_planar(g, kuratowski=False, set_pos=False, set_embedding=False, circular= True There were some problems with ``set_pos`` stability in the past, - so let's check if this this runs without exception:: + so let's check if this runs without exception:: - sage: for i,g in enumerate(atlas_graphs): # long time - ....: if (not g.is_connected() or i==0): + sage: for i, g in enumerate(atlas_graphs): # long time + ....: if (not g.is_connected() or i == 0): ....: continue ....: _ = g.is_planar(set_embedding=True, set_pos=True) """ diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index f2ebf57ac23..5d20347dd71 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -1042,7 +1042,7 @@ def direct_product(self, H, reduced=False, new_names=True): the group to be returned. If ``False``, the group to be returned keeps the generator names of the two groups forming the direct product. Note that one cannot ask to reduce the output and ask - to keep the old variable names, as they they may change meaning + to keep the old variable names, as they may change meaning in the output group if its presentation is reduced. OUTPUT: diff --git a/src/sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx b/src/sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx index 8b34a386bc3..8922e525efa 100644 --- a/src/sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx +++ b/src/sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx @@ -134,10 +134,10 @@ I. ``canonical_parent``: Apply the ``permutation`` to the ``child``, determine an arbitrary but fixed parent, apply the inverse of ``permutation`` to that parent, and return the - resulting object. Must also set the integer ``degree`` points to to the + resulting object. Must also set the integer ``degree`` points to the degree of the returned object. -NOTE: +.. NOTE:: It is a good idea to try to implement an augmentation scheme where the degree of objects on each level of the augmentation tree is constant. The diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index 5e2cacec18d..0c979b88306 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -587,8 +587,8 @@ def use(self, R): def new_from(self, type, value): """ - Returns a new Macaulay2Element of type type constructed from - value. + Return a new ``Macaulay2Element`` of type ``type`` constructed from + ``value``. EXAMPLES:: diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index e7dfab1940d..a5d571e0834 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1611,7 +1611,7 @@ def sage_global_ring(self): from sage.all import PolynomialRing # Meanwhile Singulars quotient rings are also of 'ring' type, not 'qring' as it was in the past. # To find out if a singular ring is a quotient ring or not checking for ring type does not help - # and instead of that we we check if the quotient ring is zero or not: + # and instead of that we check if the quotient ring is zero or not: if (singular.eval('ideal(basering)==0')=='1'): return PolynomialRing(BR, names=singular.eval('varstr(basering)'), order=termorder_from_singular(singular)) P = PolynomialRing(BR, names=singular.eval('varstr(basering)'), order=termorder_from_singular(singular)) diff --git a/src/sage/lfunctions/zero_sums.pyx b/src/sage/lfunctions/zero_sums.pyx index 247781bb180..6b41a6f55c3 100644 --- a/src/sage/lfunctions/zero_sums.pyx +++ b/src/sage/lfunctions/zero_sums.pyx @@ -1567,7 +1567,7 @@ cdef class LFunctionZeroSum_EllipticCurve(LFunctionZeroSum_abstract): - If True, the computation is first run with small and then successively larger Delta values up to max_Delta. If at any - point the computed bound is 0 (or 1 when when root_number is -1 + point the computed bound is 0 (or 1 when root_number is -1 or True), the computation halts and that value is returned; otherwise the minimum of the computed bounds is returned. - If False, the computation is run a single time with diff --git a/src/sage/manifolds/differentiable/levi_civita_connection.py b/src/sage/manifolds/differentiable/levi_civita_connection.py index 0e6727b1b70..332ed94dc51 100644 --- a/src/sage/manifolds/differentiable/levi_civita_connection.py +++ b/src/sage/manifolds/differentiable/levi_civita_connection.py @@ -38,7 +38,7 @@ class LeviCivitaConnection(AffineConnection): Levi-Civita connection on a pseudo-Riemannian manifold. Let `M` be a differentiable manifold of class `C^\infty` (smooth manifold) - over `\RR` endowed with with a pseudo-Riemannian metric `g`. + over `\RR` endowed with a pseudo-Riemannian metric `g`. Let `C^\infty(M)` be the algebra of smooth functions `M\rightarrow \RR` (cf. :class:`~sage.manifolds.differentiable.scalarfield_algebra.DiffScalarFieldAlgebra`) diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index e2ba35c4af0..c0343953fb6 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -936,7 +936,7 @@ def add_comp(self, basis=None): for assignment. The components with respect to other frames having the same domain - as the provided vector frame are kept. To delete them them, use the + as the provided vector frame are kept. To delete them, use the method :meth:`set_comp` instead. INPUT: diff --git a/src/sage/manifolds/differentiable/tensorfield_paral.py b/src/sage/manifolds/differentiable/tensorfield_paral.py index b208f8b8825..312ec0fc26c 100644 --- a/src/sage/manifolds/differentiable/tensorfield_paral.py +++ b/src/sage/manifolds/differentiable/tensorfield_paral.py @@ -790,7 +790,7 @@ def add_comp(self, basis=None): for assignment. The components with respect to other frames on the same domain are - kept. To delete them them, use the method :meth:`set_comp` instead. + kept. To delete them, use the method :meth:`set_comp` instead. INPUT: diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 39e3cbb44a8..398c1947ec9 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -7239,7 +7239,7 @@ cdef class Matrix(Matrix1): INPUT: - ``check`` -- (default: ``False``) If ``True`` return a tuple of - the maximal matrix and the permutations taking taking ``self`` + the maximal matrix and the permutations taking ``self`` to the maximal matrix. If ``False``, return only the maximal matrix. diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 99f427abde3..cd221e48a57 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -1150,10 +1150,11 @@ def jacobi_sum(self, char, check=True): return sum([self(x) * char(1-x) for x in rings.IntegerModRing(self.modulus())]) - def kloosterman_sum(self, a=1,b=0): + def kloosterman_sum(self, a=1, b=0): r""" Return the "twisted" Kloosterman sum associated to this Dirichlet character. - This includes Gauss sums, classical Kloosterman sums, Salie sums, etc. + + This includes Gauss sums, classical Kloosterman sums, Salié sums, etc. The Kloosterman sum associated to `\chi` and the integers a,b is @@ -1162,7 +1163,7 @@ def kloosterman_sum(self, a=1,b=0): K(a,b,\chi) = \sum_{r \in (\ZZ/m\ZZ)^\times} \chi(r)\,\zeta^{ar+br^{-1}}, where `m` is the modulus of `\chi` and `\zeta` is a primitive - `m` th root of unity. This reduces to to the Gauss sum if `b=0`. + `m` th root of unity. This reduces to the Gauss sum if `b=0`. This method performs an exact calculation and returns an element of a suitable cyclotomic field; see also :meth:`.kloosterman_sum_numerical`, diff --git a/src/sage/quivers/homspace.py b/src/sage/quivers/homspace.py index 02937e7fb32..d44d2ccf6d2 100644 --- a/src/sage/quivers/homspace.py +++ b/src/sage/quivers/homspace.py @@ -94,7 +94,7 @@ def __init__(self, domain, codomain, category=None): # and N to the vertices of the quiver. Each coordinate represents a # single entry in one of those matrices. - # Get the quiver and base ring and check they they are the same for + # Get the quiver and base ring and check that they are the same for # both modules if domain._semigroup != codomain._semigroup: raise ValueError("representations are not over the same quiver") diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index 8ac348e5438..1703babd693 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -1075,7 +1075,7 @@ def monomial_coefficient(self, monomial): INPUT: - ``monomial`` -- a monomial element which can be converted - into the the asymptotic ring of this element + into the asymptotic ring of this element OUTPUT: diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index c5cfe2c731f..08965f64046 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -10012,9 +10012,10 @@ def _multiplicative_order_table(self): def zeta(self, n=None, all=False): """ - Return an element of multiplicative order `n` in this this - cyclotomic field. If there is no such element, raise a - ``ValueError``. + Return an element of multiplicative order `n` in this + cyclotomic field. + + If there is no such element, raise a ``ValueError``. INPUT: diff --git a/src/sage/rings/padics/FP_template.pxi b/src/sage/rings/padics/FP_template.pxi index 2ce6b4575cb..6970a3d1c6d 100644 --- a/src/sage/rings/padics/FP_template.pxi +++ b/src/sage/rings/padics/FP_template.pxi @@ -1389,9 +1389,10 @@ cdef class pAdicCoercion_ZZ_FP(RingHomomorphism_coercion): """ return self._section + cdef class pAdicConvert_FP_ZZ(RingMap): """ - The map from a floating point ring back to ZZ that returns the the smallest + The map from a floating point ring back to ZZ that returns the smallest non-negative integer approximation to its input which is accurate up to the precision. If the input is not in the closure of the image of ZZ, raises a ValueError. diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 2fa0e2f4205..fc610dcfcd5 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -2214,7 +2214,7 @@ cdef class MPolynomial(CommutativeRingElement): a += b d = (t-(L[j].real()))/((t-(L[j])) * (t-(L[j].conjugate())) + u**2) c += d - #Newton's Method, to to find solutions. Error bound is while less than diameter of our z + #Newton's Method, to find solutions. Error bound is while less than diameter of our z err = z.diameter() zz = z.diameter() n = F.degree() diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori.pyx index ff42c9aaae1..1b6d7b163bd 100644 --- a/src/sage/rings/polynomial/pbori.pyx +++ b/src/sage/rings/polynomial/pbori.pyx @@ -4745,7 +4745,7 @@ cdef class PolynomialConstruct: def lead(self, x): """ Return the leading monomial of boolean polynomial ``x``, with - respect to to the order of parent ring. + respect to the order of parent ring. EXAMPLES:: @@ -8235,7 +8235,7 @@ cdef class PolynomialFactory: def lead(self, x): """ Return the leading monomial of boolean polynomial ``x``, with - respect to to the order of parent ring. + respect to the order of parent ring. EXAMPLES:: diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index c09153dc4f6..a545876aafd 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1598,7 +1598,7 @@ def analytic_rank_upper_bound(self, - ``True`` -- the computation is first run with small and then successively larger `\Delta` values up to max_Delta. If at any - point the computed bound is 0 (or 1 when when root_number is -1 + point the computed bound is 0 (or 1 when root_number is -1 or True), the computation halts and that value is returned; otherwise the minimum of the computed bounds is returned. - ``False`` -- the computation is run a single time with `\Delta` diff --git a/src/sage/schemes/elliptic_curves/isogeny_class.py b/src/sage/schemes/elliptic_curves/isogeny_class.py index 19cea05c7a0..2678b9958e0 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_class.py +++ b/src/sage/schemes/elliptic_curves/isogeny_class.py @@ -987,7 +987,7 @@ def __init__(self, E, algorithm="sage", label=None, empty=False): sage: E.isogeny_class(order='database') Traceback (most recent call last): ... - RuntimeError: unable to to find Elliptic Curve defined by y^2 = x^3 + 1001 over Rational Field in the database + RuntimeError: unable to find Elliptic Curve defined by y^2 = x^3 + 1001 over Rational Field in the database sage: TestSuite(isocls).run() """ self._algorithm = algorithm @@ -1041,11 +1041,11 @@ def _compute(self): try: label = self.E.cremona_label(space=False) except RuntimeError: - raise RuntimeError("unable to to find %s in the database"%self.E) + raise RuntimeError("unable to find %s in the database" % self.E) db = sage.databases.cremona.CremonaDatabase() curves = db.isogeny_class(label) if len(curves) == 0: - raise RuntimeError("unable to to find %s in the database"%self.E) + raise RuntimeError("unable to find %s in the database" % self.E) # All curves will have the same conductor and isogeny class, # and there are are most 8 of them, so lexicographic sorting is okay. self.curves = tuple(sorted(curves, key = lambda E: E.cremona_label())) diff --git a/src/sage/schemes/elliptic_curves/padics.py b/src/sage/schemes/elliptic_curves/padics.py index c51b3c65e8c..2ae48c5dfdc 100644 --- a/src/sage/schemes/elliptic_curves/padics.py +++ b/src/sage/schemes/elliptic_curves/padics.py @@ -1116,7 +1116,7 @@ def padic_sigma(self, p, N=20, E2=None, check=False, check_hypotheses=True): A = (-X.a1()/2 - A) * f # Convert to a power series and remove the -1/x term. - # Also we artificially bump up the accuracy from N-2 to to N-1 digits; + # Also we artificially bump up the accuracy from N-2 to N-1 digits; # the constant term needs to be known to N-1 digits, so we compute # it directly assert A.valuation() == -1 and A[-1] == 1 diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 575d616f3cd..d436e469b23 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -1457,7 +1457,7 @@ def is_preperiodic(self, f, err=0.1, return_period=False): # however precision issues can occur so we can only tell *not* preperiodic # if the value is larger than the error if h <= err: - # if the canonical height is less than than the + # if the canonical height is less than the # error, then we suspect preperiodic so check # either we can find the cycle or the height is # larger than the difference between the canonical height diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 43156631ed3..5d187cf8081 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -76,7 +76,7 @@ def build_ref_doc(args): def builder_helper(type): """ Returns a function which builds the documentation for - output type type. + output type ``type``. """ def f(self, *args, **kwds): output_dir = self._output_dir(type) @@ -481,7 +481,8 @@ def __init__(self, name, lang='en'): def _output_dir(self, type, lang='en'): """ - Returns the directory where the output of type type is stored. + Return the directory where the output of type ``type`` is stored. + If the directory does not exist, then it will automatically be created. @@ -1164,7 +1165,8 @@ def setup(app): def _output_dir(self, type): """ - Returns the directory where the output of type type is stored. + Return the directory where the output of type ``type`` is stored. + If the directory does not exist, then it will automatically be created. """ From a91fb48e8168731d1377b70b24f90a7319d66bf6 Mon Sep 17 00:00:00 2001 From: Karim Van Aelst Date: Wed, 12 Jul 2017 15:46:50 +0200 Subject: [PATCH 058/184] changes suggested by review, simpler 'an_element' methods --- src/sage/manifolds/differentiable/chart.py | 2 +- .../differentiable/integrated_curve.py | 76 ++-- src/sage/manifolds/differentiable/manifold.py | 24 +- .../differentiable/manifold_homset.py | 397 +++++++----------- 4 files changed, 197 insertions(+), 302 deletions(-) diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 6f70c6d2b4a..a7a887aa18c 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -570,7 +570,7 @@ def symbolic_velocities(self, left='D', right=None): - a list of symbolic expressions with the desired names - EXAMPLE: + EXAMPLES: Symbolic derivatives of the Cartesian coordinates of the 3-dimensional Euclidean space:: diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index 83a593e1006..e19afcf7dd5 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -10,14 +10,14 @@ classes :class:`IntegratedAutoparallelCurve` and :class:`IntegratedGeodesic` inherit. -.. RUBRIC:: Example: A geodesic in hyperbolic Poincaré half-plane +.. RUBRIC:: Examples: A geodesic in hyperbolic Poincaré half-plane First declare a chart over the Poincaré half-plane:: sage: M = Manifold(2, 'M') sage: X. = M.chart('x y:(0,+oo)') -Then declare the hyperbolic Poicaré metric:: +Then declare the hyperbolic Poincaré metric:: sage: g = M.metric('g') sage: g[0,0], g[1,1] = 1/y^2, 1/y^2 @@ -27,9 +27,9 @@ Pick an initial point and an initial tangent vector:: sage: p = M((0,1), name='p') - sage: v = M.tangent_space(p)((1,3/2)) + sage: v = M.tangent_space(p)((1,3/2), name='v') sage: v.display() - d/dx + 3/2 d/dy + v = d/dx + 3/2 d/dy Declare a geodesic with such initial conditions, denoting ``t`` the corresponding affine parameter:: @@ -46,7 +46,26 @@ sage: interp = c.interpolate() sage: graph = c.plot_integrated() - sage: graph.show() + sage: p_plot = p.plot(size=30, label_offset=0.07, fontsize=20) + sage: v_plot = v.plot(label_offset=0.05, fontsize=20) + sage: (graph + p_plot + v_plot).show() + +.. PLOT:: + + M = Manifold(2, 'M') + X = M.chart('x y') + var('x y t') + g = M.metric('g') + g[0,0], g[1,1] = 1/y**2, 1/y**2 + p = M((0,1), name='p') + v = M.tangent_space(p)((1,3/2), name='v') + c = M.integrated_geodesic(g, (t, 0, 10), v, name='c') + sol = c.solve() + interp = c.interpolate() + graph = c.plot_integrated() + p_plot = p.plot(size=30, label_offset=0.07, fontsize=20) + v_plot = v.plot(label_offset=0.05, fontsize=20) + sphinx_plot(graph + p_plot + v_plot) AUTHORS: @@ -108,7 +127,7 @@ class IntegratedCurve(DifferentiableCurve): - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - EXAMPLE: + EXAMPLES: Motion of a charged particle in an axial magnetic field linearly increasing in time and exponentially decreasing in space: @@ -618,7 +637,7 @@ def system(self, verbose=False): - list containing the attributes :attr:`equations_rhs`, :attr:`initial_tangent_vector` and :attr:`chart` - EXAMPLE: + EXAMPLES: System defining an integrated curve:: @@ -730,7 +749,7 @@ def solve_analytical(self, verbose=False): or boolean 'False' (in case the differential system could not be solved analytically) - EXAMPLE: + EXAMPLES: Analytical expression of the trajectory of a charged particle in a uniform, stationnary magnetic field:: @@ -889,7 +908,7 @@ def solve(self, step=None, method=None, solution_key=None, - list of the numerical points of the solution computed - EXAMPLE: + EXAMPLES: Computing a numerical solution:: @@ -1269,7 +1288,7 @@ def solution(self, solution_key=None, verbose=False): - list of the numerical points of the solution requested - EXAMPLE: + EXAMPLES: Requesting a numerical solution previously computed:: @@ -1343,7 +1362,7 @@ def interpolate(self, solution_key=None, method=None, - built interpolation object - EXAMPLE: + EXAMPLES: Interpolating a numerical solution previously computed:: @@ -1457,7 +1476,7 @@ def interpolation(self, interpolation_key=None, verbose=False): - requested interpolation object - EXAMPLE: + EXAMPLES: Requesting an interpolation object previously computed:: @@ -1611,7 +1630,7 @@ def tangent_vector_eval_at(self, t, - :class:`~sage.manifolds.differentiable.tangent_vector.TangentVector` tangent vector with numerical components - EXAMPLE: + EXAMPLES: Evaluating a vector tangent to the curve:: @@ -1683,8 +1702,7 @@ def tangent_vector_eval_at(self, t, for coordinate_curve_spline in interpolation] # by # default, order=1 in method 'derivative' of a class Spline basis = self._chart.frame().at(p) - v = Tp(evaluated_tgt_vec_comp, basis=basis) - return v + return Tp(evaluated_tgt_vec_comp, basis=basis) raise TypeError("unexpected type of interpolation object") @@ -1717,14 +1735,14 @@ def plot_integrated(self, chart=None, ambient_coords=None, some tangent vectors should also be plotted - ``color_tangent`` -- (default: ``blue``) color of the tangent vectors when these are plotted - - ``plot_points_tangent`` -- (default: 75) number of tangent + - ``plot_points_tangent`` -- (default: 10) number of tangent vectors to display when these are plotted - ``width_tangent`` -- (default: 1) sets the width of the arrows representing the tangent vectors - ``scale`` -- (default: 1) scale applied to the tangent vectors before displaying them - EXAMPLE: + EXAMPLES: Trajectory of a particle of unit mass and unit charge in an unit, axial, uniform, stationnary magnetic field:: @@ -2279,7 +2297,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - EXAMPLE: + EXAMPLES: Autoparallel curves associated with the Mercator projection of the unit 2-sphere :MATH:`\mathbb{S}^{2}`. @@ -2324,7 +2342,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): zero w.r.t. this frame:: sage: nab = S2.affine_connection('nab') - sage: nab.set_coef(epolar_ON)[:] + sage: nab.set_coef(frame=epolar_ON)[:] [[[0, 0], [0, 0]], [[0, 0], [0, 0]]] This connection is such that two vectors are parallel if their @@ -2443,7 +2461,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') - nab.set_coef(epolar_ON)[:] + nab.set_coef(frame=epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') p = S2.point((th0, ph0), name='p') Tp = S2.tangent_space(p) @@ -2505,7 +2523,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') - nab.set_coef(epolar_ON)[:] + nab.set_coef(frame=epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') p = S2.point((th0, ph0), name='p') Tp = S2.tangent_space(p) @@ -2564,7 +2582,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') - nab.set_coef(epolar_ON)[:] + nab.set_coef(frame=epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') p = S2.point((th0, ph0), name='p') Tp = S2.tangent_space(p) @@ -2604,7 +2622,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') - nab.set_coef(epolar_ON)[:] + nab.set_coef(frame=epolar_ON)[:] [t,tmin,tmax,th0,ph0,v_th0,v_ph0]=var('t tmin tmax th0 ph0 v_th0 v_ph0') p = S2.point((th0, ph0), name='p') Tp = S2.tangent_space(p) @@ -2695,7 +2713,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') - nab.set_coef(epolar_ON)[:] + nab.set_coef(frame=epolar_ON)[:] [t, tmin, tmax, th0, ph0] = var('t tmin tmax th0 ph0') [v_th0, v_ph0, alpha] = var('v_th0 v_ph0 alpha') p = S2.point((th0, ph0), name='p') @@ -2745,7 +2763,7 @@ class IntegratedAutoparallelCurve(IntegratedCurve): ch_basis[1,1], ch_basis[2,2] = 1, 1/sin(th) epolar_ON = epolar.new_frame(ch_basis, 'epolar_ON') nab = S2.affine_connection('nab') - nab.set_coef(epolar_ON)[:] + nab.set_coef(frame=epolar_ON)[:] [t, tmin, tmax, th0, ph0] = var('t tmin tmax th0 ph0') [v_th0, v_ph0, alpha] = var('v_th0 v_ph0 alpha') p = S2.point((th0, ph0), name='p') @@ -2815,7 +2833,7 @@ def __init__(self, parent, affine_connection, curve_parameter, i0 = parent.codomain().start_index() equations_rhs = [] - gamma = affine_connection.coef() + gamma = affine_connection.coef(frame=chart.frame()) for alpha in range(dim): rhs = 0 @@ -2923,7 +2941,7 @@ def system(self, verbose=False): - list containing the attributes :attr:`equations_rhs`, :attr:`initial_tangent_vector` and :attr:`chart` - EXAMPLE: + EXAMPLES: System defining an autoparallel curve:: @@ -3045,7 +3063,7 @@ class IntegratedGeodesic(IntegratedAutoparallelCurve): - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - EXAMPLE: + EXAMPLES: Geodesics of the unit 2-sphere :MATH:`\mathbb{S}^{2}`. Start with declaring the standard polar coordinates @@ -3299,7 +3317,7 @@ def system(self, verbose=False): - list containing the attributes :attr:`equations_rhs`, :attr:`initial_tangent_vector` and :attr:`chart` - EXAMPLE: + EXAMPLES: System defining a geodesic:: diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 412ca055671..ed4c23120bb 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -2608,18 +2608,12 @@ def integrated_curve(self, equations_rhs, velocities, curve_param, - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - - ``is_isomorphism`` -- (default: ``False``) determines whether the - constructed object is a diffeomorphism; if set to ``True``, - then `M` must have dimension one - - ``is_identity`` -- (default: ``False``) determines whether the - constructed object is the identity map; if set to ``True``, - then `M` must coincide with the domain of the curve OUTPUT: - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` - EXAMPLE: + EXAMPLES: Trajectory of a particle of unit mass and unit charge in a unit, uniform, stationnary magnetic field:: @@ -2730,18 +2724,12 @@ def integrated_autoparallel_curve(self, affine_connection, - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - - ``is_isomorphism`` -- (default: ``False``) determines whether the - constructed object is a diffeomorphism; if set to ``True``, - then `M` must have dimension one - - ``is_identity`` -- (default: ``False``) determines whether the - constructed object is the identity map; if set to ``True``, - then `M` must coincide with the domain of the curve OUTPUT: - :class:`~sage.manifolds.differentiable.curve.IntegratedAutoparallelCurve` - EXAMPLE: + EXAMPLES: Autoparallel curves associated with the Mercator projection of the unit 2-sphere :MATH:`\mathbb{S}^{2}`:: @@ -2869,18 +2857,12 @@ def integrated_geodesic(self, metric, curve_param, - ``name`` -- (default: ``None``) string; symbol given to the curve - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the curve; if none is provided, ``name`` will be used - - ``is_isomorphism`` -- (default: ``False``) determines whether the - constructed object is a diffeomorphism; if set to ``True``, - then `M` must have dimension one - - ``is_identity`` -- (default: ``False``) determines whether the - constructed object is the identity map; if set to ``True``, - then `M` must coincide with the domain of the curve OUTPUT: - :class:`~sage.manifolds.differentiable.curve.IntegratedGeodesic` - EXAMPLE: + EXAMPLES: Geodesics of the unit 2-sphere :MATH:`\mathbb{S}^{2}`:: diff --git a/src/sage/manifolds/differentiable/manifold_homset.py b/src/sage/manifolds/differentiable/manifold_homset.py index e1953b7b0b1..8985e70f23c 100644 --- a/src/sage/manifolds/differentiable/manifold_homset.py +++ b/src/sage/manifolds/differentiable/manifold_homset.py @@ -537,7 +537,7 @@ class IntegratedCurveSet(DifferentiableCurveSet): denote the set of integrated curves; if ``None``, `\mathrm{Hom_{integrated}}(I,M)` will be used - EXAMPLE: + EXAMPLES: This parent class needs to be imported:: @@ -790,7 +790,7 @@ def _element_constructor_(self, equations_rhs, velocities, - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` - EXAMPLE:: + EXAMPLES:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet sage: M = Manifold(2, 'M') @@ -822,7 +822,7 @@ def _an_element_(self): - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve` - EXAMPLE:: + EXAMPLES:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedCurveSet sage: M = Manifold(2, 'M') @@ -882,9 +882,7 @@ def _an_element_(self): from sage.categories.homset import Hom from sage.functions.trig import sin - from sage.functions.trig import cos from sage.symbolic.ring import var - from sage.symbolic.expression import Expression dom = self.domain() t = dom.canonical_coordinate() @@ -1008,7 +1006,7 @@ class IntegratedAutoparallelCurveSet(IntegratedCurveSet): the set of integrated autoparallel curves; if ``None``, `\mathrm{Hom_{autoparallel}}(I,M)` will be used - EXAMPLE: + EXAMPLES: This parent class needs to be imported:: @@ -1057,22 +1055,22 @@ class IntegratedAutoparallelCurveSet(IntegratedCurveSet): differentiable manifold M sage: sys = d.system(verbose=True) Autoparallel curve in the 2-dimensional differentiable manifold - M equipped with Affine connection nab on the Open subset U of - the 2-dimensional differentiable manifold M, and integrated - over the Real interval (-1, 2) as a solution to the following - equations, written w.r.t. Chart (M, (x, y)): + M equipped with Affine connection nab on the 2-dimensional + differentiable manifold M, and integrated over the Real + interval (-1, 2) as a solution to the following equations, + written w.r.t. Chart (M, (x, y)): Initial point: Point on the 2-dimensional differentiable - manifold M with coordinates [0.0312500000000000, -1/2] w.r.t. + manifold M with coordinates [0, -1/2] w.r.t. Chart (M, (x, y)) Initial tangent vector: Tangent vector at Point on the 2-dimensional differentiable manifold M with components - [0.156250000000000, 2.07698341289763] w.r.t. Chart (M, (x, y)) + [-1/6/(e^(-1) - 1), 1/3] w.r.t. Chart (M, (x, y)) d(x)/dt = Dx d(y)/dt = Dy - d(Dx)/dt = 0 - d(Dy)/dt = -Dx*Dy*cos(x)/sin(x) + d(Dx)/dt = -Dx*Dy + d(Dy)/dt = 0 The test suite is passed:: @@ -1116,19 +1114,19 @@ class IntegratedAutoparallelCurveSet(IntegratedCurveSet): Integrated autoparallel curve in the Real interval (a, b) sage: sys = g.system(verbose=True) Autoparallel curve in the Real interval (a, b) equipped with - Affine connection nab on the Open subset U of the Real number - line R, and integrated over the Real interval (a, b) as a - solution to the following equations, written w.r.t. - Chart ((a, b), (t,)): + Affine connection nab on the Real interval (a, b), and + integrated over the Real interval (a, b) as a solution to the + following equations, written w.r.t. Chart ((a, b), (t,)): Initial point: Point on the Real number line R with coordinates - [1/200] w.r.t. Chart ((a, b), (t,)) + [0] w.r.t. Chart ((a, b), (t,)) Initial tangent vector: Tangent vector at Point on the Real - number line R with components [-9999/400/(a - b)] w.r.t. + number line R with components + [-(e^(1/2) - 1)/(a - b)] w.r.t. Chart ((a, b), (t,)) d(t)/ds = Dt - d(Dt)/ds = -Dt^2/t + d(Dt)/ds = -Dt^2 The test suite is passed, tests '_test_one' and '_test_prod' being @@ -1238,7 +1236,7 @@ def _element_constructor_(self, affine_connection, curve_parameter, - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` - EXAMPLE:: + EXAMPLES:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet sage: M = Manifold(2, 'M') @@ -1272,80 +1270,78 @@ def _an_element_(self): - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` - EXAMPLE:: + EXAMPLES:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedAutoparallelCurveSet sage: M = Manifold(2, 'M') sage: X. = M.chart() sage: R. = RealLine() - sage: I = R.open_interval(-1, 2) - sage: H = IntegratedAutoparallelCurveSet(I, M) + sage: [a,b] = var('a b') + sage: J = R.open_interval(a, b) + sage: H = IntegratedAutoparallelCurveSet(J, M) sage: c = H._an_element_() ; c Integrated autoparallel curve in the 2-dimensional differentiable manifold M sage: sys = c.system(verbose=True) Autoparallel curve in the 2-dimensional differentiable - manifold M equipped with Affine connection nab on the Open - subset U of the 2-dimensional differentiable manifold M, - and integrated over the Real interval (-1, 2) as a solution - to the following equations, written w.r.t. - Chart (M, (x, y)): + manifold M equipped with Affine connection nab on the + 2-dimensional differentiable manifold M, and integrated + over the Real interval (a, b) as a solution to the + following equations, written w.r.t. Chart (M, (x, y)): Initial point: Point on the 2-dimensional differentiable - manifold M with coordinates [0.0312500000000000, -1/2] + manifold M with coordinates [0, -1/2] w.r.t. Chart (M, (x, y)) Initial tangent vector: Tangent vector at Point on the 2-dimensional differentiable manifold M with components - [0.156250000000000, 2.07698341289763] - w.r.t. Chart (M, (x, y)) + [1/2/((a - b)*(e^(-1) - 1)), -1/(a - b)] w.r.t. + Chart (M, (x, y)) d(x)/dt = Dx d(y)/dt = Dy - d(Dx)/dt = 0 - d(Dy)/dt = -Dx*Dy*cos(x)/sin(x) + d(Dx)/dt = -Dx*Dy + d(Dy)/dt = 0 - sage: sol = c.solve() + sage: sol = c.solve(parameters_values={a:0,b:4}) sage: interp = c.interpolate() sage: p = c(1) ; p Point on the 2-dimensional differentiable manifold M sage: p.coordinates() - (0.34375000000000056, 0.49999813529956155) + (0.1749660043664451, -0.2499999999999998) + sage: I = R.open_interval(-1, 2) sage: H = IntegratedAutoparallelCurveSet(I, I) sage: c = H._an_element_() ; c Integrated autoparallel curve in the Real interval (-1, 2) sage: sys = c.system(verbose=True) Autoparallel curve in the Real interval (-1, 2) equipped - with Affine connection nab on the Open subset U of the Real - number line R, and integrated over the Real - interval (-1, 2) as a solution to the following equations, - written w.r.t. Chart ((-1, 2), (t,)): + with Affine connection nab on the Real interval (-1, 2), + and integrated over the Real interval (-1, 2) as a solution + to the following equations, written w.r.t. + Chart ((-1, 2), (t,)): Initial point: Point on the Real number line R with coordinates [1/2] w.r.t. Chart ((-1, 2), (t,)) Initial tangent vector: Tangent vector at Point on the Real - number line R with components [7/16] w.r.t. + number line R with components [1/3*e^(3/4) - 1/3] w.r.t. Chart ((-1, 2), (t,)) d(t)/ds = Dt - d(Dt)/ds = -Dt^2/t + d(Dt)/ds = -Dt^2 sage: sol = c.solve() sage: interp = c.interpolate() sage: p = c(1) ; p Point on the Real number line R sage: p.coordinates() - (1.0606601601128764,) + (1.0565635215890166,) """ - from sage.categories.homset import Hom - from sage.symbolic.ring import var - from sage.functions.trig import sin - from sage.functions.trig import tan - from sage.functions.log import ln - from sage.symbolic.constants import pi from sage.rings.infinity import Infinity from sage.rings.rational_field import QQ + from sage.categories.homset import Hom + from sage.symbolic.ring import var + from sage.functions.log import exp dom = self.domain() t = dom.canonical_coordinate() @@ -1357,7 +1353,7 @@ def _an_element_(self): dim = codom.dim() i0 = codom.start_index() chart2 = codom.default_chart() - th = chart2[:][0] + x = chart2[:][0] # In case the codomain coincides with the domain, # it is important to distinguish between the canonical # coordinate, and the curve parameter since, in such a @@ -1383,123 +1379,75 @@ def _an_element_(self): # where a certain integrated autoparallel curve may be defined: H = Hom(dom, codom) c = H.an_element() - th_A = c.expr()[0].substitute({t:1}) - th_B = c.expr()[0].substitute({t:0}) # necessarily, th_A < th_B + x_A = c.expr()[0].substitute({t:1}) + x_B = c.expr()[0].substitute({t:0}) # necessarily, x_A < x_B if dim == 1: - # Redefine 'th_A' and 'th_B' so that they are of the same - # sign and such that th_A < th_B still holds. - # This will avoid division by zero in the equations - # defining autoparallel curves w.r.t. the connection defined - # below - if th_A.numerical_approx().sign() != th_B.numerical_approx().sign(): - # knowing that th_A < th_B - if th_B > 0: - th_A = th_B/100 - else: # th_A is necessarily strictly less than 0 - th_B = th_A/100 + nab = codom.affine_connection('nab') + nab.set_coef()[i0,i0,i0] = 1 # The initial point: - p = codom.point([th_A]) + p = codom.point([x_A]) # The initial tangent vector: - th_dot_A = (th_B**2-th_A**2)/(2*th_A*(t_max-t_min)) - v = codom.tangent_space(p)([th_dot_A]) - - U = codom.open_subset('U') - chart2_U = chart2.restrict(U, [th > th_A, th < th_B]) # this - # is to prevent from problems arising from a division by - # zero in the equations defining autoparallel curves w.r.t. - # the connection defined below - nab = U.affine_connection('nab') - nab.set_coef()[i0,i0,i0] = 1/th - return self.element_class(self, nab, param, v) # the - # autoparallel curve returned will correspond to the + x_dot_A = (exp(x_B - x_A) - 1)/(t_max - t_min) + v = codom.tangent_space(p)([x_dot_A]) + + return self.element_class(self, nab, param, v) + # the autoparallel curve returned will correspond to the # following analytical solution: - # th(t) = (th_A*(th_A + 2*th_dot_A*(t-t_min)))^(1/2) + # x(t) = ln( x_dot_A*(t-t_min) + 1 ) + x_A, which is such + # that x(t_min) = x_A and x(t_max) = x_B due to x_dot_A + # set to the value above # else: (i.e. dim >= 2) - # in dimension greater than 1, the idea is to consider the first - # two coordinates of the chart to be the polar coordinates on the - # unit 2-sphere, in order to reproduce autoparallel curves on - # the sphere with respect to the Mercator connection (which is - # described in the example introducing the class - # IntegratedAutoparallelCurve) - - # Redefine 'th_A' and 'th_B' so that [th_A, th_B] is contained - # within an interval (k*pi, (k+1)*pi), k being an integer. - # This is needed to take the inverse of the sine on [th_A, th_B] - # and to avoid problems from evaluating the tangent of 'th/2', - # which appear in the equations: - - # th_A = k_A*pi + r_A, k_A integer, 0 <= r_A < pi - # idem for th_B - r_A = th_A.numerical_approx()%pi.numerical_approx() - r_B = th_B.numerical_approx()%pi.numerical_approx() - k_A = (th_A-r_A)/pi - k_B = (th_B-r_B)/pi - if k_A == k_B and r_A == 0: - th_A = k_A*pi + (th_B-th_A)/16 - elif k_A != k_B: - th_B = (k_A+1)*pi - 1/16*((k_A+1)*pi - th_A) - if r_A == 0: - th_A = k_A*pi + (th_B-th_A)/16 - - bounds = chart2._bounds[1] # bounds of second coordinate - # Determination of an interval (x1, x2) arround target_point: - ph_min = bounds[0][0] - ph_max = bounds[1][0] + nab = codom.affine_connection('nab') + nab.set_coef()[i0,i0,i0+1] = 1 + + y_bounds = chart2._bounds[1] # bounds of second coordinate + # Determination of an interval (y_A, y_B) arround target_point: + y_min = y_bounds[0][0] + y_max = y_bounds[1][0] one_half = QQ(1) / QQ(2) - if ph_min == -Infinity: - if ph_max == Infinity: - ph_A = - one_half - ph_B = one_half + if y_min == -Infinity: + if y_max == Infinity: + y_A = - one_half + y_B = one_half else: - ph_A = ph_max - 3*one_half - ph_B = ph_max - one_half + y_A = y_max - 3*one_half + y_B = y_max - one_half else: - if ph_max == Infinity: - ph_A = ph_min + one_half - ph_B = ph_min + 3*one_half + if y_max == Infinity: + y_A = y_min + one_half + y_B = y_min + 3*one_half else: - dph = (ph_max - ph_min) / 4 - ph_A = ph_min + dph - ph_B = ph_max - dph + dy = (y_max - y_min) / 4 + y_A = y_min + dy + y_B = y_max - dy # The initial point: - p_coords = [th_A] + [ph_A] + list(c.expr()[2:dim]) + p_coords = [x_A] + [y_A] + list(c.expr()[2:dim]) p = codom.point(p_coords) # The initial tangent vector: - th_dot_A = (th_B - th_A)/(t_max - t_min) - num_tan_alpha = ln(tan(th_A/2))-ln(tan((th_dot_A*t_max+th_A)/2)) - denom_tan_alpha = ph_B - ph_A - tan_alpha = num_tan_alpha/denom_tan_alpha - ph_dot_A = - th_dot_A/(tan_alpha * sin(th_A)) - v_comps = [th_dot_A] + [ph_dot_A] + [0 for i in range(dim-2)] + y_dot_A = (y_B - y_A) / (t_max - t_min) + x_dot_A = y_dot_A*(x_B - x_A) / (1-exp(-y_dot_A*(t_max-t_min))) + v_comps = [x_dot_A] + [y_dot_A] + [0 for i in range(dim-2)] v = codom.tangent_space(p)(v_comps) - # The Mercator connection is reproduced on the first two - # coordinates - U = codom.open_subset('U') - chart2_U = chart2.restrict(U, [th > th_A, th < th_B]) # this is - # to prevent from problems arising from defining the orhtonormal - # basis 'epolar_ON' with the inverse of a sine - epolar_U = chart2_U.frame() - ch_basis = U.automorphism_field() - ch_basis[i0,i0], ch_basis[i0+1,i0+1] = 1, 1/sin(th) - if dim > 2: - for i in range(2,dim): - ch_basis[i0+i,i0+i] = 1 - epolar_ON_U = epolar_U.new_frame(ch_basis, 'epolar_ON') - nab = U.affine_connection('nab') # the connection is only - # defined on U since it was defined with respect to epolar_ON_U, - # which was defined on U only - nab.set_coef(epolar_ON_U)[:] # this sets all the coefficients to - # zero w.r.t. 'epolar_ON' - return self.element_class(self, nab, param, v) + # the autoparallel curve returned will correspond to the + # following analytical solution: + # all coordinates other than the first two coordinates are + # constant, and + # x(t) = x_dot_A/y_dot_A*(1 - exp(-y_dot_A*(t-t_min))) + x_A + # y(t) = y_dot_A*(t-t_min) + y_A + # This solution is such that + # x(t_min) = x_A and x(t_max) = x_B due to x_dot_A set to the + # value above, and + # y(t_min) = y_A and y(t_max) = y_B due to y_dot_A set to the + # value above #****************************************************************************** @@ -1522,7 +1470,7 @@ class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): the set of integrated geodesics; if ``None``, `\mathrm{Hom_{geodesic}}(I,M)` will be used - EXAMPLE: + EXAMPLES: This parent class needs to be imported:: @@ -1574,16 +1522,16 @@ class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): Chart (M, (x, y)): Initial point: Point on the 2-dimensional differentiable - manifold M with coordinates [0.0312500000000000, 0] w.r.t. + manifold M with coordinates [0, 0] w.r.t. Chart (M, (x, y)) Initial tangent vector: Tangent vector at Point on the 2-dimensional differentiable manifold M with components - [0.156250000000000, 0] w.r.t. Chart (M, (x, y)) + [1/3*e^(1/2) - 1/3, 0] w.r.t. Chart (M, (x, y)) d(x)/dt = Dx d(y)/dt = Dy - d(Dx)/dt = Dy^2*cos(x)*sin(x) - d(Dy)/dt = -2*Dx*Dy*cos(x)/sin(x) + d(Dx)/dt = -Dx^2 + d(Dy)/dt = 0 The test suite is passed:: @@ -1626,19 +1574,18 @@ class IntegratedGeodesicSet(IntegratedAutoparallelCurveSet): Integrated geodesic in the Real interval (a, b) sage: sys = g.system(verbose=True) Geodesic in the Real interval (a, b) equipped with Riemannian - metric g on the Open subset U of the Real number line R, and - integrated over the Real interval (a, b) as a solution to the - following geodesic equations, written w.r.t. - Chart ((a, b), (t,)): + metric g on the Real interval (a, b), and integrated over the + Real interval (a, b) as a solution to the following geodesic + equations, written w.r.t. Chart ((a, b), (t,)): Initial point: Point on the Real number line R with coordinates - [1/200] w.r.t. Chart ((a, b), (t,)) + [0] w.r.t. Chart ((a, b), (t,)) Initial tangent vector: Tangent vector at Point on the Real - number line R with components [-9999999999/1000/(a - b)] w.r.t. + number line R with components [-(e^(1/2) - 1)/(a - b)] w.r.t. Chart ((a, b), (t,)) d(t)/ds = Dt - d(Dt)/ds = -4*Dt^2/t + d(Dt)/ds = -Dt^2 The test suite is passed, tests '_test_one' and '_test_prod' being @@ -1744,7 +1691,7 @@ def _element_constructor_(self, metric, curve_parameter, - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic` - EXAMPLE:: + EXAMPLES:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedGeodesicSet sage: M = Manifold(2, 'M') @@ -1776,14 +1723,15 @@ def _an_element_(self): - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic` - EXAMPLE:: + EXAMPLES:: sage: from sage.manifolds.differentiable.manifold_homset import IntegratedGeodesicSet sage: M = Manifold(4, 'M', start_index=1) sage: X. = M.chart() sage: R. = RealLine() - sage: I = R.open_interval(-1, 2) - sage: H = IntegratedGeodesicSet(I, M) + sage: [a,b] = var('a b') + sage: J = R.open_interval(a, b) + sage: H = IntegratedGeodesicSet(J, M) sage: c = H._an_element_() ; c Integrated geodesic in the 4-dimensional differentiable manifold M @@ -1791,63 +1739,64 @@ def _an_element_(self): Geodesic in the 4-dimensional differentiable manifold M equipped with Riemannian metric g on the 4-dimensional differentiable manifold M, and integrated over the Real - interval (-1, 2) as a solution to the following geodesic + interval (a, b) as a solution to the following geodesic equations, written w.r.t. Chart (M, (w, x, y, z)): Initial point: Point on the 4-dimensional differentiable - manifold M with coordinates [0.0312500000000000, 0, 0, 0] - w.r.t. Chart (M, (w, x, y, z)) + manifold M with coordinates [0, 0, 0, 0] w.r.t. + Chart (M, (w, x, y, z)) Initial tangent vector: Tangent vector at Point on the 4-dimensional differentiable manifold M with components - [0.156250000000000, 0, 0, 0] w.r.t. Chart (M, (w, x, y, z)) + [-(e^(1/2) - 1)/(a - b), 0, 0, 0] w.r.t. + Chart (M, (w, x, y, z)) d(w)/dt = Dw d(x)/dt = Dx d(y)/dt = Dy d(z)/dt = Dz - d(Dw)/dt = Dx^2*cos(w)*sin(w) - d(Dx)/dt = -2*Dw*Dx*cos(w)/sin(w) + d(Dw)/dt = -Dw^2 + d(Dx)/dt = 0 d(Dy)/dt = 0 d(Dz)/dt = 0 - sage: sol = c.solve() + sage: sol = c.solve(parameters_values={a:1,b:6}) sage: interp = c.interpolate() - sage: p = c(1) ; p + sage: p = c(3) ; p Point on the 4-dimensional differentiable manifold M sage: p.coordinates() - (0.34375000000000056, 0.0, 0.0, 0.0) + (0.2307056927167852, 0.0, 0.0, 0.0) + sage: I = R.open_interval(-1, 2) sage: H = IntegratedGeodesicSet(I, I) sage: c = H._an_element_() ; c Integrated geodesic in the Real interval (-1, 2) sage: sys = c.system(verbose=True) Geodesic in the Real interval (-1, 2) equipped with - Riemannian metric g on the Open subset U of the Real number - line R, and integrated over the Real interval (-1, 2) as a - solution to the following geodesic equations, written - w.r.t. Chart ((-1, 2), (t,)): + Riemannian metric g on the Real interval (-1, 2), and + integrated over the Real interval (-1, 2) as a solution to + the following geodesic equations, written w.r.t. + Chart ((-1, 2), (t,)): Initial point: Point on the Real number line R with coordinates [1/2] w.r.t. Chart ((-1, 2), (t,)) Initial tangent vector: Tangent vector at Point on the Real - number line R with components [1031/320] w.r.t. + number line R with components [1/3*e^(3/4) - 1/3] w.r.t. Chart ((-1, 2), (t,)) d(t)/ds = Dt - d(Dt)/ds = -4*Dt^2/t + d(Dt)/ds = -Dt^2 sage: sol = c.solve() sage: interp = c.interpolate() sage: p = c(1) ; p Point on the Real number line R sage: p.coordinates() - (1.1495758053215723,) + (1.0565635215890166,) """ from sage.categories.homset import Hom from sage.symbolic.ring import var - from sage.functions.trig import sin - from sage.symbolic.constants import pi + from sage.functions.log import exp dom = self.domain() t = dom.canonical_coordinate() @@ -1859,7 +1808,7 @@ def _an_element_(self): dim = codom.dim() i0 = codom.start_index() chart2 = codom.default_chart() - th = chart2[:][0] + x = chart2[:][0] # In case the codomain coincides with the domain, # it is important to distinguish between the canonical # coordinate, and the curve parameter since, in such a @@ -1885,82 +1834,28 @@ def _an_element_(self): # where a certain integrated autoparallel curve may be defined: H = Hom(dom, codom) c = H.an_element() - th_A = c.expr()[0].substitute({t:1}) - th_B = c.expr()[0].substitute({t:0}) # necessarily, th_A < th_B + x_A = c.expr()[0].substitute({t:1}) + x_B = c.expr()[0].substitute({t:0}) # necessarily, x_A < x_B - if dim == 1: - # Redefine 'th_A' and 'th_B' so that they are of the same - # sign and such that th_A < th_B still holds. - # This will avoid division by zero in the equations - # defining geodesics w.r.t. the metric defined below. - if th_A.numerical_approx().sign() != th_B.numerical_approx().sign(): - # knowing that th_A < th_B - if th_B > 0: - th_A = th_B/100 - else: # th_A is necessarily strictly less than 0 - th_B = th_A/100 - - # The initial point: - p = codom.point([th_A]) - - # The initial tangent vector: - th_dot_A = (th_B**5-th_A**5)/(5*(th_A**4)*(t_max-t_min)) - v = codom.tangent_space(p)([th_dot_A]) - - U = codom.open_subset('U') - chart2_U = chart2.restrict(U, [th > th_A, th < th_B]) # this - # is to prevent from problems arising from a division by - # zero in the equations defining autoparallel curves w.r.t. - # the connection defined below - g = U.metric('g') - g[i0,i0] = th**8 - return self.element_class(self, g, param, v) # the - # autoparallel curve returned will correspond to the - # following analytical solution: - # th(t) = (th_A^4*(th_A + 5*th_dot_A*(t-t_min)))^(1/5) - - # else: (i.e. dim >= 2) - - # in dimension greater than 1, the idea is to consider the first - # two coordinates of the chart to be the polar coordinates on - # the unit 2-sphere, in order to reproduce a line of longitude, - # which is a geodesic on the sphere w.r.t. the standard metric - # induced by the 3D Euclidean metric on the sphere. - - # Redefine 'th_A' and 'th_B' so that [th_A, th_B] is contained - # within an interval (k*pi, (k+1)*pi), k being an integer. - # This is needed to take the inverse of the tangent function on - # [th_A, th_B] in the geodesic equations: - - # th_A = k_A*pi + r_A, k_A integer, 0 <= r_A < pi - # idem for th_B - r_A = th_A.numerical_approx()%pi.numerical_approx() - r_B = th_B.numerical_approx()%pi.numerical_approx() - k_A = (th_A-r_A)/pi - k_B = (th_B-r_B)/pi - if k_A == k_B and r_A == 0: - th_A = k_A*pi + (th_B-th_A)/16 - elif k_A != k_B: - th_B = (k_A+1)*pi - 1/16*((k_A+1)*pi - th_A) - if r_A == 0: - th_A = k_A*pi + (th_B-th_A)/16 + g = codom.metric('g') + g[i0,i0] = exp(2*x) + if dim > 1: + for i in range(1,dim): + g[i0+i,i0+i] = 1 # The initial point: - p_coords = [th_A] + list(c.expr()[1:dim]) + p_coords = [x_A] + list(c.expr()[1:dim]) p = codom.point(p_coords) # The initial tangent vector: - th_dot_A = (th_B - th_A)/(t_max - t_min) - v_comps = [th_dot_A] + [0 for i in range(dim-1)] + x_dot_A = (exp(x_B - x_A) - 1)/(t_max - t_min) + v_comps = [x_dot_A] + [0 for i in range(dim-1)] v = codom.tangent_space(p)(v_comps) - # The metric of the unit 2-sphere is reproduced on the first two - # coordinates - g = codom.metric('g') - g[i0,i0] = 1 - g[i0+1,i0+1] = sin(th)**2 - if dim > 2: - for i in range(2,dim): - g[i0+i,i0+i] = 1 - return self.element_class(self, g, param, v) + # the geodesic returned will correspond to the following + # analytical solution: + # all coordinates other than the first one are constant, and + # x(t) = ln( x_dot_A*(t-t_min) + 1 ) + x_A, which is such that + # x(t_min) = x_A and x(t_max) = x_B due to x_dot_A set to the + # value above From 30609f6e5167ae197b9519813581f86ae5ba6194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 13 Jul 2017 10:43:25 +0200 Subject: [PATCH 059/184] trac 23408 trying to use richmp for algebraic closure of finite fields --- .../rings/algebraic_closure_finite_field.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index 86fa5e8c37e..24cdc6f74c3 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -63,6 +63,8 @@ from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.rings.ring import Field from sage.structure.element import FieldElement +from sage.structure.richcmp import richcmp_method, rich_to_bool, richcmp + class AlgebraicClosureFiniteFieldElement(FieldElement): """ @@ -164,7 +166,7 @@ def _repr_(self): """ return self._value._repr_() - def __cmp__(self, right): + def _richcmp_(self, right, op): """ Compare ``self`` with ``right``. @@ -176,7 +178,7 @@ def __cmp__(self, right): """ x, y = self.parent()._to_common_subfield(self, right) - return cmp(x, y) + return richcmp(x, y, op) def __pow__(self, exp): r""" @@ -540,6 +542,7 @@ def as_finite_field_element(self, minimal=False): return (F, x, phi) +@richcmp_method class AlgebraicClosureFiniteField_generic(Field): """ Algebraic closure of a finite field. @@ -558,7 +561,7 @@ def __init__(self, base_ring, name, category=None): Field.__init__(self, base_ring=base_ring, names=name, normalize=False, category=category) - def __cmp__(self, other): + def __richcmp__(self, other, op): """ Compare ``self`` with ``other``. @@ -573,12 +576,13 @@ def __cmp__(self, other): """ if self is other: - return 0 - c = cmp(type(self), type(other)) - if c != 0: - return c - return cmp((self.base_ring(), self.variable_name(), self.category()), - (other.base_ring(), other.variable_name(), other.category())) + return rich_to_bool(op, 0) + if not isinstance(other, AlgebraicClosureFiniteField_generic): + return NotImplemented + return richcmp((self.base_ring(), self.variable_name(), + self.category()), + (other.base_ring(), other.variable_name(), + other.category()), op) def cardinality(self): """ From 7479f47d6514428ad0bd00269af33b2fb495354c Mon Sep 17 00:00:00 2001 From: Karim Van Aelst Date: Thu, 13 Jul 2017 19:02:42 +0200 Subject: [PATCH 060/184] 'init' of autoparallel_curve now robust to change of default chart, tiny corrections in documentation --- .../differentiable/integrated_curve.py | 53 +++++++++++-------- src/sage/manifolds/differentiable/manifold.py | 4 +- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/sage/manifolds/differentiable/integrated_curve.py b/src/sage/manifolds/differentiable/integrated_curve.py index e19afcf7dd5..a39886bec16 100644 --- a/src/sage/manifolds/differentiable/integrated_curve.py +++ b/src/sage/manifolds/differentiable/integrated_curve.py @@ -205,11 +205,11 @@ class IntegratedCurve(DifferentiableCurve): ....: solution_key='carac time 1', verbose=True) Performing 4th order Runge-Kutta integration with Maxima by default... - Numerical integration completed. - Checking all points are in the chart domain... + Checking all points are in the chart domain... All points are in the chart domain. + The resulting list of points was associated with the key 'carac time 1' (if this key already referred to a former numerical solution, such a solution was erased). @@ -940,11 +940,11 @@ def solve(self, step=None, method=None, solution_key=None, default... Resulting list of points will be associated with the key 'rk4_maxima' by default. - Numerical integration completed. - Checking all points are in the chart domain... + Checking all points are in the chart domain... All points are in the chart domain. + The resulting list of points was associated with the key 'rk4_maxima' (if this key already referred to a former numerical solution, such a solution was erased). @@ -1092,13 +1092,13 @@ def solve(self, step=None, method=None, solution_key=None, end_points=[t_min, t_max], step=step) - # Let q = (t_max-t_min)/step; then, the value of the - # curve parameter of the q+1-th point computed is very close - # to 't_max'. Yet, due to numerical rounding, it is very - # often a bit smaller than 't_max'. And it seems that, if - # the solver considers this value not to be close enough to - # 't_max', then the solver computes one more point, - # evaluated for a curve parameter exactly equal to 't_max'. + # The value of 'step' being set by the user when calling + # method 'solve', the value of (t_max - tmin)/step is not + # necessarily an integer. + # As a result, when the solver 'desolve_system_rk4' reaches + # a curve parameter that is distant to 't_max' by less than + # 'step', it computes one last point evaluated for a curve + # parameter exactly equal to 't_max'. # Therefore, the difference between the curve parameter # corresponding to this last point and that corresponding # to the previous one is strictly less than 'step'. If this @@ -1111,9 +1111,9 @@ def solve(self, step=None, method=None, solution_key=None, # solution when it is a point that was added by the solver # and threatens to be too close to the previous one # (arbitrarily, we consider two points to be too close if - # their curve parameters are separated by less than half a + # their curve parameters are separated by less than 90% of a # step). - if len(sol) > 1 and abs(sol[-1][0] - sol[-2][0]) < 0.5*step: + if len(sol) > 1 and abs(sol[-1][0] - sol[-2][0]) < 0.9*step: del sol[-1] elif method == "ode_int": @@ -1245,7 +1245,7 @@ def jacobian(t,y): coords_sol = [point[0:dim+1] for point in sol] if verbose: - print("\nNumerical integration completed.\n" + + print("Numerical integration completed.\n\n" + "Checking all points are in the chart domain...") N = len(coords_sol) @@ -1255,8 +1255,10 @@ def jacobian(t,y): if n < N: raise ValueError("the {}th point ".format(n) + - "of the numerical solution (obtained at " + - "time {}) is out ".format(t_min + n*step) + + "(initial point being the '0th' point) " + + "of the numerical solution (obtained " + + "for a curve parameter equal " + + "to {}) is out ".format(sol[n][0]) + "of the chart domain; a curve with a " + "smaller maximal value of the curve " + "parameter, or a smaller initial tangent "+ @@ -1264,7 +1266,7 @@ def jacobian(t,y): else: self._solutions[solution_key] = coords_sol if verbose: - print("\nAll points are in the chart domain.\n" + + print("All points are in the chart domain.\n\n" + "The resulting list of points was associated " + "with the key '{}' ".format(solution_key) + "(if this key already referred to a former " + @@ -2835,13 +2837,22 @@ def __init__(self, parent, affine_connection, curve_parameter, gamma = affine_connection.coef(frame=chart.frame()) - for alpha in range(dim): + for rho in range(dim): rhs = 0 for mu in range(dim): for nu in range(dim): - AUX = velocities[mu] * velocities[nu] - rhs-= gamma[alpha+i0, mu+i0, nu+i0].expr() * AUX - # 'AUX' only used for the line above to be shorter + vMUvNU = velocities[mu] * velocities[nu] + gammaRHO_mu_nu = gamma[[rho+i0, mu+i0, nu+i0]].expr(chart=chart) + # line above is the expression of the scalar + # field 'gamma[[rho+i0, mu+i0, nu+i0]]' in terms + # of 'chart' (here, in any point of the manifold, + # the scalar field 'gamma[[rho+i0, mu+i0, nu+i0]]' + # provides the coefficient [rho+i0, mu+i0, nu+i0] + # of the affine connection with respect to frame + # 'chart.frame()') + rhs -= gammaRHO_mu_nu * vMUvNU + # 'vMUvNU' and 'gammaRHO_mu_nu' only used for the + # line above to be shorter equations_rhs += [rhs.simplify_full()] IntegratedCurve.__init__(self, parent, equations_rhs, diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index ed4c23120bb..a8e47d0e538 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -2727,7 +2727,7 @@ def integrated_autoparallel_curve(self, affine_connection, OUTPUT: - - :class:`~sage.manifolds.differentiable.curve.IntegratedAutoparallelCurve` + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve` EXAMPLES: @@ -2860,7 +2860,7 @@ def integrated_geodesic(self, metric, curve_param, OUTPUT: - - :class:`~sage.manifolds.differentiable.curve.IntegratedGeodesic` + - :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic` EXAMPLES: From e3497e851f155914d54174ad18bd28123f6b37a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 14 Jul 2017 13:10:16 +0200 Subject: [PATCH 061/184] steps towards unicode doctesting framework --- src/sage/doctest/forker.py | 6 +++-- src/sage/doctest/parsing.py | 8 ++++-- src/sage/doctest/sources.py | 40 ++++++++++++++++++++++++++--- src/sage/structure/dynamic_class.py | 5 ++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 9a5d9822f12..705b7188168 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -514,7 +514,9 @@ def _run(self, test, compileflags, out): finally: if self.debugger is not None: self.debugger.set_continue() # ==== Example Finished ==== - got = self._fakeout.getvalue() # the actual output + got = self._fakeout.getvalue().decode('utf-8') + # the actual output + outcome = FAILURE # guilty until proved innocent or insane # If the example executed without raising any exceptions, @@ -768,7 +770,7 @@ def update_digests(self, example): sage: DTR.running_global_digest.hexdigest() '3cb44104292c3a3ab4da3112ce5dc35c' """ - s = pre_hash(get_source(example)) + s = pre_hash(get_source(example)).encode('utf-8') self.running_global_digest.update(s) self.running_doctest_digest.update(s) if example.predecessors is not None: diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 77a1c8f8488..01746f7f558 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -22,8 +22,9 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function, absolute_import +from __future__ import print_function, absolute_import, unicode_literals from sage.misc.six import u +from six import text_type import re import sys @@ -291,7 +292,7 @@ def reduce_hex(fingerprints): return "%032x" % res -class MarkedOutput(str): +class MarkedOutput(text_type): """ A subclass of string with context for whether another string matches it. @@ -306,6 +307,9 @@ class MarkedOutput(str): 'abc' sage: s.rel_tol 0.0500000000000000 + + sage: MarkedOutput(u"56 µs") + u'56 \xb5s' """ random = False rel_tol = 0 diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index c8bd433e4c7..f3e05a6654e 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -19,7 +19,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals import os import sys @@ -61,6 +61,9 @@ sagestart = re.compile(r"^\s*(>>> |sage: )\s*[^#\s]") untested = re.compile("(not implemented|not tested)") +# For parsing a PEP 0263 encoding declaration +pep_0263 = re.compile(r'coding[:=]\s*([-\w.]+)') + # Source line number in warning output doctest_line_number = re.compile(r"^\s*doctest:[0-9]") @@ -226,7 +229,7 @@ def _process_doc(self, doctests, doc, namespace, start): def _create_doctests(self, namespace, tab_okay=None): """ - Creates a list doctests defined in this source. + Creates a list of doctests defined in this source. This function collects functionality common to file and string sources, and is called by @@ -511,10 +514,13 @@ def __init__(self, path, options): base, ext = os.path.splitext(path) if ext in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx'): self.__class__ = dynamic_class('PythonFileSource',(FileDocTestSource,PythonSource)) + self.encoding = "utf-8" elif ext == '.tex': self.__class__ = dynamic_class('TexFileSource',(FileDocTestSource,TexSource)) + self.encoding = "utf-8" elif ext == '.rst': self.__class__ = dynamic_class('RestFileSource',(FileDocTestSource,RestSource)) + self.encoding = "utf-8" else: raise ValueError("unknown file extension %r"%ext) @@ -536,10 +542,38 @@ def __iter__(self): 1 sage: 2 + 2 2 4 3 ''' + + The encoding is "utf-8" by default:: + + sage: FDS.encoding + 'utf-8' + + We create a file with a Latin-1 encoding without declaring it:: + + sage: s = "'''\nRegardons le polyn\xF4me...\n'''\n" + sage: open(filename, 'w').write(s) + sage: FDS = FileDocTestSource(filename, DocTestDefaults()) + sage: L = list(FDS) + Traceback (most recent call last): + ... + UnicodeDecodeError: 'utf8' codec can't decode byte 0xf4 in position 18: invalid continuation byte + + This works if we add a PEP 0263 encoding declaration:: + + sage: s = "#!/usr/bin/env python\n# -*- coding: latin-1 -*-\n" + s + sage: open(filename, 'w').write(s) + sage: FDS = FileDocTestSource(filename, DocTestDefaults()) + sage: L = list(FDS) + sage: FDS.encoding + 'latin-1' """ with open(self.path) as source: for lineno, line in enumerate(source): - yield lineno, line + if lineno < 2: + match = pep_0263.search(line) + if match: + self.encoding = match.group(1) + yield lineno, unicode(line, self.encoding) @lazy_attribute def printpath(self): diff --git a/src/sage/structure/dynamic_class.py b/src/sage/structure/dynamic_class.py index 2269bd78e39..8edeb51cac8 100644 --- a/src/sage/structure/dynamic_class.py +++ b/src/sage/structure/dynamic_class.py @@ -123,6 +123,7 @@ class MyPermutation(UniqueRepresentation, PermutationCycleType, PosetElement, Gr from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.misc.inherit_comparison import InheritComparisonMetaclass, InheritComparisonClasscallMetaclass + def dynamic_class(name, bases, cls=None, reduction=None, doccls=None, prepend_cls_bases=True, cache=True): r""" @@ -309,6 +310,10 @@ def dynamic_class(name, bases, cls=None, reduction=None, doccls=None, """ bases = tuple(bases) #assert(len(bases) > 0 ) + try: + name = str(name) + except: + pass assert(isinstance(name, str)) # assert(cls is None or issubtype(type(cls), type) or type(cls) is classobj) if cache is True: From cc03d22738a327999db7162c095daac7d23f9c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 14 Jul 2017 15:31:51 +0200 Subject: [PATCH 062/184] motorhead detail --- src/sage/repl/rich_output/backend_ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 894b7d274e8..5085c3dca58 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -236,7 +236,7 @@ def displayhook(self, plain_text, rich_output): sage: from sage.repl.rich_output import get_display_manager sage: dm = get_display_manager() sage: dm.displayhook(Foo()) - ({u'text/plain': u'Mot\xc3\xb6rhead'}, {}) + ({u'text/plain': u'Mot\xf6rhead'}, {}) """ if isinstance(rich_output, OutputPlainText): return ({u'text/plain': rich_output.text.get_unicode()}, {}) From b091e6a60311ab5756d2fb744e60adabae120a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 14 Jul 2017 19:23:51 +0200 Subject: [PATCH 063/184] trac 14153 fixing another doctest --- src/sage/plot/graphics.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 2c8a48c1085..8fd3998927b 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -693,8 +693,7 @@ def axes_labels(self, l=None): sage: c = circle((0,0), 1) sage: c.axes_labels(['axe des abscisses', u'axe des ordonnées']) sage: c._axes_labels - ('axe des abscisses', u'axe des ordonn\xc3\xa9es') - + ('axe des abscisses', u'axe des ordonn\xe9es') """ if l is None: try: From 8394952acfe919aba99695f84248edbc212a3c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 14 Jul 2017 20:01:04 +0200 Subject: [PATCH 064/184] trac 14153 fixing another doctest --- src/sage/interfaces/r.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 9db603f8eb3..e9096dfd90c 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -668,7 +668,7 @@ def library(self, library_name): ... ImportError: ... """ - ret = self.eval('require("%s")'%library_name) + ret = self.eval('require("%s")'%library_name).decode('utf-8') # try hard to parse the message string in a locale-independent way if ' library(' in ret: # locale-independent key-word raise ImportError("%s"%ret) From 6ed83495fb93a53be35b5202d9e9ab483e8af737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 15 Jul 2017 16:32:56 +0200 Subject: [PATCH 065/184] better ascii and unicode art for plane partitions --- src/sage/combinat/plane_partition.py | 173 +++++++++++++++------------ 1 file changed, 97 insertions(+), 76 deletions(-) diff --git a/src/sage/combinat/plane_partition.py b/src/sage/combinat/plane_partition.py index f16f92e2ac1..8a72dd62b05 100644 --- a/src/sage/combinat/plane_partition.py +++ b/src/sage/combinat/plane_partition.py @@ -220,58 +220,81 @@ def _repr_diagram(self, show_box=False, use_unicode=False): EXAMPLES:: sage: print(PlanePartition([[4,3,3,1],[2,1,1],[1,1]])._repr_diagram()) - / \ - |\ /| - |\|/ \ - / \|\ / \ - |\ /|\|\ /| - / \|/ \|\|/| - |\ / \ / \|/ \ - \|\ /|\ /|\ /| - \|/ \|/ \|/ + __ + /\_\ + __/\/_/ + __/\_\/\_\ + /\_\/_/\/\_\ + \/\_\_\/\/_/ + \/_/\_\/_/ + \/_/\_\ + \/_/ sage: print(PlanePartition([[4,3,3,1],[2,1,1],[1,1]])._repr_diagram(True)) - / \ - /|\ /|\ - /|/|\|/ \|\ - |/|/ \|\ / \|\ - |/|\ /|\|\ /|\| - |/ \|/ \|\|/|\| - |\ / \ / \|/ \| - \|\ /|\ /|\ /| - \|/ \|/ \|/ - \ / \ / - \ / + ______ + /_/_/\_\ + /_/_/\/_/\ + /_/\_\/\_\/\ + /\_\/_/\/\_\/\ + \/\_\_\/\/_/\/ + \/_/\_\/_/\/ + \_\/_/\_\/ + \_\_\/_/ """ x = self._max_x y = self._max_y z = self._max_z - drawing = [[" " for i in range(2*x+2*y+1)] for j in range(x+y+z)] - def add_topside(i,j,k): - drawing[z+0+i+j-k][2*x-1-2*i+2*j] = u"╱" if use_unicode else "/" - drawing[z+0+i+j-k][2*x+1-2*i+2*j] = u"╲" if use_unicode else "\\" - drawing[z+1+i+j-k][2*x-1-2*i+2*j] = u"╲" if use_unicode else "\\" - drawing[z+1+i+j-k][2*x+1-2*i+2*j] = u"╱" if use_unicode else "/" - def add_rightside(i,j,k): - drawing[z+0+i+j-k][2*x-2-2*i+2*j] = u"│" if use_unicode else "|" - drawing[z+0+i+j-k][2*x-1-2*i+2*j] = u"╱" if use_unicode else "/" - drawing[z-1+i+j-k][2*x-1-2*i+2*j] = u"╱" if use_unicode else "/" - drawing[z-1+i+j-k][2*x-0-2*i+2*j] = u"│" if use_unicode else "|" - def add_leftside(i,j,k): - drawing[z-1+i+j-k][2*x-0-2*i+2*j] = u"│" if use_unicode else "|" - drawing[z-1+i+j-k][2*x+1-2*i+2*j] = u"╲" if use_unicode else "\\" - drawing[z+0+i+j-k][2*x+2-2*i+2*j] = u"│" if use_unicode else "|" - drawing[z+0+i+j-k][2*x+1-2*i+2*j] = u"╲" if use_unicode else "\\" + + drawing = [[" " for i in range(2 * x + y + z)] + for j in range(y + z + 1)] + + hori = u"_" if use_unicode else "_" + down = u"╲" if use_unicode else "\\" + up = u"╱" if use_unicode else "/" + + def superpose(l, c, letter): + # add the given letter at line l and column c + exist = drawing[l][c] + if exist == " " or exist == "_": + drawing[l][c] = letter + + def add_topside(i, j, k): + X = z + j - k + Y = 2 * x - 2 * i + j + k + superpose(X, Y-2, hori) + superpose(X, Y-1, hori) + superpose(X + 1, Y-2, down) + superpose(X + 1, Y-1, hori) + superpose(X + 1, Y, down) + + def add_rightside(i, j, k): + X = z + j - k + Y = 2 * x - 2 * i + j + k + superpose(X - 1, Y - 1, hori) + superpose(X - 1, Y, hori) + superpose(X, Y - 2, up) + superpose(X, Y - 1, hori) + superpose(X, Y, up) + + def add_leftside(i, j, k): + X = z + j - k + Y = 2 * x - 2 * i + j + k + superpose(X, Y, up) + superpose(X, Y + 1, down) + superpose(X + 1, Y + 1, up) + superpose(X + 1, Y, down) tab = self.z_tableau() for r in range(len(tab)): for c in range(len(tab[r])): if tab[r][c] > 0 or show_box: add_topside(r, c, tab[r][c]) + tab = self.y_tableau() for r in range(len(tab)): for c in range(len(tab[r])): if self.y_tableau()[r][c] > 0 or show_box: add_rightside(c, tab[r][c], r) + tab = self.x_tableau() for r in range(len(tab)): for c in range(len(tab[r])): @@ -298,16 +321,17 @@ def _ascii_art_(self): EXAMPLES:: - sage: print(PlanePartition([[4,3,3,1],[2,1,1],[1,1]])._ascii_art_()) - / \ - |\ /| - |\|/ \ - / \|\ / \ - |\ /|\|\ /| - / \|/ \|\|/| - |\ / \ / \|/ \ - \|\ /|\ /|\ /| - \|/ \|/ \|/ + sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]]) + sage: ascii_art(PP) + __ + /\_\ + __/\/_/ + __/\_\/\_\ + /\_\/_/\/\_\ + \/\_\_\/\/_/ + \/_/\_\/_/ + \/_/\_\ + \/_/ """ from sage.typeset.ascii_art import AsciiArt return AsciiArt(self._repr_diagram().splitlines(), baseline=0) @@ -320,15 +344,15 @@ def _unicode_art_(self): sage: PP = PlanePartition([[4,3,3,1],[2,1,1],[1,1]]) sage: unicode_art(PP) - ╱ ╲ - │╲ ╱│ - │╲│╱ ╲ - ╱ ╲│╲ ╱ ╲ - │╲ ╱│╲│╲ ╱│ - ╱ ╲│╱ ╲│╲│╱│ - │╲ ╱ ╲ ╱ ╲│╱ ╲ - ╲│╲ ╱│╲ ╱│╲ ╱│ - ╲│╱ ╲│╱ ╲│╱ + __ + ╱╲_╲ + __╱╲╱_╱ + __╱╲_╲╱╲_╲ + ╱╲_╲╱_╱╲╱╲_╲ + ╲╱╲_╲_╲╱╲╱_╱ + ╲╱_╱╲_╲╱_╱ + ╲╱_╱╲_╲ + ╲╱_╱ """ from sage.typeset.unicode_art import UnicodeArt return UnicodeArt(self._repr_diagram(use_unicode=True).splitlines(), baseline=0) @@ -349,27 +373,25 @@ def pp(self, show_box=False): EXAMPLES:: sage: PlanePartition([[4,3,3,1],[2,1,1],[1,1]]).pp() - / \ - |\ /| - |\|/ \ - / \|\ / \ - |\ /|\|\ /| - / \|/ \|\|/| - |\ / \ / \|/ \ - \|\ /|\ /|\ /| - \|/ \|/ \|/ + __ + /\_\ + __/\/_/ + __/\_\/\_\ + /\_\/_/\/\_\ + \/\_\_\/\/_/ + \/_/\_\/_/ + \/_/\_\ + \/_/ sage: PlanePartition([[4,3,3,1],[2,1,1],[1,1]]).pp(True) - / \ - /|\ /|\ - /|/|\|/ \|\ - |/|/ \|\ / \|\ - |/|\ /|\|\ /|\| - |/ \|/ \|\|/|\| - |\ / \ / \|/ \| - \|\ /|\ /|\ /| - \|/ \|/ \|/ - \ / \ / - \ / + ______ + /_/_/\_\ + /_/_/\/_/\ + /_/\_\/\_\/\ + /\_\/_/\/\_\/\ + \/\_\_\/\/_/\/ + \/_/\_\/_/\/ + \_\/_/\_\/ + \_\_\/_/ """ print(self._repr_diagram(show_box)) @@ -836,4 +858,3 @@ def leq(thing1, thing2): return self.element_class(self, Z, check=False) Element = PlanePartition - From c6789dc46526f27741a83eb474c2c0da2ba25e35 Mon Sep 17 00:00:00 2001 From: David Lowry-Duda Date: Tue, 18 Jul 2017 11:16:53 -0400 Subject: [PATCH 066/184] Remove curves/notes/idea1.txt William noted that this should be deleted, as this is an empty directory except for one old note. [And this is my first commit] --- src/sage/modular/curves/notes/idea1.txt | 37 ------------------------- 1 file changed, 37 deletions(-) delete mode 100644 src/sage/modular/curves/notes/idea1.txt diff --git a/src/sage/modular/curves/notes/idea1.txt b/src/sage/modular/curves/notes/idea1.txt deleted file mode 100644 index edcfc40f3b5..00000000000 --- a/src/sage/modular/curves/notes/idea1.txt +++ /dev/null @@ -1,37 +0,0 @@ -Extremely cool class idea! Very nice. - -A method which yields a smooth model (if known) of lowest degree -would be nice too, if there was a natural one to choose from. For -example, if the genus was one or 2 this might be known. - -+++++++++++++= - -William Stein wrote: -> > David Joyner wrote: ->> >> BTW, I'm happy to document it properly if you want the ->> >> code. I guess you want this as a method in the Gamma0 ->> >> class in congroup? -> > -> > I don't know where it should go. Probably we should define -> > a ModularCurve type and it should go there, right? -> > -> > sage: X = ModularCurve(Gamma0(15)) -> > sage: X.genus() -> > 1 -> > sage: X = X0(389) # shorthand -> > sage: X.genus() -> > 389 -> > -> > And then there's a temptation to do more. -> > sage: X.canonical_embedding() -> > ... -> > sage: X.modular_polynomial() -> > -> > sage: z = X(1 + 5*I) # point defined by point in upper half -> > plane. -> > sage: E = EllipticCurve("389A") -> > sage: phi = E.modular_parametrization(X) -> > sage: phi(z) # point on E over C -> > sage: sum(...) # construct a Heegner point. -> > -> > William \ No newline at end of file From 81c8f00c70a5f6ad3510c414ea45010f1815f708 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Tue, 18 Jul 2017 11:20:11 -0500 Subject: [PATCH 067/184] 23457: allow infinity to initialize projective point --- .../schemes/projective/projective_point.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 575d616f3cd..7878fb5b4d6 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -132,16 +132,27 @@ def __init__(self, X, v, check=True): sage: P = ProjectiveSpace(1, R.quo(t^2+1)) sage: P([2*t, 1]) (2*tbar : 1) + + :: + + sage: P = ProjectiveSpace(ZZ,1) + sage: P.point(Infinity) + (1 : 0) + sage: P(infinity) + (1 : 0) """ SchemeMorphism.__init__(self, X) if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field d = X.codomain().ambient_space().ngens() + if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) - elif v is infinity: - v = [0] * (d) - v[1] = 1 + elif v is infinity or (len(v) == 1 and v[0] is infinity): + if d > 2: + raise ValueError("infinity not well defined in dimension > 1") + v = [1, 0] + if not isinstance(v,(list,tuple)): raise TypeError("argument v (= %s) must be a scheme point, list, or tuple"%str(v)) if len(v) != d and len(v) != d-1: @@ -1542,6 +1553,14 @@ def __init__(self, X, v, check=True): sage: Q=P([2, 1]) sage: Q[0].parent() Finite Field of size 7 + + :: + + sage: P = ProjectiveSpace(QQ,1) + sage: P.point(Infinity) + (1 : 0) + sage: P(infinity) + (1 : 0) """ SchemeMorphism.__init__(self, X) if check: @@ -1549,10 +1568,11 @@ def __init__(self, X, v, check=True): d = X.codomain().ambient_space().ngens() if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) - elif v is infinity: - v = [0] * (d) - v[1] = 1 - if not isinstance(v,(list,tuple)): + elif v is infinity or (len(v) == 1 and v[0] is infinity): + if d > 2: + raise ValueError("infinity not well defined in dimension > 1") + v = [1, 0] + if not isinstance(v, (list,tuple)): raise TypeError("argument v (= %s) must be a scheme point, list, or tuple"%str(v)) if len(v) != d and len(v) != d-1: raise TypeError("v (=%s) must have %s components"%(v, d)) From dc5af6782e1791e68ce4b347d0dc23c666af5a7d Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Tue, 18 Jul 2017 16:34:58 -0500 Subject: [PATCH 068/184] 23457: update doctests --- .../schemes/projective/projective_point.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 7878fb5b4d6..8730f58a393 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -140,6 +140,14 @@ def __init__(self, X, v, check=True): (1 : 0) sage: P(infinity) (1 : 0) + + :: + + sage: P = ProjectiveSpace(ZZ,2) + sage: P(Infinity) + Traceback (most recent call last): + ... + ValueError: +Infinity not well defined in dimension > 1 """ SchemeMorphism.__init__(self, X) if check: @@ -150,7 +158,7 @@ def __init__(self, X, v, check=True): v = list(v) elif v is infinity or (len(v) == 1 and v[0] is infinity): if d > 2: - raise ValueError("infinity not well defined in dimension > 1") + raise ValueError("%s not well defined in dimension > 1"%v) v = [1, 0] if not isinstance(v,(list,tuple)): @@ -1561,6 +1569,14 @@ def __init__(self, X, v, check=True): (1 : 0) sage: P(infinity) (1 : 0) + + :: + + sage: P = ProjectiveSpace(ZZ,2) + sage: P(infinity) + Traceback (most recent call last): + ... + ValueError: +Infinity not well defined in dimension > 1 """ SchemeMorphism.__init__(self, X) if check: @@ -1570,7 +1586,7 @@ def __init__(self, X, v, check=True): v = list(v) elif v is infinity or (len(v) == 1 and v[0] is infinity): if d > 2: - raise ValueError("infinity not well defined in dimension > 1") + raise ValueError("%s not well defined in dimension > 1"%v) v = [1, 0] if not isinstance(v, (list,tuple)): raise TypeError("argument v (= %s) must be a scheme point, list, or tuple"%str(v)) From 90999eeaee6ab5e9f6807c33ade0126cb3f92150 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Tue, 18 Jul 2017 18:43:03 -0500 Subject: [PATCH 069/184] 23457: fixed higher dimensional test --- src/sage/schemes/generic/homset.py | 20 +++++++++++++++++++ .../schemes/projective/projective_point.py | 10 +++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index ac8e95ca743..f281366e703 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -392,6 +392,26 @@ def _element_constructor_(self, x, check=True): raise TypeError("x must be a ring homomorphism, list or tuple") + def _an_element_(self): + """ + Construct a sample morphism. + + OUTPUT: + + An element of the homset. + + EXAMPLES:: + + sage: P2 = ProjectiveSpace(QQ, 2) + sage: P2(QQ)._an_element_() + (1 : 1 : 1) + sage: End(P2)._an_element_() + Scheme endomorphism of Projective Space of dimension 2 over Rational + Field + Defn: Defined on coordinates by sending (x0 : x1 : x2) to + (x0 : x0 : x0) + """ + return self([self.domain().coordinate_ring().gen(0)]*self.codomain().ngens()) #******************************************************************* # Base class for points diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 8730f58a393..a5e2cdc43ea 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -148,6 +148,10 @@ def __init__(self, X, v, check=True): Traceback (most recent call last): ... ValueError: +Infinity not well defined in dimension > 1 + sage: P.point(infinity) + Traceback (most recent call last): + ... + ValueError: +Infinity not well defined in dimension > 1 """ SchemeMorphism.__init__(self, X) if check: @@ -1572,11 +1576,15 @@ def __init__(self, X, v, check=True): :: - sage: P = ProjectiveSpace(ZZ,2) + sage: P = ProjectiveSpace(QQ,2) sage: P(infinity) Traceback (most recent call last): ... ValueError: +Infinity not well defined in dimension > 1 + sage: P.point(infinity) + Traceback (most recent call last): + ... + ValueError: +Infinity not well defined in dimension > 1 """ SchemeMorphism.__init__(self, X) if check: From 8351aa992136b9228983755cdb1cda0e7dbd315f Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 19 Jul 2017 07:28:49 +0000 Subject: [PATCH 070/184] Fixing doctest errors from merge --- src/sage/rings/padics/local_generic.py | 32 +++++++------------ .../rings/padics/padic_extension_generic.py | 4 +-- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index a3de49cf89c..84a4aa77aeb 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -270,61 +270,51 @@ def change(self, **kwds): sage: K. = QqFP(125, prec=4) sage: K.change(q=64) - Unramified Extension of 2-adic Field with floating precision 4 in a defined by x^6 + x^4 + x^3 + x + 1 + Unramified Extension in a defined by x^6 + x^4 + x^3 + x + 1 with floating precision 4 over 2-adic Field sage: R. = QQ[] sage: K.change(modulus = x^2 - x + 2, print_pos=False) - Unramified Extension of 5-adic Field with floating precision 4 in a defined by x^2 - x + 2 + Unramified Extension in a defined by x^2 - x + 2 with floating precision 4 over 5-adic Field and variable names:: sage: K.change(names='b') - Unramified Extension of 5-adic Field with floating precision 4 in b defined by x^3 + 3*x + 3 + Unramified Extension in b defined by x^3 + 3*x + 3 with floating precision 4 over 5-adic Field and precision:: sage: Kup = K.change(prec=8); Kup - Unramified Extension of 5-adic Field with floating precision 8 in a defined by x^3 + 3*x + 3 + Unramified Extension in a defined by x^3 + 3*x + 3 with floating precision 8 over 5-adic Field sage: Kup.base_ring() 5-adic Field with floating precision 8 If you decrease the precision, the precision of the base stays the same:: sage: Kdown = K.change(prec=2); Kdown - Unramified Extension of 5-adic Field with floating precision 4 in a defined by x^3 + 3*x + 3 + Unramified Extension in a defined by x^3 + 3*x + 3 with floating precision 2 over 5-adic Field sage: Kdown.precision_cap() 2 sage: Kdown.base_ring() 5-adic Field with floating precision 4 - Changing the prime works for extensions as long as the defining polynomial is exact:: + Changing the prime works for extensions:: sage: x = polygen(ZZ) sage: R. = Zp(5).extension(x^2 + 2) sage: S = R.change(p=7) sage: S.defining_polynomial() - (1 + O(7^20))*x^2 + (O(7^20))*x + (2 + O(7^20)) + x^2 + 2 sage: A. = Zp(5)[] sage: R. = Zp(5).extension(y^2 + 2) sage: S = R.change(p=7) - Traceback (most recent call last): - ... - NotImplementedError: conversion between padic extensions not implemented + sage: S.defining_polynomial() + y^2 + 2 - If the defining polynomial is exact, you can raise the precision at will:: + :: sage: R. = Zq(5^3) sage: S = R.change(prec=50) sage: S.defining_polynomial() - (1 + O(5^50))*x^3 + (O(5^50))*x^2 + (3 + O(5^50))*x + (3 + O(5^50)) - - However, you cannot increase the precision above the limit imposed by - an inexact defining polynomial:: - - sage: R. = Zp(5).extension(y^2 + 2) - sage: S = R.change(prec=50) - Traceback (most recent call last): - ... - ValueError: Not enough precision in defining polynomial + x^3 + 3*x + 3 """ # We support both print_* and * for *=mode, pos, sep, alphabet for atr in ('print_mode', 'print_pos', 'print_sep', 'print_alphabet'): diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index f5b1d6549b4..9c1fe5d8896 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -293,13 +293,13 @@ def construction(self): sage: c, R0 = R.construction(); R0 5-adic Ring with capped relative precision 8 sage: c(R0) - Unramified Extension of 5-adic Ring with capped relative precision 8 in a defined by (1 + O(5^8))*x^2 + (4 + O(5^8))*x + (2 + O(5^8)) + Unramified Extension in a defined by x^2 + 4*x + 2 with capped relative precision 8 over 5-adic Ring sage: c(R0) == R True """ from sage.categories.pushout import AlgebraicExtensionFunctor as AEF print_mode = self._printer.dict() - return (AEF([self._pre_poly], [self.variable_name()], + return (AEF([self.defining_polynomial()], [self.variable_name()], prec=self.precision_cap(), print_mode=self._printer.dict(), implementation=self._implementation), self.base_ring()) From 4dc084e452d83fe8bf80b105d25b404a21092be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 19 Jul 2017 12:58:35 +0200 Subject: [PATCH 071/184] trac 14153 reviewer's suggestions + gap doc interface --- src/sage/doctest/forker.py | 6 +++++- src/sage/interfaces/gap.py | 3 ++- src/sage/interfaces/r.py | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 705b7188168..325c704e1eb 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -514,7 +514,11 @@ def _run(self, test, compileflags, out): finally: if self.debugger is not None: self.debugger.set_continue() # ==== Example Finished ==== - got = self._fakeout.getvalue().decode('utf-8') + got = self._fakeout.getvalue() + try: + got = got.decode('utf-8') + except UnicodeDecodeError: + got = got.decode('latin1') # the actual output outcome = FAILURE # guilty until proved innocent or insane diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index dd7b27f3e9b..7e765a1f550 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -188,6 +188,7 @@ from sage.structure.element import ModuleElement import re import os +import io import pexpect import time import platform @@ -1339,7 +1340,7 @@ def help(self, s, pager=True): (sline,) = match.groups() if self.is_remote(): self._get_tmpfile() - F = open(self._local_tmpfile(),"r") + F = io.open(self._local_tmpfile(), "r", encoding='utf-8') help = F.read() if pager: from IPython.core.page import page diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index e9096dfd90c..2406d3b7f50 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -668,7 +668,11 @@ def library(self, library_name): ... ImportError: ... """ - ret = self.eval('require("%s")'%library_name).decode('utf-8') + ret = self.eval('require("%s")' % library_name) + try: + ret = ret.decode('utf-8') + except UnicodeDecodeError: + ret = ret.decode('latin-1') # try hard to parse the message string in a locale-independent way if ' library(' in ret: # locale-independent key-word raise ImportError("%s"%ret) From 2ddfd21bfd2b81cd90f26df5a0075c57283b5c69 Mon Sep 17 00:00:00 2001 From: Ander Steele Date: Wed, 19 Jul 2017 15:03:35 +0000 Subject: [PATCH 072/184] Initial commit. Working prototype of gauss sum computation --- src/sage/rings/padics/misc.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index 893fc842a95..4f5a8e703af 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -26,7 +26,31 @@ from six.moves.builtins import min as python_min from six.moves.builtins import max as python_max from sage.rings.infinity import infinity +from sage.rings.padics.factory import Zp +from sage.rings.all import PolynomialRing +def gauss_sum(a, p, f, prec = 20): + """ + Returns the gauss sum g_q(a) = \sum_{u\in F_q^\times} omega(u)^(-a) \zeta_q^u + where q = p^f, \omega is the Teichmuller character and \zeta_q is some arbitrary + choice of primitive p-th root of unity + """ + a = a % (p**f) + R = Zp(p, prec) + R_poly = PolynomialRing(R,name='X') + X = R_poly.gen() + F = R.ext(X**(p-1)+p, names='pi') + pi = F.gen() + digits = Zp(p)(a).list(start_val = 0) + n = len(digits) + digits = digits + [0]*(f-n) + s = sum(digits) + out = -pi**(s) + for i in range(0,f): + a_i = R(sum([digits[k]*p**((i+k)%f) for k in range(f)])) #an O(p^prec) term is necessay + if a_i: + out = out*R((a_i/(p**f-1)).gamma()) #for coercing 0 correctly + return out def min(*L): r""" From 47e562f247cd8221cd08c32211bfed3951b7b10a Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Wed, 19 Jul 2017 11:17:13 -0500 Subject: [PATCH 073/184] 23457: implement .point() instead --- src/sage/schemes/generic/algebraic_scheme.py | 54 +++++++++++++++++++ src/sage/schemes/generic/homset.py | 21 -------- .../schemes/projective/projective_point.py | 9 ---- .../schemes/projective/projective_space.py | 50 +++++++++++++++++ 4 files changed, 104 insertions(+), 30 deletions(-) diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index ed770124014..71343967454 100644 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -2463,6 +2463,60 @@ class AlgebraicScheme_subscheme_projective(AlgebraicScheme_subscheme): x^2 - y*z """ + def point(self, v, check=True): + """ + Create a point on this projective subscheme. + + INPUT: + + - ``v`` -- anything that defines a point + + - ``check`` -- boolean (optional, default: ``True``); whether + to check the defining data for consistency + + OUTPUT: A point of the subscheme. + + EXAMPLES:: + + sage: P2. = ProjectiveSpace(QQ, 2) + sage: X = P2.subscheme([x-y,y-z]) + sage: X.point([1,1,1]) + (1 : 1 : 1) + + :: + + sage: P2. = ProjectiveSpace(QQ, 1) + sage: X = P2.subscheme([y]) + sage: X.point(infinity) + (1 : 0) + + :: + + sage: P. = ProjectiveSpace(QQ, 1) + sage: X = P.subscheme(x^2+2*y^2) + sage: X.point(infinity) + Traceback (most recent call last): + ... + TypeError: Coordinates [1, 0] do not define a point on Closed subscheme + of Projective Space of dimension 1 over Rational Field defined by: + x^2 + 2*y^2 + """ + from sage.rings.infinity import infinity + if v is infinity or\ + (isinstance(v, (list,tuple)) and len(v) == 1 and v[0] is infinity): + if self.ambient_space().dimension_relative() > 1: + raise ValueError("%s not well defined in dimension > 1"%v) + v = [1, 0] + # todo: update elliptic curve stuff to take point_homset as argument + from sage.schemes.elliptic_curves.ell_generic import is_EllipticCurve + if is_EllipticCurve(self): + try: + return self._point(self.point_homset(), v, check=check) + except AttributeError: # legacy code without point_homset + return self._point(self, v, check=check) + + return self.point_homset()(v, check=check) + def _morphism(self, *args, **kwds): r""" Construct a morphism determined by action on points of ``self``. diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index f281366e703..4f2c8e1f139 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -392,27 +392,6 @@ def _element_constructor_(self, x, check=True): raise TypeError("x must be a ring homomorphism, list or tuple") - def _an_element_(self): - """ - Construct a sample morphism. - - OUTPUT: - - An element of the homset. - - EXAMPLES:: - - sage: P2 = ProjectiveSpace(QQ, 2) - sage: P2(QQ)._an_element_() - (1 : 1 : 1) - sage: End(P2)._an_element_() - Scheme endomorphism of Projective Space of dimension 2 over Rational - Field - Defn: Defined on coordinates by sending (x0 : x1 : x2) to - (x0 : x0 : x0) - """ - return self([self.domain().coordinate_ring().gen(0)]*self.codomain().ngens()) - #******************************************************************* # Base class for points #******************************************************************* diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index a5e2cdc43ea..81ea4fc1219 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -36,7 +36,6 @@ from sage.categories.integral_domains import IntegralDomains from sage.categories.number_fields import NumberFields _NumberFields = NumberFields() -from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ from sage.rings.fraction_field import FractionField from sage.rings.morphism import RingHomomorphism_im_gens @@ -160,10 +159,6 @@ def __init__(self, X, v, check=True): if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) - elif v is infinity or (len(v) == 1 and v[0] is infinity): - if d > 2: - raise ValueError("%s not well defined in dimension > 1"%v) - v = [1, 0] if not isinstance(v,(list,tuple)): raise TypeError("argument v (= %s) must be a scheme point, list, or tuple"%str(v)) @@ -1592,10 +1587,6 @@ def __init__(self, X, v, check=True): d = X.codomain().ambient_space().ngens() if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) - elif v is infinity or (len(v) == 1 and v[0] is infinity): - if d > 2: - raise ValueError("%s not well defined in dimension > 1"%v) - v = [1, 0] if not isinstance(v, (list,tuple)): raise TypeError("argument v (= %s) must be a scheme point, list, or tuple"%str(v)) if len(v) != d and len(v) != d-1: diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 981c6c7c669..fe76eea524e 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -709,6 +709,56 @@ def _point_homset(self, *args, **kwds): """ return SchemeHomset_points_projective_ring(*args, **kwds) + def point(self, v, check=True): + """ + Create a point on this projective space. + + INPUT: + + - ``v`` -- anything that defines a point + + - ``check`` -- boolean (optional, default: ``True``); whether + to check the defining data for consistency + + OUTPUT: A point of this projective space. + + EXAMPLES:: + + sage: P2 = ProjectiveSpace(QQ, 2) + sage: P2.point([4,5]) + (4 : 5 : 1) + + :: + + sage: P = ProjectiveSpace(QQ, 1) + sage: P.point(infinity) + (1 : 0) + + :: + + sage: P = ProjectiveSpace(QQ, 2) + sage: P.point(infinity) + Traceback (most recent call last): + ... + ValueError: +Infinity not well defined in dimension > 1 + + :: + + sage: P = ProjectiveSpace(ZZ, 2) + sage: P.point([infinity]) + Traceback (most recent call last): + ... + ValueError: [+Infinity] not well defined in dimension > 1 + """ + from sage.rings.infinity import infinity + if v is infinity or\ + (isinstance(v, (list,tuple)) and len(v) == 1 and v[0] is infinity): + if self.dimension_relative() > 1: + raise ValueError("%s not well defined in dimension > 1"%v) + v = [1, 0] + + return self.point_homset()(v, check=check) + def _point(self, *args, **kwds): """ Construct a point. From 45b56d0cd0ae65795ce9da3d42b43c22bee4c3a3 Mon Sep 17 00:00:00 2001 From: Ander Steele Date: Wed, 19 Jul 2017 16:20:24 +0000 Subject: [PATCH 074/184] Add documentation --- src/sage/rings/padics/misc.py | 53 ++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index 4f5a8e703af..220e95ac79a 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -31,9 +31,54 @@ def gauss_sum(a, p, f, prec = 20): """ - Returns the gauss sum g_q(a) = \sum_{u\in F_q^\times} omega(u)^(-a) \zeta_q^u + Return the gauss sum g_q(a) as a p-adic number. + + g_q(a) is defined by g_q(a)= \sum_{u\in F_q^\times} omega(u)^(-a) \zeta_q^u where q = p^f, \omega is the Teichmuller character and \zeta_q is some arbitrary - choice of primitive p-th root of unity + choice of primitive q-th root of unity + + + INPUT: + + - ``a`` -- integer. + + - ``p`` -- prime. + + - ``f`` -- positive integer. + + - ``prec`` -- positive integer. set to 20 by default. + + OUTPUT: + + - a `p`-adic number in an Eisenstein extension of Q_p + + .. NOTE:: + + This is based on GP code written by Adriana Salerno + + + EXAMPLES: + + In this example, we verify that g_3(0) = -1 + + sage: from sage.rings.padics import gauss_sum + sage: -gauss_sum(0,3,1) + 1 + O(pi^40) + + Next, we verify that g_5(a)*g_5(-a) = 5*(-1)^a + + sage: from sage.rings.padics import gauss_sum + sage: gauss_sum(2,5,1)^2-5 + O(pi^84) + sage: gauss_sum(1,5,1)*gauss_sum(3,5,1)+5 + O(pi^84) + + Finally, we compute a non-trivial value + + sage: from sage.rings.padics import gauss_sum + sage: gauss_sum(2,13,2) + 6*pi^2 + 7*pi^14 + 11*pi^26 + 3*pi^62 + 6*pi^74 + 3*pi^86 + 5*pi^98 + pi^110 + 7*pi^134 + 9*pi^146 + 4*pi^158 + 6*pi^170 + 4*pi^194 + pi^206 + 6*pi^218 + 9*pi^230 + O(pi^242) + """ a = a % (p**f) R = Zp(p, prec) @@ -47,9 +92,9 @@ def gauss_sum(a, p, f, prec = 20): s = sum(digits) out = -pi**(s) for i in range(0,f): - a_i = R(sum([digits[k]*p**((i+k)%f) for k in range(f)])) #an O(p^prec) term is necessay + a_i = R(sum([digits[k]*p**((i+k)%f) for k in range(f)])) if a_i: - out = out*R((a_i/(p**f-1)).gamma()) #for coercing 0 correctly + out = out*R((a_i/(p**f-1)).gamma()) return out def min(*L): From b77e34f83d4c50ca8517abc66e2e333aad3600f0 Mon Sep 17 00:00:00 2001 From: Ander Steele Date: Wed, 19 Jul 2017 16:25:43 +0000 Subject: [PATCH 075/184] fix import on doctests --- src/sage/rings/padics/misc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index 220e95ac79a..088e9918536 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -61,13 +61,13 @@ def gauss_sum(a, p, f, prec = 20): In this example, we verify that g_3(0) = -1 - sage: from sage.rings.padics import gauss_sum + sage: from sage.rings.padics.misc import gauss_sum sage: -gauss_sum(0,3,1) 1 + O(pi^40) Next, we verify that g_5(a)*g_5(-a) = 5*(-1)^a - sage: from sage.rings.padics import gauss_sum + sage: from sage.rings.padics.misc import gauss_sum sage: gauss_sum(2,5,1)^2-5 O(pi^84) sage: gauss_sum(1,5,1)*gauss_sum(3,5,1)+5 @@ -75,7 +75,7 @@ def gauss_sum(a, p, f, prec = 20): Finally, we compute a non-trivial value - sage: from sage.rings.padics import gauss_sum + sage: from sage.rings.padics.misc import gauss_sum sage: gauss_sum(2,13,2) 6*pi^2 + 7*pi^14 + 11*pi^26 + 3*pi^62 + 6*pi^74 + 3*pi^86 + 5*pi^98 + pi^110 + 7*pi^134 + 9*pi^146 + 4*pi^158 + 6*pi^170 + 4*pi^194 + pi^206 + 6*pi^218 + 9*pi^230 + O(pi^242) From cb21c90a1ed8e169c2f71237fe9f3573b1805c49 Mon Sep 17 00:00:00 2001 From: Ander Steele Date: Wed, 19 Jul 2017 18:53:41 +0000 Subject: [PATCH 076/184] Fix whitespace problems with documentation --- src/sage/rings/padics/misc.py | 98 +++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index 088e9918536..77f1858ae35 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -1,14 +1,17 @@ r""" Miscellaneous Functions -This file contains two miscellaneous functions used by `p`-adics. +This file contains several miscellaneous functions used by `p`-adics. +- ``gauss_sum`` -- Computes gauss sums using the Gross-Koblitz formula. - ``min`` -- a version of ``min`` that returns `\infty` on empty input. - ``max`` -- a version of ``max`` that returns `-\infty` on empty input. AUTHORS: - David Roe +- Adriana Salerno +- Ander Steele """ #***************************************************************************** @@ -32,53 +35,58 @@ def gauss_sum(a, p, f, prec = 20): """ Return the gauss sum g_q(a) as a p-adic number. - + g_q(a) is defined by g_q(a)= \sum_{u\in F_q^\times} omega(u)^(-a) \zeta_q^u where q = p^f, \omega is the Teichmuller character and \zeta_q is some arbitrary choice of primitive q-th root of unity - - - INPUT: - - - ``a`` -- integer. - - - ``p`` -- prime. - - - ``f`` -- positive integer. - - - ``prec`` -- positive integer. set to 20 by default. - - OUTPUT: - - - a `p`-adic number in an Eisenstein extension of Q_p - - .. NOTE:: - - This is based on GP code written by Adriana Salerno - - - EXAMPLES: - - In this example, we verify that g_3(0) = -1 - - sage: from sage.rings.padics.misc import gauss_sum - sage: -gauss_sum(0,3,1) - 1 + O(pi^40) - - Next, we verify that g_5(a)*g_5(-a) = 5*(-1)^a - - sage: from sage.rings.padics.misc import gauss_sum - sage: gauss_sum(2,5,1)^2-5 - O(pi^84) - sage: gauss_sum(1,5,1)*gauss_sum(3,5,1)+5 - O(pi^84) - - Finally, we compute a non-trivial value - - sage: from sage.rings.padics.misc import gauss_sum - sage: gauss_sum(2,13,2) - 6*pi^2 + 7*pi^14 + 11*pi^26 + 3*pi^62 + 6*pi^74 + 3*pi^86 + 5*pi^98 + pi^110 + 7*pi^134 + 9*pi^146 + 4*pi^158 + 6*pi^170 + 4*pi^194 + pi^206 + 6*pi^218 + 9*pi^230 + O(pi^242) - + + + INPUT: + + - ``a`` -- integer. + + - ``p`` -- prime. + + - ``f`` -- positive integer. + + - ``prec`` -- positive integer. set to 20 by default. + + OUTPUT: + + - a `p`-adic number in an Eisenstein extension of Q_p + + .. NOTE:: + + This is based on GP code written by Adriana Salerno + + EXAMPLES: + + In this example, we verify that g_3(0) = -1 + + :: + + sage: from sage.rings.padics.misc import gauss_sum + sage: -gauss_sum(0,3,1) + 1 + O(pi^40) + + Next, we verify that g_5(a)*g_5(-a) = 5*(-1)^a + + :: + + sage: from sage.rings.padics.misc import gauss_sum + sage: gauss_sum(2,5,1)^2-5 + O(pi^84) + sage: gauss_sum(1,5,1)*gauss_sum(3,5,1)+5 + O(pi^84) + + Finally, we compute a non-trivial value + + :: + + sage: from sage.rings.padics.misc import gauss_sum + sage: gauss_sum(2,13,2) + 6*pi^2 + 7*pi^14 + 11*pi^26 + 3*pi^62 + 6*pi^74 + 3*pi^86 + 5*pi^98 + pi^110 + 7*pi^134 + 9*pi^146 + 4*pi^158 + 6*pi^170 + 4*pi^194 + pi^206 + 6*pi^218 + 9*pi^230 + O(pi^242) + """ a = a % (p**f) R = Zp(p, prec) From 3a3a5f15639e5bcae109fd8a338a38acfd453368 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 19 Jul 2017 20:56:01 +0100 Subject: [PATCH 077/184] Make is_galois for number fields work in higher degrees (beyond where pari can compute Galois groups) --- src/sage/rings/number_field/number_field.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index c5cfe2c731f..4bad27b89c6 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -4966,8 +4966,12 @@ def is_galois(self): True sage: NumberField(x^3 + 2, 'a').is_galois() False + sage: NumberField(x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1, 'a').is_galois() + True + sage: NumberField(x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 10, 'a').is_galois() + False """ - return self.galois_group(type="pari").order() == self.degree() + return len(self.automorphisms()) == self.degree() @cached_method def galois_group(self, type=None, algorithm='pari', names=None): From 1c3d06ab244343f2f3c6ce956d1ca502f3b6eef2 Mon Sep 17 00:00:00 2001 From: Ander Steele Date: Wed, 19 Jul 2017 21:30:10 +0000 Subject: [PATCH 078/184] prettify documentation with latex tags --- src/sage/rings/padics/misc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index 77f1858ae35..2ae896cff5b 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -34,11 +34,11 @@ def gauss_sum(a, p, f, prec = 20): """ - Return the gauss sum g_q(a) as a p-adic number. + Return the gauss sum `g_q(a)` as a `p`-adic number. - g_q(a) is defined by g_q(a)= \sum_{u\in F_q^\times} omega(u)^(-a) \zeta_q^u - where q = p^f, \omega is the Teichmuller character and \zeta_q is some arbitrary - choice of primitive q-th root of unity + `g_q(a)` is defined by `g_q(a)= \sum_{u\in F_q^*} \omega(u)^{(-a)} \zeta_q^u` + where `q = p^f`, `\omega` is the Teichmuller character and `\zeta_q` is some arbitrary + choice of primitive `q`-th root of unity INPUT: From 4ac09ac4c36a1a9174dad8d5e31d86d197f8a19b Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 19 Jul 2017 21:36:26 +0000 Subject: [PATCH 079/184] Fix changing print mode to digits for eisenstein extensions --- src/sage/rings/padics/local_generic.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 84a4aa77aeb..53a3ea76104 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -266,6 +266,15 @@ def change(self, **kwds): sage: repr(~R(17)) '...13403' + Changing print mode to 'digits' works for Eisenstein extensions:: + + sage: S. = ZZ[] + sage: W. = Zp(3).extension(x^4 + 9*x^2 + 3*x - 3) + sage: W.print_mode() + 'series' + sage: W.change(print_mode='digits').print_mode() + 'digits' + You can change extensions:: sage: K. = QqFP(125, prec=4) @@ -348,6 +357,14 @@ def get_unramified_modulus(q, res_name): kwds['show_prec'] = _default_show_prec(new_type, kwds['mode']) else: raise RuntimeError + p = kwds.get('p', functor.p if hasattr(functor, 'p') else self.prime()) + curpstr = str(self.prime()) + # If we are switching to 'digits', or changing p, need to ensure a large enough alphabet. + if 'alphabet' not in kwds and (kwds.get('mode') == 'digits' or + (functor.extras['print_mode'].get('mode') == 'digits' and p > functor.p)): + from .padic_printing import _printer_defaults + kwds['alphabet'] = _printer_defaults.alphabet()[:p] + # There are two kinds of functors possible: # CompletionFunctor and AlgebraicExtensionFunctor # We distinguish them by the presence of ``prec``, @@ -367,14 +384,6 @@ def get_unramified_modulus(q, res_name): raise TypeError('You must specify the type explicitly') elif ring.is_field(): ring = ring.ring_of_integers() - p = kwds.get('p', functor.p) - curpstr = str(self.prime()) - # If we are switching to 'digits', or changing p, need to ensure a large enough alphabet. - if 'alphabet' not in kwds and (kwds.get('mode') == 'digits' or - (functor.extras['print_mode'].get('mode') == 'digits' and - p > functor.p)): - from .padic_printing import _printer_defaults - kwds['alphabet'] = _printer_defaults.alphabet()[:p] for atr in ('p', 'prec', 'type'): if atr in kwds: setattr(functor, atr, kwds.pop(atr)) From 85019cc687930cd9be60d01e45eb08dd9ea91910 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 19 Jul 2017 21:59:35 +0000 Subject: [PATCH 080/184] Fix errors due to moving digits code earlier in the change function --- src/sage/rings/padics/local_generic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 53a3ea76104..38e54f900f5 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -359,9 +359,10 @@ def get_unramified_modulus(q, res_name): raise RuntimeError p = kwds.get('p', functor.p if hasattr(functor, 'p') else self.prime()) curpstr = str(self.prime()) + functor_dict = getattr(functor, "extras", getattr(functor, "kwds", None)) # If we are switching to 'digits', or changing p, need to ensure a large enough alphabet. if 'alphabet' not in kwds and (kwds.get('mode') == 'digits' or - (functor.extras['print_mode'].get('mode') == 'digits' and p > functor.p)): + (functor_dict['print_mode'].get('mode') == 'digits' and p > getattr(functor, "p", p))): from .padic_printing import _printer_defaults kwds['alphabet'] = _printer_defaults.alphabet()[:p] From 1c8d050818d3e7bb2c5149e82c346ddb20c9ec0e Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 19 Jul 2017 23:11:50 +0100 Subject: [PATCH 081/184] Use old method for is_galois when the degree is small enough for speed --- src/sage/rings/number_field/number_field.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 4bad27b89c6..0e2f329cbf0 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -4971,7 +4971,10 @@ def is_galois(self): sage: NumberField(x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 10, 'a').is_galois() False """ - return len(self.automorphisms()) == self.degree() + if self.degree() < 12: + return self.galois_group(type='pari').order() == self.degree() + else: + return len(self.automorphisms()) == self.degree() @cached_method def galois_group(self, type=None, algorithm='pari', names=None): From 7c362ef9033b074e2c6857b7dcb3c27ab4b42c23 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 19 Jul 2017 23:27:18 +0000 Subject: [PATCH 082/184] fix typo --- src/sage/rings/padics/CR_template.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/CR_template.pxi b/src/sage/rings/padics/CR_template.pxi index 0c1d66a18c6..293c60178be 100644 --- a/src/sage/rings/padics/CR_template.pxi +++ b/src/sage/rings/padics/CR_template.pxi @@ -2314,7 +2314,7 @@ cdef class pAdicCoercion_CR_frac_field(RingHomomorphism_coercion): def is_surjective(self): r""" - Return whether this map is injective. + Return whether this map is surjective. EXAMPLES:: From b283a1a3e777325e68984890f0ea127cc604d193 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 19 Jul 2017 23:30:15 +0000 Subject: [PATCH 083/184] Identity morphisms are injective and surjective --- src/sage/categories/morphism.pyx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index e30abd4ae75..8de43882852 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -420,6 +420,30 @@ cdef class IdentityMorphism(Morphism): def __invert__(self): return self + def is_surjective(self): + r""" + Return whether this morphism is surjective. + + EXAMPLES:: + + sage: ZZ.hom(ZZ).is_surjective() + True + + """ + return True + + def is_injective(self): + r""" + Return whether this morphism is injective + + EXAMPLES:: + + sage: ZZ.hom(ZZ).is_injective() + True + + """ + return True + cdef class SetMorphism(Morphism): def __init__(self, parent, function): """ From 569cde21ddc1ad06c844b0425f8dd924f38ddb57 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 19 Jul 2017 23:32:39 +0000 Subject: [PATCH 084/184] =?UTF-8?q?Z=20=E2=86=92=20Zmod=20is=20surjective?= =?UTF-8?q?=20but=20not=20injective?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/rings/finite_rings/integer_mod.pyx | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index e2e930e3a6b..85f9629b51a 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -4208,6 +4208,30 @@ cdef class Integer_to_IntegerMod(IntegerMod_hom): def section(self): return IntegerMod_to_Integer(self._codomain) + def is_surjective(self): + r""" + Return whether this morphism is surjective. + + EXAMPLES:: + + sage: ZZ.hom(Zmod(2)).is_surjective() + True + + """ + return True + + def is_injective(self): + r""" + Return whether this morphism is injective. + + EXAMPLES:: + + sage: ZZ.hom(Zmod(2)).is_injective() + False + + """ + return False + cdef class IntegerMod_to_Integer(Map): """ Map to lift elements to :class:`~sage.rings.integer.Integer`. From 04f2da04780dc29ca4a7d9a78fc528dbea1c5687 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 19 Jul 2017 23:34:00 +0000 Subject: [PATCH 085/184] =?UTF-8?q?Zmod(nm)=20=E2=86=92=20Zmod(n)=20is=20s?= =?UTF-8?q?urjective=20but=20not=20injective?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/rings/finite_rings/integer_mod.pyx | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index e2e930e3a6b..158d854da55 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -4163,6 +4163,30 @@ cdef class IntegerMod_to_IntegerMod(IntegerMod_hom): def _repr_type(self): return "Natural" + def is_surjective(self): + r""" + Return whether this morphism is surjective. + + EXAMPLES:: + + sage: Zmod(4).hom(Zmod(2)).is_surjective() + True + + """ + return True + + def is_injective(self): + r""" + Return whether this morphism is injective. + + EXAMPLES:: + + sage: Zmod(4).hom(Zmod(2)).is_injective() + False + + """ + return self.domain().order() == self.codomain().order() + cdef class Integer_to_IntegerMod(IntegerMod_hom): r""" Fast `\ZZ \rightarrow \ZZ/n\ZZ` From 2ee2abf22f8bf06995c2ce0fb70f365cba8055d8 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 19 Jul 2017 23:36:33 +0000 Subject: [PATCH 086/184] Do not override __richcmp__ in Ring morphisms --- src/sage/rings/morphism.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 978fe442e81..c5e8be448f5 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -499,7 +499,7 @@ cdef class RingMap_lift(RingMap): _slots['S'] = self.S return Morphism._extra_slots(self, _slots) - def __richcmp__(self, other, int op): + def _richcmp_(self, other, int op): """ Compare a ring lifting maps ``self`` to ``other``. @@ -940,7 +940,7 @@ cdef class RingHomomorphism_coercion(RingHomomorphism): """ return "Ring Coercion" - def __richcmp__(self, other, int op): + def _richcmp_(self, other, int op): """ Compare a ring coercion morphism ``self`` to ``other``. @@ -1633,7 +1633,7 @@ cdef class RingHomomorphism_cover(RingHomomorphism): """ return self.codomain().defining_ideal() - def __richcmp__(self, other, int op): + def _richcmp_(self, other, int op): """ Compare ``self`` to ``other``. @@ -1830,7 +1830,7 @@ cdef class RingHomomorphism_from_quotient(RingHomomorphism): """ return self.phi - def __richcmp__(self, other, op): + def _richcmp_(self, other, op): """ Compare ``self`` to ``other``. From 7428353cf0520558fb24b292e48be1358fa0d305 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 02:20:27 +0000 Subject: [PATCH 087/184] minor docstring fixes --- src/sage/rings/padics/factory.py | 6 +++--- src/sage/rings/padics/local_generic.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index f98c90e0b8a..35142a32d20 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -241,7 +241,7 @@ class Qp_class(UniqueFactory): - ``print_max_terms`` -- integer (default ``None``) The maximum number of terms shown. See PRINTING below. - - ``show_prec`` -- bool (default ``None``) Whether to show the precision + - ``show_prec`` -- bool (default ``None``) whether to show the precision for elements. See PRINTING below. - ``check`` -- bool (default ``True``) whether to check if `p` is prime. @@ -663,7 +663,7 @@ def Qq(q, prec = None, type = 'capped-rel', modulus = None, names=None, number of terms in the polynomial representation of an element (using ``'terse'``). See PRINTING below. - - ``show_prec`` -- bool (default ``None``) Whether to show the precision + - ``show_prec`` -- bool (default ``None``) whether to show the precision for elements. See PRINTING below. - ``check`` -- bool (default ``True``) whether to check inputs. @@ -1236,7 +1236,7 @@ class Zp_class(UniqueFactory): - ``print_max_terms`` -- integer (default ``None``) The maximum number of terms shown. See PRINTING below. - - ``show_prec`` -- bool (default ``None``) Whether to show the precision + - ``show_prec`` -- bool (default ``None``) whether to show the precision for elements. See PRINTING below. - ``check`` -- bool (default ``True``) whether to check if `p` is diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 38e54f900f5..416edce1c8e 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -193,7 +193,7 @@ def _latex_(self): return self._repr_(do_latex = True) def change(self, **kwds): - """ + r""" Return a new ring with changed attributes. INPUT: @@ -225,10 +225,10 @@ def change(self, **kwds): the precision on the base is increased as necessary (respecting ramification). If the precision is decreased, the precision of the base is unchanged. - - ``field`` -- bool. If True, switch to a tower of fields via the fraction field. + - ``field`` -- bool. If ``True``, switch to a tower of fields via the fraction field. If False, switch to a tower of rings of integers. - - ``q`` -- prime power. Replace the initial unramified extension of `\Qp` or `\Zp` + - ``q`` -- prime power. Replace the initial unramified extension of `\Q_p` or `\Z_p` with an unramified extension of residue cardinality `q`. If the initial extension is ramified, add in an unramified extension. From 964974b5ceb58f4b401f6ac70452c194579bc67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 20 Jul 2017 14:17:41 +0300 Subject: [PATCH 088/184] Remove a warning block. --- src/sage/graphs/generic_graph.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index e1abe634213..f87b33a60bb 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -20782,12 +20782,6 @@ def automorphism_group(self, partition=None, verbosity=0, "sage" uses Sage's implementation. If set to ``None`` (default), bliss is used when available. - .. WARNING:: - - Since :trac:`14319` the domain of the automorphism group is equal to - the graph's vertex set, and the ``translation`` argument has become - useless. - OUTPUT: The order of the output is group, order, orbits. However, there are options to turn each of these on or off. From 3144b308c4b7d7857a737ba19da54d5075902028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 20 Jul 2017 15:03:19 +0300 Subject: [PATCH 089/184] Minor documentation changes. --- src/sage/categories/finite_posets.py | 47 +++++++++++++++++++++------- src/sage/combinat/posets/posets.py | 27 +++++++++++++--- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 2d6f1c1d534..6909e37568b 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -51,49 +51,72 @@ class ParentMethods: def is_lattice(self): r""" - Returns whether this poset is both a meet and a join semilattice. + Return ``True`` if the poset is lattice, and + ``False`` otherwise. + + A poset is lattice if every pair of elements have + both the least upper bound ("join") and the greatest lower bound + ("meet") in the poset. EXAMPLES:: - sage: P = Poset([[1,3,2],[4],[4,5,6],[6],[7],[7],[7],[]]) + sage: P = Poset([[1, 3, 2], [4], [4, 5, 6], [6], [7], [7], [7], []]) sage: P.is_lattice() True - sage: P = Poset([[1,2],[3],[3],[]]) + sage: P = Poset([[1, 2], [3], [3], []]) sage: P.is_lattice() True - sage: P = Poset({0:[2,3],1:[2,3]}) + sage: P = Poset({0: [2, 3], 1: [2, 3]}) sage: P.is_lattice() False + + sage: P = Poset({1: [2, 3, 4], 2: [5, 6], 3: [5, 7], 4: [6, 7], 5: [8, 9], + ....: 6: [8, 10], 7: [9, 10], 8: [11], 9: [11], 10: [11]}) + sage: P.is_lattice() + False + + TESTS:: + + sage: P = Poset() + sage: P.is_lattice() + True + + .. SEEALSO:: + + - Weaker properties: :meth:`~sage.combinat.posets.posets.FinitePoset.is_join_semilattice`, + :meth:`~sage.combinat.posets.posets.FinitePoset.is_meet_semilattice` """ return (self.cardinality() == 0 or (self.has_bottom() and self.is_join_semilattice())) def is_selfdual(self): r""" - Returns whether this poset is *self-dual*, that is - isomorphic to its dual poset. + Return ``True`` if the poset is *self-dual*, and + ``False`` otherwise. + + A poset is self-dual if it is isomorphic to its dual poset. EXAMPLES:: - sage: P = Poset(([1,2,3],[[1,3],[2,3]]),cover_relations=True) + sage: P = Poset({1: [3, 4], 2: [3, 4]}) sage: P.is_selfdual() - False + True - sage: P = Poset(([1,2,3,4],[[1,3],[1,4],[2,3],[2,4]]),cover_relations=True) + sage: P = Poset({1: [2, 3]}) sage: P.is_selfdual() - True + False TESTS:: - sage: P = Poset( {} ) + sage: P = Poset() sage: P.is_selfdual() True .. SEEALSO:: - - Stronger properties: :meth:`~sage.combinat.posets.lattices.FiniteLattice.is_orthocomplemented` + - Stronger properties: :meth:`~sage.combinat.posets.lattices.FiniteLatticePoset.is_orthocomplemented` (for lattices) """ # Two quick checks before full isomorphic test. if sorted(self._hasse_diagram.in_degree()) != sorted(self._hasse_diagram.out_degree()): diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 6a01feb5345..d470d7e1867 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -2498,7 +2498,11 @@ def has_bottom(self): sage: Q.has_bottom() True - .. SEEALSO:: :meth:`bottom`, :meth:`has_top`, :meth:`is_bounded` + .. SEEALSO:: + + - Dual Property: :meth:`has_top` + - Stronger properties: :meth:`is_bounded` + - Other: :meth:`bottom` TESTS:: @@ -2550,7 +2554,11 @@ def has_top(self): sage: Q.has_top() True - .. SEEALSO:: :meth:`top`, :meth:`has_bottom`, :meth:`is_bounded` + .. SEEALSO:: + + - Dual Property: :meth:`has_bottom` + - Stronger properties: :meth:`is_bounded` + - Other: :meth:`top` TESTS:: @@ -2658,7 +2666,10 @@ def is_bounded(self): sage: Q.is_bounded() True - .. SEEALSO:: :meth:`has_bottom`, :meth:`has_top` + .. SEEALSO:: + + - Weaker properties: :meth:`has_bottom`, :meth:`has_top` + - Other: :meth:`with_bounds`, :meth:`without_bounds` TESTS:: @@ -3653,7 +3664,10 @@ def is_meet_semilattice(self, certificate=False): sage: V.is_meet_semilattice(certificate=True) (False, ((2, 2, 1), (3, 1, 1))) - .. SEEALSO:: :meth:`is_join_semilattice`, :meth:`~sage.categories.finite_posets.FinitePosets.ParentMethods.is_lattice` + .. SEEALSO:: + + - Dual property: :meth:`is_join_semilattice` + - Stronger properties: :meth:`~sage.categories.finite_posets.FinitePosets.ParentMethods.is_lattice` TESTS:: @@ -3721,7 +3735,10 @@ def is_join_semilattice(self, certificate=False): sage: P.is_join_semilattice(certificate=True) (False, (2, 1)) - .. SEEALSO:: :meth:`is_meet_semilattice`, :meth:`~sage.categories.finite_posets.FinitePosets.ParentMethods.is_lattice` + .. SEEALSO:: + + - Dual property: :meth:`is_meet_semilattice` + - Stronger properties: :meth:`~sage.categories.finite_posets.FinitePosets.ParentMethods.is_lattice` TESTS:: From 7bb64012a9b5ec1f1fe7819797e4fed70ffb390b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 20 Jul 2017 15:43:09 +0200 Subject: [PATCH 090/184] Remove useless function list_composition() --- src/sage/graphs/cliquer.pyx | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/sage/graphs/cliquer.pyx b/src/sage/graphs/cliquer.pyx index 1e78c3837d9..d84f1441858 100644 --- a/src/sage/graphs/cliquer.pyx +++ b/src/sage/graphs/cliquer.pyx @@ -89,7 +89,7 @@ def max_clique(graph): sig_free(list) graph_free(g) - return list_composition(b,d_inv) + return [d_inv[k] for k in b] # computes all the maximum clique of a graph and return its list @@ -162,7 +162,7 @@ def all_max_clique(graph): if(list[i]!=-1): c.append(list[i]) else: - b.append(list_composition(c,d_inv)) + b.append([d_inv[k] for k in c]) c=[] sig_free(list) @@ -214,22 +214,3 @@ def clique_number(graph): graph_free(g) sig_off() return c - - -def list_composition(a,b): - """ - Composes a list ``a`` with a map ``b``. - - EXAMPLES:: - - sage: from sage.graphs.cliquer import list_composition - sage: list_composition([1,3,'a'], {'a':'b', 1:2, 2:3, 3:4}) - [2, 4, 'b'] - - """ - value=[] - for i in a: - value.append(b[i]) - return value - - From 44084cdcbca46c40c989c7a49828f2e149328e66 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 20 Jul 2017 13:44:23 +0200 Subject: [PATCH 091/184] Assign methods to classes directly --- src/sage/graphs/base/boost_graph.pyx | 2 + src/sage/graphs/base/static_dense_graph.pyx | 1 + src/sage/graphs/base/static_sparse_graph.pyx | 1 + src/sage/graphs/chrompoly.pyx | 1 + src/sage/graphs/cliquer.pyx | 2 +- src/sage/graphs/comparability.pyx | 2 + src/sage/graphs/digraph.py | 10 +-- src/sage/graphs/distances_all_pairs.pyx | 1 + src/sage/graphs/generic_graph.py | 18 ++--- src/sage/graphs/graph.py | 73 +++++-------------- .../graph_decompositions/graph_products.pyx | 1 + .../graphs/graph_decompositions/rankwidth.pyx | 1 + .../vertex_separation.pyx | 1 + src/sage/graphs/matchpoly.pyx | 1 + src/sage/graphs/spanning_tree.pyx | 27 +++---- src/sage/graphs/tutte_polynomial.py | 2 +- src/sage/graphs/weakly_chordal.pyx | 1 + src/sage/groups/finitely_presented.py | 17 ++--- src/sage/groups/matrix_gps/matrix_group.py | 19 +++-- src/sage/groups/perm_gps/permgroup.py | 4 +- .../libs/linkages/padics/unram_shared.pxi | 7 ++ src/sage/rings/padics/qadic_flint_CA.pyx | 8 +- src/sage/rings/padics/qadic_flint_CR.pyx | 8 +- src/sage/rings/padics/qadic_flint_FM.pyx | 8 +- src/sage/rings/padics/qadic_flint_FP.pyx | 8 +- src/sage/structure/element.pyx | 3 - 26 files changed, 91 insertions(+), 136 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 796c966b10b..3be998c6ce5 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -46,6 +46,7 @@ Functions # http://www.gnu.org/licenses/ #***************************************************************************** +cimport cython from cysignals.signals cimport sig_check, sig_on, sig_off @@ -294,6 +295,7 @@ cpdef clustering_coeff(g, vertices=None): return (average_clustering, clust_v_sage) +@cython.binding(True) cpdef dominator_tree(g, root, return_dict=False): r""" Uses Boost to compute the dominator tree of ``g``, rooted at ``root``. diff --git a/src/sage/graphs/base/static_dense_graph.pyx b/src/sage/graphs/base/static_dense_graph.pyx index 963d25970f2..a073d6dcfd4 100644 --- a/src/sage/graphs/base/static_dense_graph.pyx +++ b/src/sage/graphs/base/static_dense_graph.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Static dense graphs diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index b3aa2f73dff..8e692cb9968 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Static Sparse Graphs diff --git a/src/sage/graphs/chrompoly.pyx b/src/sage/graphs/chrompoly.pyx index c5027ae830b..f82eb6fa562 100644 --- a/src/sage/graphs/chrompoly.pyx +++ b/src/sage/graphs/chrompoly.pyx @@ -1,3 +1,4 @@ +# cython: binding=True """ Chromatic Polynomial diff --git a/src/sage/graphs/cliquer.pyx b/src/sage/graphs/cliquer.pyx index d84f1441858..007c2314fbf 100644 --- a/src/sage/graphs/cliquer.pyx +++ b/src/sage/graphs/cliquer.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Interface with Cliquer (clique-related problems) @@ -24,7 +25,6 @@ Methods ------- """ - #***************************************************************************** # Copyright (C) 2009 Nathann Cohen # diff --git a/src/sage/graphs/comparability.pyx b/src/sage/graphs/comparability.pyx index 5d7ec382f85..b3582df4d80 100644 --- a/src/sage/graphs/comparability.pyx +++ b/src/sage/graphs/comparability.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Comparability and permutation graphs @@ -209,6 +210,7 @@ Methods # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + from __future__ import print_function from cysignals.memory cimport sig_free diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 8c29602da6b..e99eb1b861b 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -3510,10 +3510,6 @@ def flow_polytope(self, edges=None, ends=None): return Polyhedron(ieqs=ineqs, eqns=eqs) -import types - -import sage.graphs.comparability -DiGraph.is_transitive = types.MethodType(sage.graphs.comparability.is_transitive, None, DiGraph) - -from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components -DiGraph.strongly_connected_components = types.MethodType(tarjan_strongly_connected_components, None, DiGraph) + # Aliases to functions defined in other modules + from sage.graphs.comparability import is_transitive + from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components as strongly_connected_components diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 5965dd18fa0..8563cef4ade 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Distances/shortest paths between all pairs of vertices diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index e1abe634213..93f7e4b563d 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -21874,20 +21874,12 @@ def is_cayley(self, return_group = False, mapping = False, else: return c -import types + # Aliases to functions defined in other modules + from sage.graphs.distances_all_pairs import distances_distribution + from sage.graphs.base.boost_graph import dominator_tree + from sage.graphs.base.static_sparse_graph import spectral_radius + from sage.graphs.line_graph import line_graph -import sage.graphs.distances_all_pairs -GenericGraph.distances_distribution = types.MethodType(sage.graphs.distances_all_pairs.distances_distribution, None, GenericGraph) - -from sage.graphs.base.boost_graph import dominator_tree -GenericGraph.dominator_tree = types.MethodType(dominator_tree, None, GenericGraph) - -from sage.graphs.base.static_sparse_graph import spectral_radius -GenericGraph.spectral_radius = types.MethodType(spectral_radius, None, GenericGraph) - -# From Python modules -import sage.graphs.line_graph -GenericGraph.line_graph = sage.graphs.line_graph.line_graph def tachyon_vertex_plot(g, bgcolor=(1,1,1), vertex_colors=None, diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index b63135d8e64..7293790c95f 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -2818,7 +2818,8 @@ def treewidth(self,k=None,certificate=False,algorithm=None): if k is not None and k >= g.order()-1: if certificate: - return Graph({sage.sets.set.Set(g.vertices()):[]}, + from sage.sets.set import Set + return Graph({Set(g.vertices()):[]}, name="Tree decomposition") return True @@ -7745,60 +7746,24 @@ def has_perfect_matching(self, algorithm="Edmonds", solver=None, verbose=0): else: raise ValueError('algorithm must be set to "Edmonds", "LP_matching" or "LP"') + # Aliases to functions defined in other modules + from sage.graphs.weakly_chordal import is_long_hole_free, is_long_antihole_free, is_weakly_chordal + from sage.graphs.asteroidal_triples import is_asteroidal_triple_free + from sage.graphs.chrompoly import chromatic_polynomial + from sage.graphs.graph_decompositions.rankwidth import rank_decomposition + from sage.graphs.graph_decompositions.vertex_separation import pathwidth + from sage.graphs.matchpoly import matching_polynomial + from sage.graphs.cliquer import all_max_clique as cliques_maximum + from sage.graphs.spanning_tree import random_spanning_tree + from sage.graphs.graph_decompositions.graph_products import is_cartesian_product + from sage.graphs.distances_all_pairs import is_distance_regular + from sage.graphs.base.static_dense_graph import is_strongly_regular + from sage.graphs.line_graph import is_line_graph + from sage.graphs.tutte_polynomial import tutte_polynomial + from sage.graphs.lovasz_theta import lovasz_theta + from sage.graphs.partial_cube import is_partial_cube + from sage.graphs.orientations import strong_orientations_iterator -# Aliases to functions defined in Cython modules -import types - -import sage.graphs.weakly_chordal -Graph.is_long_hole_free = types.MethodType(sage.graphs.weakly_chordal.is_long_hole_free, None, Graph) -Graph.is_long_antihole_free = types.MethodType(sage.graphs.weakly_chordal.is_long_antihole_free, None, Graph) -Graph.is_weakly_chordal = types.MethodType(sage.graphs.weakly_chordal.is_weakly_chordal, None, Graph) - -import sage.graphs.asteroidal_triples -Graph.is_asteroidal_triple_free = types.MethodType(sage.graphs.asteroidal_triples.is_asteroidal_triple_free, None, Graph) - -import sage.graphs.chrompoly -Graph.chromatic_polynomial = types.MethodType(sage.graphs.chrompoly.chromatic_polynomial, None, Graph) - -import sage.graphs.graph_decompositions.rankwidth -Graph.rank_decomposition = types.MethodType(sage.graphs.graph_decompositions.rankwidth.rank_decomposition, None, Graph) - -import sage.graphs.graph_decompositions.vertex_separation -Graph.pathwidth = types.MethodType(sage.graphs.graph_decompositions.vertex_separation.pathwidth, None, Graph) - -import sage.graphs.matchpoly -Graph.matching_polynomial = types.MethodType(sage.graphs.matchpoly.matching_polynomial, None, Graph) - -import sage.graphs.cliquer -Graph.cliques_maximum = types.MethodType(sage.graphs.cliquer.all_max_clique, None, Graph) - -import sage.graphs.spanning_tree -Graph.random_spanning_tree = types.MethodType(sage.graphs.spanning_tree.random_spanning_tree, None, Graph) - -import sage.graphs.graph_decompositions.graph_products -Graph.is_cartesian_product = types.MethodType(sage.graphs.graph_decompositions.graph_products.is_cartesian_product, None, Graph) - -import sage.graphs.distances_all_pairs -Graph.is_distance_regular = types.MethodType(sage.graphs.distances_all_pairs.is_distance_regular, None, Graph) - -import sage.graphs.base.static_dense_graph -Graph.is_strongly_regular = types.MethodType(sage.graphs.base.static_dense_graph.is_strongly_regular, None, Graph) - -# From Python modules -import sage.graphs.line_graph -Graph.is_line_graph = sage.graphs.line_graph.is_line_graph - -from sage.graphs.tutte_polynomial import tutte_polynomial -Graph.tutte_polynomial = tutte_polynomial - -from sage.graphs.lovasz_theta import lovasz_theta -Graph.lovasz_theta = lovasz_theta - -from sage.graphs.partial_cube import is_partial_cube -Graph.is_partial_cube = is_partial_cube - -from sage.graphs.orientations import strong_orientations_iterator -Graph.strong_orientations_iterator = strong_orientations_iterator _additional_categories = { Graph.is_long_hole_free : "Graph properties", diff --git a/src/sage/graphs/graph_decompositions/graph_products.pyx b/src/sage/graphs/graph_decompositions/graph_products.pyx index f290a8f91ba..97ee3166f0e 100644 --- a/src/sage/graphs/graph_decompositions/graph_products.pyx +++ b/src/sage/graphs/graph_decompositions/graph_products.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Products of graphs diff --git a/src/sage/graphs/graph_decompositions/rankwidth.pyx b/src/sage/graphs/graph_decompositions/rankwidth.pyx index 26871baad48..768bab26742 100644 --- a/src/sage/graphs/graph_decompositions/rankwidth.pyx +++ b/src/sage/graphs/graph_decompositions/rankwidth.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Rank Decompositions of graphs diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index 4683b242a17..90d994ecb5c 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Vertex separation diff --git a/src/sage/graphs/matchpoly.pyx b/src/sage/graphs/matchpoly.pyx index 8c6b87fca3c..96e949c1fd7 100644 --- a/src/sage/graphs/matchpoly.pyx +++ b/src/sage/graphs/matchpoly.pyx @@ -1,3 +1,4 @@ +# cython: binding=True """ Matching Polynomial diff --git a/src/sage/graphs/spanning_tree.pyx b/src/sage/graphs/spanning_tree.pyx index 690dcaaeed7..f332aac8f93 100644 --- a/src/sage/graphs/spanning_tree.pyx +++ b/src/sage/graphs/spanning_tree.pyx @@ -52,24 +52,20 @@ Methods ------- """ -########################################################################### -# Copyright (c) 2007 Jason Grout -# Copyright (c) 2009 Mike Hansen -# Copyright (c) 2010 Gregory McWhirter -# Copyright (c) 2010 Minh Van Nguyen +#***************************************************************************** +# Copyright (c) 2007 Jason Grout +# Copyright (c) 2009 Mike Hansen +# Copyright (c) 2010 Gregory McWhirter +# Copyright (c) 2010 Minh Van Nguyen # -# This program is free software; you can redistribute it and/or modify +# 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 +# the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# http://www.gnu.org/licenses/ -########################################################################### +# http://www.gnu.org/licenses/ +#***************************************************************************** + +cimport cython cpdef kruskal(G, wfunction=None, bint check=False): @@ -340,6 +336,7 @@ cpdef kruskal(G, wfunction=None, bint check=False): return sorted(T) +@cython.binding(True) def random_spanning_tree(self, output_as_graph=False): r""" Return a random spanning tree of the graph. diff --git a/src/sage/graphs/tutte_polynomial.py b/src/sage/graphs/tutte_polynomial.py index 9df75d740ed..a67e3ab5747 100644 --- a/src/sage/graphs/tutte_polynomial.py +++ b/src/sage/graphs/tutte_polynomial.py @@ -41,7 +41,6 @@ from contextlib import contextmanager from sage.misc.lazy_attribute import lazy_attribute -from sage.graphs.graph import Graph from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ from sage.misc.decorators import sage_wraps @@ -197,6 +196,7 @@ def underlying_graph(G): sage: underlying_graph(G).edges() [(0, 1, None)] """ + from sage.graphs.graph import Graph g = Graph() g.allow_loops(True) for edge in set(G.edges(labels=False)): diff --git a/src/sage/graphs/weakly_chordal.pyx b/src/sage/graphs/weakly_chordal.pyx index f3b2faa1838..c8aad629b95 100644 --- a/src/sage/graphs/weakly_chordal.pyx +++ b/src/sage/graphs/weakly_chordal.pyx @@ -1,3 +1,4 @@ +# cython: binding=True r""" Weakly chordal graphs diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index f2ebf57ac23..2171f8f4d9c 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -118,17 +118,15 @@ - Miguel Angel Marco Buzunariz """ -############################################################################## +#***************************************************************************** # Copyright (C) 2012 Miguel Angel Marco Buzunariz # -# Distributed under the terms of the GNU General Public License (GPL) -# -# The full text of the GPL is available at: -# +# 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/ -############################################################################## - -import types +#***************************************************************************** from sage.groups.group import Group from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP @@ -147,7 +145,6 @@ from sage.rings.integer_ring import IntegerRing from sage.functions.generalized import sign from sage.matrix.constructor import matrix -from sage.groups.generic import structure_description from sage.categories.morphism import SetMorphism @@ -1574,4 +1571,4 @@ def rewriting_system(self): """ return RewritingSystem(self) -FinitelyPresentedGroup.structure_description = types.MethodType(structure_description, None, FinitelyPresentedGroup) + from sage.groups.generic import structure_description diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index cf5c1ce83f6..a254049958f 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -36,19 +36,19 @@ - Simon King (2010-05): Improve invariant_generators by using GAP for the construction of the Reynolds operator in Singular. """ -from __future__ import absolute_import -############################################################################## +#***************************************************************************** # Copyright (C) 2006 David Joyner and William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# -# The full text of the GPL is available at: -# +# 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 __future__ import absolute_import -import types from sage.rings.all import ZZ from sage.rings.integer import is_Integer from sage.rings.ring import is_Ring @@ -61,7 +61,6 @@ from sage.structure.sage_object import SageObject from sage.misc.decorators import rename_keyword from sage.misc.cachefunc import cached_method -from sage.groups.generic import structure_description from sage.groups.group import Group from sage.groups.libgap_wrapper import ParentLibGAP @@ -699,4 +698,4 @@ def _subgroup_constructor(self, libgap_subgroup): return FinitelyGeneratedMatrixGroup_gap(self.degree(), self.base_ring(), libgap_subgroup, ambient=self) -MatrixGroup_gap.structure_description = types.MethodType(structure_description, None, MatrixGroup_gap) + from sage.groups.generic import structure_description diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 4b45c56a7c7..eef59035dd8 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -151,7 +151,6 @@ from sage.categories.all import FiniteEnumeratedSets from sage.groups.conjugacy_classes import ConjugacyClassGAP from sage.functions.other import factorial -from sage.groups.generic import structure_description def load_hap(): """ @@ -4187,7 +4186,8 @@ def upper_central_series(self): UCS = self._gap_().UpperCentralSeriesOfGroup() return [self.subgroup(gap_group=group) for group in UCS] -PermutationGroup_generic.structure_description = types.MethodType(structure_description, None, PermutationGroup_generic) + from sage.groups.generic import structure_description + class PermutationGroup_subgroup(PermutationGroup_generic): """ diff --git a/src/sage/libs/linkages/padics/unram_shared.pxi b/src/sage/libs/linkages/padics/unram_shared.pxi index 98521ec3769..4f0fe5361d4 100644 --- a/src/sage/libs/linkages/padics/unram_shared.pxi +++ b/src/sage/libs/linkages/padics/unram_shared.pxi @@ -1,3 +1,6 @@ +cimport cython + +@cython.binding(True) def frobenius_unram(self, arithmetic=True): """ Returns the image of this element under the Frobenius automorphism @@ -54,6 +57,8 @@ def frobenius_unram(self, arithmetic=True): ans += ppow * L[m]**exp return ans + +@cython.binding(True) def norm_unram(self, base = None): """ Return the absolute or relative norm of this element. @@ -133,6 +138,8 @@ def norm_unram(self, base = None): 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() + +@cython.binding(True) def trace_unram(self, base = None): """ Return the absolute or relative trace of this element. diff --git a/src/sage/rings/padics/qadic_flint_CA.pyx b/src/sage/rings/padics/qadic_flint_CA.pyx index 745fd3233c6..fec455a14cf 100644 --- a/src/sage/rings/padics/qadic_flint_CA.pyx +++ b/src/sage/rings/padics/qadic_flint_CA.pyx @@ -1,5 +1,3 @@ -from types import MethodType - include "sage/libs/linkages/padics/fmpz_poly_unram.pxi" include "sage/libs/linkages/padics/unram_shared.pxi" include "CA_template.pxi" @@ -24,9 +22,9 @@ cdef class PowComputer_(PowComputer_flint_unram): PowComputer_flint_unram.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) cdef class qAdicCappedAbsoluteElement(CAElement): - frobenius = MethodType(frobenius_unram, None, qAdicCappedAbsoluteElement) - trace = MethodType(trace_unram, None, qAdicCappedAbsoluteElement) - norm = MethodType(norm_unram, None, qAdicCappedAbsoluteElement) + frobenius = frobenius_unram + trace = trace_unram + norm = norm_unram def matrix_mod_pn(self): """ diff --git a/src/sage/rings/padics/qadic_flint_CR.pyx b/src/sage/rings/padics/qadic_flint_CR.pyx index 988316ab748..0c48a5a7ec9 100644 --- a/src/sage/rings/padics/qadic_flint_CR.pyx +++ b/src/sage/rings/padics/qadic_flint_CR.pyx @@ -1,5 +1,3 @@ -from types import MethodType - include "sage/libs/linkages/padics/fmpz_poly_unram.pxi" include "sage/libs/linkages/padics/unram_shared.pxi" include "CR_template.pxi" @@ -24,9 +22,9 @@ cdef class PowComputer_(PowComputer_flint_unram): PowComputer_flint_unram.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) cdef class qAdicCappedRelativeElement(CRElement): - frobenius = MethodType(frobenius_unram, None, qAdicCappedRelativeElement) - trace = MethodType(trace_unram, None, qAdicCappedRelativeElement) - norm = MethodType(norm_unram, None, qAdicCappedRelativeElement) + frobenius = frobenius_unram + trace = trace_unram + norm = norm_unram def matrix_mod_pn(self): """ diff --git a/src/sage/rings/padics/qadic_flint_FM.pyx b/src/sage/rings/padics/qadic_flint_FM.pyx index 0075726647e..f8210a8d1ed 100644 --- a/src/sage/rings/padics/qadic_flint_FM.pyx +++ b/src/sage/rings/padics/qadic_flint_FM.pyx @@ -1,5 +1,3 @@ -from types import MethodType - include "sage/libs/linkages/padics/fmpz_poly_unram.pxi" include "sage/libs/linkages/padics/unram_shared.pxi" include "FM_template.pxi" @@ -24,9 +22,9 @@ cdef class PowComputer_(PowComputer_flint_unram): PowComputer_flint_unram.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) cdef class qAdicFixedModElement(FMElement): - frobenius = MethodType(frobenius_unram, None, qAdicFixedModElement) - trace = MethodType(trace_unram, None, qAdicFixedModElement) - norm = MethodType(norm_unram, None, qAdicFixedModElement) + frobenius = frobenius_unram + trace = trace_unram + norm = norm_unram def matrix_mod_pn(self): """ diff --git a/src/sage/rings/padics/qadic_flint_FP.pyx b/src/sage/rings/padics/qadic_flint_FP.pyx index 0137a9f2da6..3f6b66568e3 100644 --- a/src/sage/rings/padics/qadic_flint_FP.pyx +++ b/src/sage/rings/padics/qadic_flint_FP.pyx @@ -1,5 +1,3 @@ -from types import MethodType - include "sage/libs/linkages/padics/fmpz_poly_unram.pxi" include "sage/libs/linkages/padics/unram_shared.pxi" include "FP_template.pxi" @@ -24,9 +22,9 @@ cdef class PowComputer_(PowComputer_flint_unram): PowComputer_flint_unram.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) cdef class qAdicFloatingPointElement(FPElement): - frobenius = MethodType(frobenius_unram, None, qAdicFloatingPointElement) - trace = MethodType(trace_unram, None, qAdicFloatingPointElement) - norm = MethodType(norm_unram, None, qAdicFloatingPointElement) + frobenius = frobenius_unram + trace = trace_unram + norm = norm_unram def matrix_mod_pn(self): """ diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index a0d857fb8b0..bdbe440b499 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -290,9 +290,6 @@ cdef dict _coerce_op_symbols = dict( add='+', sub='-', mul='*', div='/', truediv='/', floordiv='//', mod='%', iadd='+', isub='-', imul='*', idiv='/', itruediv='/', ifloordiv='//') -cdef MethodType -from types import MethodType - from sage.structure.richcmp cimport rich_to_bool from sage.structure.coerce cimport py_scalar_to_element from sage.structure.parent cimport Parent From ef4ed3320f13fad45238ba3095e9ab89da99c505 Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 20 Jul 2017 16:51:07 +0000 Subject: [PATCH 092/184] Fix SEEALSO: --- src/sage/rings/padics/padic_generic_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 0256c6a12fd..5d2e25b5a77 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -1501,7 +1501,7 @@ cdef class pAdicGenericElement(LocalGenericElement): approximations to the unit part with numerator and denominator bounded by ``sqrt(p^absprec / 2)``. - .. SEEALSO: + .. SEEALSO:: :meth:`_rational_` From 1754b445f154e165e2545af99a5882da8cdcb01e Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 20 Jul 2017 17:57:04 +0000 Subject: [PATCH 093/184] Fix exact=True errors in the right branch --- src/sage/rings/padics/local_generic.py | 6 +++--- src/sage/rings/padics/padic_extension_generic.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 416edce1c8e..900a37d551e 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -310,19 +310,19 @@ def change(self, **kwds): sage: x = polygen(ZZ) sage: R. = Zp(5).extension(x^2 + 2) sage: S = R.change(p=7) - sage: S.defining_polynomial() + sage: S.defining_polynomial(exact=True) x^2 + 2 sage: A. = Zp(5)[] sage: R. = Zp(5).extension(y^2 + 2) sage: S = R.change(p=7) - sage: S.defining_polynomial() + sage: S.defining_polynomial(exact=True) y^2 + 2 :: sage: R. = Zq(5^3) sage: S = R.change(prec=50) - sage: S.defining_polynomial() + sage: S.defining_polynomial(exact=True) x^3 + 3*x + 3 """ # We support both print_* and * for *=mode, pos, sep, alphabet diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index deb75665232..6285240bcdd 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -316,7 +316,7 @@ def construction(self): """ from sage.categories.pushout import AlgebraicExtensionFunctor as AEF print_mode = self._printer.dict() - return (AEF([self.defining_polynomial()], [self.variable_name()], + return (AEF([self.defining_polynomial(exact=True)], [self.variable_name()], prec=self.precision_cap(), print_mode=self._printer.dict(), implementation=self._implementation), self.base_ring()) From 18169e4708fe8a62e6f60785db16eabd5a4e9bb2 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 18:45:05 +0000 Subject: [PATCH 094/184] =?UTF-8?q?R[x]=E2=86=92S[x]=20is=20injective/surj?= =?UTF-8?q?ective=20iff=20R=E2=86=92S=20is?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../polynomial_ring_homomorphism.pyx | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx b/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx index 45de93b2fb0..18140a24981 100644 --- a/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx +++ b/src/sage/rings/polynomial/polynomial_ring_homomorphism.pyx @@ -94,3 +94,31 @@ cdef class PolynomialRingHomomorphism_from_base(RingHomomorphism_from_base): return P({a: f(b) for a, b in x.dict().iteritems()}, *args, **kwds) else: return P([f(b) for b in x], *args, **kwds) + + def is_injective(self): + r""" + Return whether this morphism is injective. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: S. = QQ[] + sage: R.hom(S).is_injective() + True + + """ + return self.underlying_map().is_injective() + + def is_surjective(self): + r""" + Return whether this morphism is surjective. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: S. = Zmod(2)[] + sage: R.hom(S).is_surjective() + True + + """ + return self.underlying_map().is_surjective() From f5583f680a031dd20544a3dea950f700538d6bc8 Mon Sep 17 00:00:00 2001 From: Ander Steele Date: Thu, 20 Jul 2017 18:56:56 +0000 Subject: [PATCH 095/184] Add documentation --- src/sage/rings/padics/misc.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index 2ae896cff5b..d77345fcb98 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -1,4 +1,4 @@ -r""" +""" Miscellaneous Functions This file contains several miscellaneous functions used by `p`-adics. @@ -38,7 +38,15 @@ def gauss_sum(a, p, f, prec = 20): `g_q(a)` is defined by `g_q(a)= \sum_{u\in F_q^*} \omega(u)^{(-a)} \zeta_q^u` where `q = p^f`, `\omega` is the Teichmuller character and `\zeta_q` is some arbitrary - choice of primitive `q`-th root of unity + choice of primitive `q`-th root of unity. The computation is adapted from the main theorem + in Alain Robert's paper The Gross-Koblitz formula revisited, + Rend. Sem. Mat. Univ. Padova 105 (2001), 157--170. + + Let `p` be a prime, `f` a positive integer, `q=p^f`, and `\pi` be a root of `f(x)=x^{p-1}+p`. + Let `0\leq a Date: Thu, 20 Jul 2017 21:20:19 +0200 Subject: [PATCH 096/184] trac 23493 details --- src/sage/categories/finite_posets.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 6909e37568b..af85790a6d4 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -51,11 +51,10 @@ class ParentMethods: def is_lattice(self): r""" - Return ``True`` if the poset is lattice, and - ``False`` otherwise. + Return whether the poset is a lattice. - A poset is lattice if every pair of elements have - both the least upper bound ("join") and the greatest lower bound + A poset is a lattice if all pairs of elements have + both a least upper bound ("join") and a greatest lower bound ("meet") in the poset. EXAMPLES:: @@ -93,8 +92,7 @@ def is_lattice(self): def is_selfdual(self): r""" - Return ``True`` if the poset is *self-dual*, and - ``False`` otherwise. + Return whether the poset is *self-dual*. A poset is self-dual if it is isomorphic to its dual poset. From a31c4754d74bf0ff56d1f58a20d74f76a3c204c2 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Jul 2017 20:45:18 +0100 Subject: [PATCH 097/184] Fix ticket 23499 by adding functions to test if a number field is abelian and if so, to compute its conductor. --- src/sage/rings/number_field/number_field.py | 115 +++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index c5cfe2c731f..b072fa98977 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -29,6 +29,9 @@ - Peter Bruin (2016-06): make number fields fully satisfy unique representation +- John Jones (2017-07): improve check for is_galois(), add is_abelian(), building on work in patch by Chris Wuthrich + + .. note:: Unlike in PARI/GP, class group computations *in Sage* do *not* by default @@ -2709,6 +2712,76 @@ def algebraic_closure(self): """ return sage.rings.all.QQbar + @cached_method + def conductor(self, check_abelian=True): + r""" + Computes the conductor of the abelian field `K`. + If check_abelian is set to false and the field is not an + abelian extension of `\mathbb{Q}`, the output is not meaningful. + + INPUT: Integer which is the conductor of the field. + - ``check_abelian`` - a boolean (default: ``True``); check to see that this is an abelian extension of `\mathbb{Q}` + + OUTPUT: Integer which is the conductor of the field. + + EXAMPLES:: + + sage: K = CyclotomicField(27) + sage: k = K.subfields(9)[0][0] + sage: k.conductor() + 27 + sage: K. = NumberField(x^3+x^2-2*x-1) + sage: K.conductor() + 7 + sage: K. = NumberField(x^3+x^2-36*x-4) + sage: K.conductor() + 109 + sage: K = CyclotomicField(48) + sage: k = K.subfields(16)[0][0] + sage: k.conductor() + 48 + sage: NumberField(x,'a').conductor() + 1 + sage: NumberField(x^8 - 8*x^6 + 19*x^4 - 12*x^2 + 1,'a').conductor() + 40 + sage: NumberField(x^8 + 7*x^4 + 1,'a').conductor() + 40 + sage: NumberField(x^8 - 40*x^6 + 500*x^4 - 2000*x^2 + 50,'a').conductor() + 160 + + ALGORITHM: For odd primes, it is easy to compute from the ramification + index because the p-Sylow subgroup is cyclic. For p=2, there + are two choices for a given ramification index. They can be + distinguished by the parity of the exponent in the discriminant + of a 2-adic completion. + """ + m = 1 + if check_abelian: + if not self.is_abelian(): + raise ValueError, "The conductor is only defined for abelian fields" + + try: + De = self.__disc + except AttributeError: + De = self.polynomial().discriminant() + A = De.numerator().prime_factors()+De.denominator().prime_factors() + else: + A = De.prime_factors() + + for p in A: + R = self.maximal_order(p) + e = R.fractional_ideal(p).prime_factors()[0].ramification_index() + if e!= 1: + if p==2: + m *= e*2 + c = R.discriminant().valuation(2) + c /= self.polynomial().degree()/e + if is_odd(c): + m *= 2 + else: + m *= p**(e.valuation(p)+1) + return m + def latex_variable_name(self, name=None): """ Return the latex representation of the variable name for this @@ -4967,7 +5040,45 @@ def is_galois(self): sage: NumberField(x^3 + 2, 'a').is_galois() False """ - return self.galois_group(type="pari").order() == self.degree() + #return self.galois_group(type="pari").order() == self.degree() + if self.degree() < 12: + return self.galois_group(type='pari').order() == self.degree() + else: + return len(self.automorphisms()) == self.degree() + + @cached_method + def is_abelian(self): + r""" + Return True if this number field is an abelian Galois extension of + `\QQ`. + + EXAMPLES:: + + sage: NumberField(x^2 + 1, 'i').is_abelian() + True + sage: NumberField(x^3 + 2, 'a').is_abelian() + False + sage: NumberField(x^3 + x^2 - 2*x - 1, 'a').is_abelian() + True + sage: NumberField(x^6 + 40*x^3 + 1372, 'a').is_abelian() + False + sage: NumberField(x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1, 'a').is_abelian() + True + """ + + if not self.is_galois(): + return False + + d = self.degree() + if d.is_prime() or d in [1,4,9,25,49,121]: + return True + + if d <= 11: + return self.galois_group().is_abelian() + + pari_pol = pari(self.polynomial()) + return pari_pol.galoisinit().galoisisabelian(1)==1 + @cached_method def galois_group(self, type=None, algorithm='pari', names=None): @@ -5915,7 +6026,7 @@ def units(self, proof=None): sage: A = x^4 - 10*x^3 + 20*5*x^2 - 15*5^2*x + 11*5^3 sage: K = NumberField(A, 'a') sage: K.units() - (1/275*a^3 + 4/55*a^2 - 5/11*a + 3,) + (8/275*a^3 - 12/55*a^2 + 15/11*a - 3,) For big number fields, provably computing the unit group can take a very long time. In this case, one can ask for the From b6ef396f00e8cde354385ded5a9e36934c2016b9 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 21:29:15 +0000 Subject: [PATCH 098/184] Remove obsolete type checks --- src/sage/rings/morphism.pyx | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index c5e8be448f5..9f9944b29e0 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -524,9 +524,6 @@ cdef class RingMap_lift(RingMap): if op not in [Py_EQ, Py_NE]: return NotImplemented - if not isinstance(other, RingMap_lift): - return (op == Py_NE) - # Since they are lifting maps they are determined by their # parents, i.e., by the domain and codomain, since we just # compare those. @@ -962,9 +959,6 @@ cdef class RingHomomorphism_coercion(RingHomomorphism): if op not in [Py_EQ, Py_NE]: return NotImplemented - if not isinstance(other, RingHomomorphism_coercion): - return (op == Py_NE) - # Since they are coercion morphisms they are determined by # their parents, i.e., by the domain and codomain, so we just # compare those. @@ -1177,11 +1171,6 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): sage: loads(dumps(f2)) == f2 True """ - if not isinstance(other, RingHomomorphism_im_gens): - if op in [Py_EQ, Py_NE]: - return (op == Py_NE) - return NotImplemented - return richcmp(self.__im_gens, (other).__im_gens, op) def __hash__(self): @@ -1471,10 +1460,6 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): sage: f1M == loads(dumps(f1M)) True """ - if not isinstance(other, RingHomomorphism_from_base): - if op in [Py_EQ, Py_NE]: - return (op == Py_NE) - return NotImplemented return richcmp(self.__underlying, (other).__underlying, op) def _repr_defn(self): @@ -1650,9 +1635,6 @@ cdef class RingHomomorphism_cover(RingHomomorphism): if op not in [Py_EQ, Py_NE]: return NotImplemented - if not isinstance(other, RingHomomorphism_cover): - return (op == Py_NE) - return richcmp(self.parent(), other.parent(), op) def __hash__(self): @@ -1849,9 +1831,6 @@ cdef class RingHomomorphism_from_quotient(RingHomomorphism): if op not in [Py_EQ, Py_NE]: return NotImplemented - if not isinstance(other, RingHomomorphism_from_quotient): - return (op == Py_NE) - cdef RingHomomorphism_from_quotient left = self cdef RingHomomorphism_from_quotient right = other return richcmp(left.phi, right.phi, op) From 88b954e7c2bc522f4cf5d0d04de424f8296bff76 Mon Sep 17 00:00:00 2001 From: Ander Steele Date: Thu, 20 Jul 2017 21:49:25 +0000 Subject: [PATCH 099/184] Fix modulus for argument a, which depends only on residue class mod(p^f-1) --- src/sage/rings/padics/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/misc.py b/src/sage/rings/padics/misc.py index d77345fcb98..55af8faabc9 100644 --- a/src/sage/rings/padics/misc.py +++ b/src/sage/rings/padics/misc.py @@ -97,7 +97,7 @@ def gauss_sum(a, p, f, prec = 20): """ - a = a % (p**f) + a = a % (p**f-1) R = Zp(p, prec) R_poly = PolynomialRing(R,name='X') X = R_poly.gen() From 5074b2e2a628747bc436e47f5c0fbec5d83e9ef5 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Thu, 20 Jul 2017 21:52:35 +0000 Subject: [PATCH 100/184] Fixed doctest the error message has changed because this now goes through a different morphism. I don't think that's relevant. --- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index b76bbb2c123..b6b2765033c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -10031,7 +10031,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: f.quo_rem(g) Traceback (most recent call last): ... - ArithmeticError: Division non exact (consider coercing to polynomials over the fraction field) + ValueError: fraction must have unit denominator sage: g = 0 sage: f.quo_rem(g) Traceback (most recent call last): From db40536f9ec096530e1e09682b42a8d3cde9c20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Fri, 21 Jul 2017 10:27:15 +0300 Subject: [PATCH 101/184] From n to s. --- src/sage/combinat/posets/poset_examples.py | 2 +- .../combinat/root_system/root_lattice_realization_algebras.py | 2 +- src/sage/combinat/root_system/type_E.py | 2 +- src/sage/combinat/skew_tableau.py | 2 +- src/sage/data_structures/bitset.pxi | 2 +- src/sage/game_theory/catalog_normal_form_games.py | 2 +- src/sage/graphs/generators/classical_geometries.py | 4 ++-- src/sage/lfunctions/zero_sums.pyx | 2 +- src/sage/modular/abvar/abvar.py | 2 +- src/sage/plot/plot3d/introduction.py | 2 +- src/sage/plot/plot3d/shapes.pyx | 2 +- src/sage/repl/rich_output/display_manager.py | 2 +- src/sage/rings/number_field/bdd_height.py | 2 +- src/sage/rings/polynomial/skew_polynomial_ring.py | 2 +- src/sage/tensor/modules/free_module_automorphism.py | 2 +- src/sage/tensor/modules/free_module_morphism.py | 2 +- 16 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 5de15345f0c..0f5f0860779 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -1594,7 +1594,7 @@ def _random_stone_lattice(n): ALGORITHM: Randomly split `n` to some factors. For every factor `p` generate - a random distributive lattice on `p-1` elements and add a new new bottom + a random distributive lattice on `p-1` elements and add a new bottom element to it. Compute the cartesian product of those lattices. """ from sage.arith.misc import factor diff --git a/src/sage/combinat/root_system/root_lattice_realization_algebras.py b/src/sage/combinat/root_system/root_lattice_realization_algebras.py index 1ccea98c0f7..7bbcc832acb 100644 --- a/src/sage/combinat/root_system/root_lattice_realization_algebras.py +++ b/src/sage/combinat/root_system/root_lattice_realization_algebras.py @@ -737,7 +737,7 @@ def demazure_lusztig_operators_on_classical(self, q, q1, q2, convention="antidom # In type BC dual we used q^2 and q elsewhere # Not sure this is the right thing to do or just a workaround ... # This probably makes up for the fact that, in type BC - # dual, the null null coroot is twice Sage's deltacheck + # dual, the null coroot is twice Sage's deltacheck # whereas the null root is delta. So we need to map delta # to q^2 in the q_projection. # Should this go in q_project instead? diff --git a/src/sage/combinat/root_system/type_E.py b/src/sage/combinat/root_system/type_E.py index 7cc835879c9..8df8417a8dd 100644 --- a/src/sage/combinat/root_system/type_E.py +++ b/src/sage/combinat/root_system/type_E.py @@ -159,7 +159,7 @@ def simple_root(self, i): def negative_roots(self): """ - The negative negative roots. + The negative roots. EXAMPLES:: diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 0d343f4d533..aed3661b148 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -1579,7 +1579,7 @@ def is_k_tableau(self, k): def _label_skew(list_of_cells, sk): """ - Return a filled-in standard standard skew tableau given an + Return a filled-in standard skew tableau given an ordered list ``list_of_cells`` of the coordinates to fill in (as pairs) and an empty shape ``sk``. diff --git a/src/sage/data_structures/bitset.pxi b/src/sage/data_structures/bitset.pxi index 24b0ef9d258..090b307082b 100644 --- a/src/sage/data_structures/bitset.pxi +++ b/src/sage/data_structures/bitset.pxi @@ -85,7 +85,7 @@ cdef inline bint bitset_init(bitset_t bits, mp_bitcnt_t size) except -1: cdef inline int bitset_realloc(bitset_t bits, mp_bitcnt_t size) except -1: """ - Reallocate a bitset to size size. If reallocation is larger, new bitset + Reallocate a bitset to size ``size``. If reallocation is larger, new bitset does not contain any of the extra bits. """ cdef mp_size_t limbs_old = bits.limbs diff --git a/src/sage/game_theory/catalog_normal_form_games.py b/src/sage/game_theory/catalog_normal_form_games.py index c0a8aa88587..e60fe48a4db 100644 --- a/src/sage/game_theory/catalog_normal_form_games.py +++ b/src/sage/game_theory/catalog_normal_form_games.py @@ -331,7 +331,7 @@ def AntiCoordinationGame(A=3, a=3, B=5, b=1, C=1, c=5, D=0, d=0): Return a 2 by 2 AntiCoordination Game. An anti coordination game is a particular type of game where the pure Nash - equilibria is for the players to pick different strategies strategies. + equilibria is for the players to pick different strategies. In general these are represented as a normal form game using the following two matrices: diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index dd9f75475ae..8a3f67170ce 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -212,7 +212,7 @@ def _orthogonal_polar_graph(m, q, sign="+", point_type=[0]): r""" A helper function to build ``OrthogonalPolarGraph`` and ``NO2,3,5`` graphs. - See see the `page of + See the `page of Andries Brouwer's website `_. INPUT: @@ -333,7 +333,7 @@ def OrthogonalPolarGraph(m, q, sign="+"): r""" Returns the Orthogonal Polar Graph `O^{\epsilon}(m,q)`. - For more information on Orthogonal Polar graphs, see see the `page of + For more information on Orthogonal Polar graphs, see the `page of Andries Brouwer's website `_. INPUT: diff --git a/src/sage/lfunctions/zero_sums.pyx b/src/sage/lfunctions/zero_sums.pyx index 247781bb180..e036652ca96 100644 --- a/src/sage/lfunctions/zero_sums.pyx +++ b/src/sage/lfunctions/zero_sums.pyx @@ -1228,7 +1228,7 @@ cdef class LFunctionZeroSum_EllipticCurve(LFunctionZeroSum_abstract): if n Date: Fri, 21 Jul 2017 11:08:55 +0200 Subject: [PATCH 102/184] trac 14153 trying to fix the gap doc doctests --- src/sage/interfaces/gap.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 7e765a1f550..3f9bf1f7883 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1330,8 +1330,9 @@ def help(self, s, pager=True): else: tmp_to_use = self._local_tmpfile() self.eval('SetGAPDocTextTheme("none")') - self.eval(r'\$SAGE.tempfile := "%s";'%tmp_to_use) - line = Expect.eval(self, "? %s"%s) + self.eval('GAPInfo.TermEncoding := "UTF-8";') + self.eval(r'\$SAGE.tempfile := "%s";' % tmp_to_use) + line = Expect.eval(self, "? %s" % s) Expect.eval(self, "? 1") match = re.search("Page from (\d+)", line) if match is None: From 0faa9436561f00cf1ed5b9c8a65ca4667032f2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 21 Jul 2017 13:18:13 +0200 Subject: [PATCH 103/184] trac 14153 detect gap encoding --- src/sage/interfaces/gap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 3f9bf1f7883..38d95d7c676 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1330,7 +1330,7 @@ def help(self, s, pager=True): else: tmp_to_use = self._local_tmpfile() self.eval('SetGAPDocTextTheme("none")') - self.eval('GAPInfo.TermEncoding := "UTF-8";') + gap_encoding = str(self('GAPInfo.TermEncoding;')) self.eval(r'\$SAGE.tempfile := "%s";' % tmp_to_use) line = Expect.eval(self, "? %s" % s) Expect.eval(self, "? 1") @@ -1341,7 +1341,7 @@ def help(self, s, pager=True): (sline,) = match.groups() if self.is_remote(): self._get_tmpfile() - F = io.open(self._local_tmpfile(), "r", encoding='utf-8') + F = io.open(self._local_tmpfile(), "r", encoding=gap_encoding) help = F.read() if pager: from IPython.core.page import page From 4a13ad2735307362540128bb6fa081523d6db1d1 Mon Sep 17 00:00:00 2001 From: Mckenzie West Date: Fri, 21 Jul 2017 13:58:20 -0400 Subject: [PATCH 104/184] Added function to find all completely split primes less than B --- src/sage/rings/number_field/number_field.py | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index c5cfe2c731f..3c3139c71c2 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -3313,6 +3313,38 @@ def primes_of_degree_one_list(self, n, num_integer_primes=10000, max_iterations= it = self.primes_of_degree_one_iter() return [ next(it) for i in range(n) ] + def completely_split_primes(self, B = 200): + r""" + Returns a list of rational primes which split completely in the number field `K`. + + INPUT: + + - ``B`` -- a positive integer bound (default: 200) + + OUTPUT: + + A list of all primes ``p < B`` which split completely in ``K``. + + EXAMPLE:: + + sage: K. = NumberField(x^3 - 3*x + 1) + sage: K.completely_split_primes(100) + [17, 19, 37, 53, 71, 73, 89] + + """ + from sage.rings.fast_arith import prime_range + from sage.rings.finite_rings.finite_field_constructor import GF + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.arith.all import factor + split_primes = [] + for p in prime_range(B): + Fp = GF(p) + FpT = PolynomialRing(Fp,'T') + g = FpT(self.defining_polynomial()) + if len(factor(g)) == self.degree(): + split_primes.append(p) + return split_primes + def _is_valid_homomorphism_(self, codomain, im_gens): """ Return whether or not there is a homomorphism defined by the given From bfc11dff7141ac967f07b574589c0c9f7026ed6d Mon Sep 17 00:00:00 2001 From: alexjbest Date: Fri, 21 Jul 2017 21:55:48 +0000 Subject: [PATCH 105/184] Update dokchitser's computel script --- src/ext/pari/dokchitser/computel.gp | 4 ++-- src/ext/pari/dokchitser/computel.gp.template | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ext/pari/dokchitser/computel.gp b/src/ext/pari/dokchitser/computel.gp index 55c50602c27..85f8ce042a0 100644 --- a/src/ext/pari/dokchitser/computel.gp +++ b/src/ext/pari/dokchitser/computel.gp @@ -1,4 +1,4 @@ -/************* ComputeL v1.3.3, 2001-2013, (c) Tim Dokchitser ************/ +/************* ComputeL v1.3.4, 2001-2016, (c) Tim Dokchitser ************/ /**************** computing special values of L-functions ****************/ /* arXiv.org/abs/math.NT/0207280, Exper. Math. 13 (2004), no. 2, 137-150 */ /****** Questions/comments welcome! -> tim.dokchitser@bristol.ac.uk ******/ @@ -353,7 +353,7 @@ cflength(cutoff=1.2)= t = if(t1,(t1+t2)\2,t2); tt = t/cutoff/vA; res = coefgrow(t) * asympconstf*exp(-d*tt^(2/d))*tt^expdifff; - if (t1,if(res>err,t1=t,t2=t),if(reserr,t1=t,t2=t),if(abs(res) tim.dokchitser@bristol.ac.uk ******/ @@ -353,7 +353,7 @@ cflength_$i(cutoff=1.2)= t = if(t1,(t1+t2)\2,t2); tt = t/cutoff/vA_$i; res = coefgrow_$i(t) * asympconstf_$i*exp(-d*tt^(2/d))*tt^expdifff_$i; - if (t1,if(res>err,t1=t,t2=t),if(reserr,t1=t,t2=t),if(abs(res) Date: Fri, 21 Jul 2017 17:19:58 -0500 Subject: [PATCH 106/184] Use morphisms that are guaranteed to be the IdentityMorphism. --- src/sage/categories/morphism.pyx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index 8de43882852..142993d0e7f 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -426,21 +426,19 @@ cdef class IdentityMorphism(Morphism): EXAMPLES:: - sage: ZZ.hom(ZZ).is_surjective() + sage: Hom(ZZ, ZZ).identity().is_surjective() True - """ return True def is_injective(self): r""" - Return whether this morphism is injective + Return whether this morphism is injective. EXAMPLES:: - sage: ZZ.hom(ZZ).is_injective() + sage: Hom(ZZ, ZZ).identity().is_injective() True - """ return True From ffc7371eb9c3ff84e575cf3d31000ce3bd3ee2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 21 Jul 2017 17:43:14 -0500 Subject: [PATCH 107/184] Revert "Remove obsolete type checks" This reverts commit b6ef396f00e8cde354385ded5a9e36934c2016b9. Jeroen noted: Sorry, I was wrong. At least some of the type checks are still needed, because equal parents does not imply equal types here. For example: ``` sage: R. = PolynomialRing(QQ, 2) sage: S. = R.quo(x^2 + y^2) sage: phi = S.cover() sage: alpha = R.hom(R, (0,0)) sage: psi = phi.pre_compose(alpha) sage: parent(psi) is parent(phi) True sage: type(psi) is type(phi) True ``` --- src/sage/rings/morphism.pyx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 9f9944b29e0..c5e8be448f5 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -524,6 +524,9 @@ cdef class RingMap_lift(RingMap): if op not in [Py_EQ, Py_NE]: return NotImplemented + if not isinstance(other, RingMap_lift): + return (op == Py_NE) + # Since they are lifting maps they are determined by their # parents, i.e., by the domain and codomain, since we just # compare those. @@ -959,6 +962,9 @@ cdef class RingHomomorphism_coercion(RingHomomorphism): if op not in [Py_EQ, Py_NE]: return NotImplemented + if not isinstance(other, RingHomomorphism_coercion): + return (op == Py_NE) + # Since they are coercion morphisms they are determined by # their parents, i.e., by the domain and codomain, so we just # compare those. @@ -1171,6 +1177,11 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): sage: loads(dumps(f2)) == f2 True """ + if not isinstance(other, RingHomomorphism_im_gens): + if op in [Py_EQ, Py_NE]: + return (op == Py_NE) + return NotImplemented + return richcmp(self.__im_gens, (other).__im_gens, op) def __hash__(self): @@ -1460,6 +1471,10 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): sage: f1M == loads(dumps(f1M)) True """ + if not isinstance(other, RingHomomorphism_from_base): + if op in [Py_EQ, Py_NE]: + return (op == Py_NE) + return NotImplemented return richcmp(self.__underlying, (other).__underlying, op) def _repr_defn(self): @@ -1635,6 +1650,9 @@ cdef class RingHomomorphism_cover(RingHomomorphism): if op not in [Py_EQ, Py_NE]: return NotImplemented + if not isinstance(other, RingHomomorphism_cover): + return (op == Py_NE) + return richcmp(self.parent(), other.parent(), op) def __hash__(self): @@ -1831,6 +1849,9 @@ cdef class RingHomomorphism_from_quotient(RingHomomorphism): if op not in [Py_EQ, Py_NE]: return NotImplemented + if not isinstance(other, RingHomomorphism_from_quotient): + return (op == Py_NE) + cdef RingHomomorphism_from_quotient left = self cdef RingHomomorphism_from_quotient right = other return richcmp(left.phi, right.phi, op) From 1cf2b44352669236a029492cdac8b2c4393f7da9 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 22 Jul 2017 03:40:46 +0000 Subject: [PATCH 108/184] fix doctest output --- src/sage/rings/morphism.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index cfaa46f3484..80106b41472 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -729,7 +729,7 @@ cdef class RingHomomorphism(RingMap): Defn: x |--> a + b y |--> a - b then - Conversion via FractionFieldElement map: + Coercion map: From: Multivariate Polynomial Ring in a, b over Rational Field To: Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field @@ -764,7 +764,7 @@ cdef class RingHomomorphism(RingMap): From: Multivariate Polynomial Ring in x, y over Rational Field To: Multivariate Polynomial Ring in a, b over Rational Field then - Conversion via FractionFieldElement map: + Coercion map: From: Multivariate Polynomial Ring in a, b over Rational Field To: Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field From faed28f4a7f263b8b84c78bbb96f28c8218ef583 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 22 Jul 2017 03:58:11 +0000 Subject: [PATCH 109/184] Accept keyword parameters and speed up the most trivial case --- src/sage/rings/fraction_field.py | 37 ++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 58c9ce8bf55..d3cd513709b 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -947,7 +947,7 @@ class FractionFieldEmbeddingSection(Section): sage: TestSuite(f).run() """ - def _call_(self, x): + def _call_(self, x, check=True): r""" Evaluate this map at ``x``. @@ -962,13 +962,42 @@ def _call_(self, x): sage: f(1/x) Traceback (most recent call last): ... - ValueError: fraction must have unit denominator + TypeError: fraction must have unit denominator + + TESTS: + + Over inexact rings, we have to take the precision of the denominators + into account:: + + sage: R=ZpCR(2) + sage: S. = R[] + sage: f = x/S(R(3,absprec=2)) + sage: R(f) + (1 + 2 + O(2^2))*x """ - if not x.denominator().is_unit(): - raise ValueError("fraction must have unit denominator") + if self.codomain().is_exact() and x.denominator().is_one(): + return x.numerator() + if check and not x.denominator().is_unit(): + # This should probably a ValueError. + # However, too much existing code is expecting this to throw a + # TypeError, so we decided to keep it for the time being. + raise TypeError("fraction must have unit denominator") return x.numerator() * x.denominator().inverse_of_unit() + def _call_with_args(self, x, args=(), kwds={}): + r""" + Evaluation this map at ``x``. + + INPUT: + + - ``check`` -- whether or not to check + """ + check = kwds.pop('check', True) + if args or kwds: + raise NotImplementedError("__call__ can not be called with additional arguments other than check=True/False") + return self._call_(x, check=check) + def _richcmp_(self, other, op): r""" Compare this element to ``other`` with respect to ``op``. From 0ba63309c8067c5a2a94736c4e89275fb8df1902 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 22 Jul 2017 05:42:27 +0000 Subject: [PATCH 110/184] fix typo --- src/sage/rings/fraction_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index d3cd513709b..006bb6eba1a 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -972,7 +972,7 @@ def _call_(self, x, check=True): sage: R=ZpCR(2) sage: S. = R[] sage: f = x/S(R(3,absprec=2)) - sage: R(f) + sage: S(f) (1 + 2 + O(2^2))*x """ From b2acd67fd30e83d9efe75e01bb30c8cc0fea41e1 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 22 Jul 2017 05:42:37 +0000 Subject: [PATCH 111/184] reverted to old exception messages in doctest --- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index b6b2765033c..b76bbb2c123 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -10031,7 +10031,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: f.quo_rem(g) Traceback (most recent call last): ... - ValueError: fraction must have unit denominator + ArithmeticError: Division non exact (consider coercing to polynomials over the fraction field) sage: g = 0 sage: f.quo_rem(g) Traceback (most recent call last): From 04db2514f171f58606ed41fa751ecb0bb4c996be Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 22 Jul 2017 05:58:03 +0000 Subject: [PATCH 112/184] fix whitespace --- src/sage/rings/fraction_field.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 006bb6eba1a..726abc2c5d9 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -822,7 +822,7 @@ class FractionFieldEmbedding(DefaultConvertMap_unique): Coercion map: From: Univariate Polynomial Ring in x over Rational Field To: Fraction Field of Univariate Polynomial Ring in x over Rational Field - + TESTS:: sage: from sage.rings.fraction_field import FractionFieldEmbedding @@ -897,7 +897,7 @@ def _richcmp_(self, other, op): sage: f = R.fraction_field().coerce_map_from(R) sage: S. = GF(2)[] sage: g = S.fraction_field().coerce_map_from(S) - + sage: f == g # indirect doctest False sage: f == f @@ -938,7 +938,7 @@ class FractionFieldEmbeddingSection(Section): Section map: From: Fraction Field of Univariate Polynomial Ring in x over Rational Field To: Univariate Polynomial Ring in x over Rational Field - + TESTS:: sage: from sage.rings.fraction_field import FractionFieldEmbeddingSection @@ -1008,7 +1008,7 @@ def _richcmp_(self, other, op): sage: f = R.fraction_field().coerce_map_from(R).section() sage: S. = GF(2)[] sage: g = S.fraction_field().coerce_map_from(S).section() - + sage: f == g # indirect doctest False sage: f == f From 79bac8fb2b798928270cd4d8d35bab8add32b124 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 22 Jul 2017 05:58:41 +0000 Subject: [PATCH 113/184] fixed grammar --- src/sage/rings/fraction_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 726abc2c5d9..5a74ca045a7 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -979,7 +979,7 @@ def _call_(self, x, check=True): if self.codomain().is_exact() and x.denominator().is_one(): return x.numerator() if check and not x.denominator().is_unit(): - # This should probably a ValueError. + # This should probably be a ValueError. # However, too much existing code is expecting this to throw a # TypeError, so we decided to keep it for the time being. raise TypeError("fraction must have unit denominator") From a4556481b365f3b7280a656c73b62132c753ef49 Mon Sep 17 00:00:00 2001 From: Adele Bourgeois Date: Sat, 22 Jul 2017 10:26:48 -0400 Subject: [PATCH 114/184] Fix Input of conductor method in the documentation --- src/sage/rings/number_field/number_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index b072fa98977..13e3fe9079f 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -2719,7 +2719,7 @@ def conductor(self, check_abelian=True): If check_abelian is set to false and the field is not an abelian extension of `\mathbb{Q}`, the output is not meaningful. - INPUT: Integer which is the conductor of the field. + INPUT: - ``check_abelian`` - a boolean (default: ``True``); check to see that this is an abelian extension of `\mathbb{Q}` OUTPUT: Integer which is the conductor of the field. From 9ab81230f814b7e24d8f71a6abf3a85b24eb9796 Mon Sep 17 00:00:00 2001 From: alexjbest Date: Sun, 23 Jul 2017 04:43:01 +0000 Subject: [PATCH 115/184] added doctest for dokchitser update 1.3.3->1.3.4, num_coeffs now works when spectral parameters have non-zero imaginary part, e.g. for maass forms --- src/sage/lfunctions/dokchitser.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/lfunctions/dokchitser.py b/src/sage/lfunctions/dokchitser.py index 48c20befec4..94d21848b28 100644 --- a/src/sage/lfunctions/dokchitser.py +++ b/src/sage/lfunctions/dokchitser.py @@ -365,6 +365,11 @@ def num_coeffs(self, T=1): sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 + + Verify that ``num_coeffs`` works with non-real spectral parameters, e.g. for the L-function of the level 10 Maass form with eigenvalue 2.7341055592527126 + + sage: Dokchitser(conductor=10, gammaV=[2.7341055592527126*i, -2.7341055592527126*i],weight=2,eps=1).num_coeffs() + 26 """ return Integer(self._gp_call_inst('cflength', T)) From fb66f9ffa7e95b8d28774b2da4d2ac03a4d88bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 06:48:08 +0000 Subject: [PATCH 116/184] Fix richcmp there was an else missing - and this is much easier anyway. --- src/sage/rings/fraction_field.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 58c9ce8bf55..fe745b9c469 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -78,7 +78,7 @@ import sage.misc.latex as latex from sage.misc.cachefunc import cached_method -from sage.structure.richcmp import richcmp_not_equal, op_EQ, op_NE +from sage.structure.richcmp import richcmp from sage.structure.parent import Parent from sage.structure.coerce_maps import CallableConvertMap, DefaultConvertMap_unique from sage.categories.basic import QuotientFields @@ -904,13 +904,7 @@ def _richcmp_(self, other, op): True """ - if isinstance(other, FractionFieldEmbedding) and other.domain() is self.domain() and other.codomain() is self.codomain(): - if op == op_EQ: - return True - if op == op_NE: - return False - else: - return richcmp_not_equal(self, other, op) + return richcmp((type(self), self.domain(), self.codomain()), (type(other), other.domain(), other.codomain()), op) def __hash__(self): r""" @@ -986,13 +980,7 @@ def _richcmp_(self, other, op): True """ - if isinstance(other, FractionFieldEmbeddingSection) and other.domain() is self.domain() and other.codomain() is self.codomain(): - if op == op_EQ: - return True - if op == op_NE: - return False - else: - return richcmp_not_equal(self, other, op) + return richcmp((type(self), self.domain(), self.codomain()), (type(other), other.domain(), other.codomain()), op) def __hash__(self): r""" From f3ecd0eeacb31055283b6edab35c8dcc178d96bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 23 Jul 2017 06:55:24 +0000 Subject: [PATCH 117/184] added missing doctest --- src/sage/rings/fraction_field.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 370b563d157..5705ba6e958 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -986,6 +986,14 @@ def _call_with_args(self, x, args=(), kwds={}): INPUT: - ``check`` -- whether or not to check + + EXAMPLES:: + + sage: R. = QQ[] + sage: K = R.fraction_field() + sage: R(K.gen(), check=True) + x + """ check = kwds.pop('check', True) if args or kwds: From e0137f5f7eb506bff0c7e079d7a2588e821c341e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 23 Jul 2017 11:28:14 +0200 Subject: [PATCH 118/184] some bad syntax in trac role and raise --- src/sage/homology/chain_complex.py | 2 +- src/sage/structure/coerce_maps.pyx | 2 +- src/sage/structure/parent.pyx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 02232aa1969..3923edc817d 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -237,7 +237,7 @@ def ChainComplex(data=None, base_ring=None, grading_group=None, if grading_group is None: grading_group = ZZ if degree_of_differential != 1 and degree != 1: - raise(ValueError, 'specify only one of degree_of_differential or degree, not both') + raise ValueError('specify only one of degree_of_differential or degree, not both') if degree_of_differential != 1: degree = degree_of_differential try: diff --git a/src/sage/structure/coerce_maps.pyx b/src/sage/structure/coerce_maps.pyx index deee5e617a3..e0a8617266b 100644 --- a/src/sage/structure/coerce_maps.pyx +++ b/src/sage/structure/coerce_maps.pyx @@ -42,7 +42,7 @@ cdef class DefaultConvertMap(Map): sage: f.parent() Set of Morphisms from Finite Field of size 7 to Finite Field of size 11 in Category of sets with partial maps - Test that `trac`:23211 is resolved:: + Test that :trac:`23211` is resolved:: sage: f._is_coercion False diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index a38713695ff..0e15153b4ea 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1813,7 +1813,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject): TESTS: - We check that `trac`:23184 has been resolved:: + We check that :trac:`23184` has been resolved:: sage: QQ['x', 'y']._generic_coerce_map(QQ).category_for() Category of unique factorization domains From e72abf286ca4c2ab252abeb61ea8f54eda31bef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 23 Jul 2017 21:12:07 +0200 Subject: [PATCH 119/184] trac 23512 doc details --- src/sage/lfunctions/dokchitser.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/lfunctions/dokchitser.py b/src/sage/lfunctions/dokchitser.py index 94d21848b28..8d68826bf8f 100644 --- a/src/sage/lfunctions/dokchitser.py +++ b/src/sage/lfunctions/dokchitser.py @@ -366,9 +366,13 @@ def num_coeffs(self, T=1): sage: L.num_coeffs() 4 - Verify that ``num_coeffs`` works with non-real spectral parameters, e.g. for the L-function of the level 10 Maass form with eigenvalue 2.7341055592527126 + Verify that ``num_coeffs`` works with non-real spectral + parameters, e.g. for the L-function of the level 10 Maass form + with eigenvalue 2.7341055592527126:: - sage: Dokchitser(conductor=10, gammaV=[2.7341055592527126*i, -2.7341055592527126*i],weight=2,eps=1).num_coeffs() + sage: ev = 2.7341055592527126 + sage: L = Dokchitser(conductor=10, gammaV=[ev*i, -ev*i],weight=2,eps=1) + sage: L.num_coeffs() 26 """ return Integer(self._gp_call_inst('cflength', T)) From daf58320a7aa95a0ad4cd24b1e5ab2d8a924e04c Mon Sep 17 00:00:00 2001 From: paulmasson Date: Sun, 23 Jul 2017 12:55:06 -0700 Subject: [PATCH 120/184] Update links --- src/doc/en/reference/plot3d/threejs_examples/helix.html | 4 ++-- src/doc/en/reference/plot3d/threejs_examples/spheres.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/en/reference/plot3d/threejs_examples/helix.html b/src/doc/en/reference/plot3d/threejs_examples/helix.html index 4bfa45da48e..9300d18060a 100644 --- a/src/doc/en/reference/plot3d/threejs_examples/helix.html +++ b/src/doc/en/reference/plot3d/threejs_examples/helix.html @@ -15,8 +15,8 @@ - - + + - + +