From 3c9b80a2551994493f23640344e9ade63240cb01 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sat, 24 Jul 2021 00:52:51 +0200 Subject: [PATCH 01/50] Trac #29581: implement algorithms for chern, pontryagin and euler --- .../characteristic_cohomology_class.py | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 src/sage/manifolds/differentiable/characteristic_cohomology_class.py diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py new file mode 100644 index 00000000000..e8d52f7ffce --- /dev/null +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -0,0 +1,140 @@ +from sage.manifolds.differentiable.de_rham_cohomology import DeRhamCohomologyClass + +def CharacteristicCohomologyClass(): + pass + +class CharacteristicCohomologyClass_base(DeRhamCohomologyClass): + pass + +class CharacteristicCohomologyClass_complex(CharacteristicCohomologyClass_base): + pass + +class CharacteristicCohomologyClass_real_even(CharacteristicCohomologyClass_base): + pass + +class CharacteristicCohomologyClass_real_odd(CharacteristicCohomologyClass_base): + pass + +#***************************************************************************** +# ALGORITHMS +#***************************************************************************** + +from sage.misc.fast_methods import Singleton +from sage.structure.sage_object import SageObject +from sage.misc.cachefunc import cached_method +from sage.misc.abstract_method import abstract_method +from sage.manifolds.differentiable.affine_connection import AffineConnection +from sage.manifolds.differentiable.bundle_connection import BundleConnection + +class Algorithm_generic(SageObject): + r""" + Algorithm class to generate characteristic forms. + """ + @cached_method + def get(self, nabla): + r""" + Return the global characteristic form w.r.t. a given connection. + """ + if isinstance(nabla, AffineConnection): + vbundle = nabla._domain.tangent_bundle() + elif isinstance(nabla, BundleConnection): + vbundle = nabla._vbundle + else: + raise TypeError(f'{nabla} must be a connection') + dom = nabla._domain + res = dom.mixed_form() + for frame in dom._get_min_covering(nabla._coefficients): + cmatrix = [[nabla.curvature_form(i, j, frame) + for j in vbundle.irange()] + for i in vbundle.irange()] + res_loc = self.get_local(cmatrix) + res.set_restriction(res_loc) + return res + + @abstract_method + def get_local(self, cmat): + pass + +class ChernAlgorithm(Singleton, Algorithm_generic): + r""" + Algorithm class to generate Chern forms. + """ + def get_local(self, cmat): + r""" + Return the local Chern forms for a given curvature matrix. + """ + from sage.symbolic.constants import pi + from sage.libs.pynac.pynac import I + + rk = len(cmat) + dim = cmat[0][0]._domain._dim + ran = min(rk, dim//2) + fac = I / (2*pi) + res = [] + m = cmat + for k in range(1, ran): + c = -sum(m[i][i] for i in range(rk)) / k + res.append(fac * c) + for i in range(rk): + m[i][i] += c + fac *= I / (2*pi) + m = [[sum(cmat[i][l].wedge(m[l][j]) for l in range(rk)) + for j in range(rk)] for i in range(rk)] + res -= fac * sum(m[i][i] for i in range(rk)) / ran + return res + + +class PontryaginAlgorithm(Singleton, Algorithm_generic): + r""" + Algorithm class to generate Pontryagin forms. + """ + def get_local(self, cmat): + r""" + Return the local Pontryagin forms for a given curvature matrix. + """ + from sage.symbolic.constants import pi + + rk = len(cmat) + dim = cmat[0][0]._domain._dim + ran = min(rk//2, dim//4) + fac = -1 / (2*pi)**2 + res = [] + m = cmat2 = [[sum(cmat[i][l].wedge(cmat[l][j]) + for l in range(rk)) + for j in range(rk)] for i in range(rk)] + for k in range(1, ran): + c = -sum(m[i][i] for i in range(rk)) / (2*k) + res.append(fac * c) + for i in range(rk): + m[i][i] += c + fac *= -1 / (2*pi)**2 + m = [[sum(cmat2[i][l].wedge(m[l][j]) for l in range(rk)) + for j in range(rk)] for i in range(rk)] + res -= fac * sum(m[i][i] for i in range(rk)) / (2*ran) + return res + +class EulerAlgorithm(Singleton, Algorithm_generic): + r""" + Algorithm class to generate Euler forms. + """ + def get_local(self, cmat): + r""" + Return the local Euler form for a given curvature matrix. + """ + from sage.symbolic.constants import pi + + rk = len(cmat) + ran = rk // 2 + m = a = [cmat[i].copy() for i in range(rk)] + for i in range(0, rk, 2): + m[i], m[i+1] = m[i+1], m[i] # swap entries + for k in range(rk): + m[k][i+1] = -m[k][i+1] + for k in range(1, ran): + c = -sum(m[i][i] for i in range(rk)) / (2*k) + for i in range(rk): + m[i][i] += c + m = [[sum(a[i][l].wedge(m[l][j]) for l in range(rk)) + for j in range(rk)] for i in range(rk)] + c = -sum(m[i][i] for i in range(rk)) / (2*rk) + return (-1/(2*pi))**rk * c From 03529f8919357f98b65ee033e8dc5c7d48b2fa42 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sat, 24 Jul 2021 02:03:17 +0200 Subject: [PATCH 02/50] Trac #29581: return mixed form --- .../characteristic_cohomology_class.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index e8d52f7ffce..1b4ae025270 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -66,15 +66,16 @@ def get_local(self, cmat): from sage.symbolic.constants import pi from sage.libs.pynac.pynac import I + dom = cmat[0][0]._domain rk = len(cmat) - dim = cmat[0][0]._domain._dim + dim = dom._dim ran = min(rk, dim//2) fac = I / (2*pi) - res = [] + res = dom.mixed_form_algebra().one() m = cmat for k in range(1, ran): c = -sum(m[i][i] for i in range(rk)) / k - res.append(fac * c) + res += fac * c for i in range(rk): m[i][i] += c fac *= I / (2*pi) @@ -94,17 +95,18 @@ def get_local(self, cmat): """ from sage.symbolic.constants import pi + dom = cmat[0][0]._domain rk = len(cmat) - dim = cmat[0][0]._domain._dim + dim = dom._dim ran = min(rk//2, dim//4) fac = -1 / (2*pi)**2 - res = [] + res = dom.mixed_form_algebra().one() m = cmat2 = [[sum(cmat[i][l].wedge(cmat[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] for k in range(1, ran): c = -sum(m[i][i] for i in range(rk)) / (2*k) - res.append(fac * c) + res += fac * c for i in range(rk): m[i][i] += c fac *= -1 / (2*pi)**2 @@ -123,6 +125,8 @@ def get_local(self, cmat): """ from sage.symbolic.constants import pi + dom = cmat[0][0]._domain + A = dom.mixed_form_algebra() rk = len(cmat) ran = rk // 2 m = a = [cmat[i].copy() for i in range(rk)] @@ -137,4 +141,5 @@ def get_local(self, cmat): m = [[sum(a[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] c = -sum(m[i][i] for i in range(rk)) / (2*rk) - return (-1/(2*pi))**rk * c + c *= (-1/(2*pi))**rk # normalize + return A(c) From ab288d62786a40b9f314d40ca932506d7b6932d9 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 17 Aug 2021 17:38:12 +0200 Subject: [PATCH 03/50] Trac #29581: algorithm for complex vbundles --- .../differentiable/bundle_connection.py | 24 +-- .../characteristic_cohomology_class.py | 163 ++++++++++++++---- 2 files changed, 138 insertions(+), 49 deletions(-) diff --git a/src/sage/manifolds/differentiable/bundle_connection.py b/src/sage/manifolds/differentiable/bundle_connection.py index 3c0c22a2b0d..a3353b820cb 100644 --- a/src/sage/manifolds/differentiable/bundle_connection.py +++ b/src/sage/manifolds/differentiable/bundle_connection.py @@ -266,7 +266,7 @@ def __init__(self, vbundle, name, latex_name=None): "vector bundle") Mutability.__init__(self) self._vbundle = vbundle - self._base_space = vbundle.base_space() + self._domain = vbundle.base_space() self._name = name if latex_name is None: self._latex_name = self._name @@ -394,7 +394,7 @@ def __eq__(self, other): return True if not isinstance(other, BundleConnection): return False - if other._base_space != self._base_space: + if other._domain != self._domain: return False if self._connection_forms == {}: return False @@ -566,7 +566,7 @@ def connection_forms(self, frame=None): """ if frame is None: - smodule = self._vbundle.section_module(domain=self._base_space) + smodule = self._vbundle.section_module(domain=self._domain) frame = smodule.default_frame() if frame is None: raise ValueError("a frame must be provided") @@ -832,7 +832,7 @@ def add_connection_form(self, i, j, frame=None): """ self._require_mutable() if frame is None: - smodule = self._vbundle.section_module(domain=self._base_space) + smodule = self._vbundle.section_module(domain=self._domain) frame = smodule.default_frame() if frame is None: raise ValueError("a frame must be provided") @@ -840,7 +840,7 @@ def add_connection_form(self, i, j, frame=None): if frame not in self._connection_forms: if frame not in self._vbundle._frames: raise ValueError("the {} is not".format(frame) + - " a frame on the {}".format(self._base_space)) + " a frame on the {}".format(self._domain)) self._connection_forms[frame] = self._new_forms(frame) self._del_derived() # deletes the derived quantities return self._connection_forms[frame][(i, j)] @@ -967,7 +967,7 @@ def del_other_forms(self, frame=None): """ if frame is None: - smodule = self._vbundle.section_module(domain=self._base_space) + smodule = self._vbundle.section_module(domain=self._domain) frame = smodule.default_frame() if frame is None: raise ValueError("a frame must be provided") @@ -1023,7 +1023,7 @@ def curvature_form(self, i, j, frame=None): """ if frame is None: - smodule = self._vbundle.section_module(domain=self._base_space) + smodule = self._vbundle.section_module(domain=self._domain) frame = smodule.default_frame() if frame is None: raise ValueError("a frame must be provided") @@ -1127,13 +1127,13 @@ def __getitem__(self, args): # extract frame from first index: vb = self._vbundle if isinstance(args, (int, Integer, slice)): - smodule = vb.section_module(domain=self._base_space) + smodule = vb.section_module(domain=self._domain) frame = smodule.default_frame() elif not isinstance(args[0], (int, Integer, slice)): frame = args[0] args = args[1:] else: - smodule = vb.section_module(domain=self._base_space) + smodule = vb.section_module(domain=self._domain) frame = smodule.default_frame() # indexing: if isinstance(args, slice): @@ -1200,13 +1200,13 @@ def __setitem__(self, args, value): # extract frame from first index: vb = self._vbundle if isinstance(args, (int, Integer, slice)): - smodule = vb.section_module(domain=self._base_space) + smodule = vb.section_module(domain=self._domain) frame = smodule.default_frame() elif not isinstance(args[0], (int, Integer, slice)): frame = args[0] args = args[1:] else: - smodule = vb.section_module(domain=self._base_space) + smodule = vb.section_module(domain=self._domain) frame = smodule.default_frame() # determine indices: if isinstance(args, slice): @@ -1350,7 +1350,7 @@ def display(self, frame=None, vector_frame=None, chart=None, """ vb = self._vbundle if frame is None: - smodule = vb.section_module(domain=self._base_space) + smodule = vb.section_module(domain=self._domain) frame = smodule.default_frame() if frame is None: raise ValueError("a local frame must be provided") diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 1b4ae025270..70023ee5db9 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1,54 +1,144 @@ from sage.manifolds.differentiable.de_rham_cohomology import DeRhamCohomologyClass +from sage.algebras.finite_gca import FiniteGCAlgebra +from sage.combinat.free_module import IndexedFreeModuleElement +from sage.misc.fast_methods import Singleton +from sage.structure.sage_object import SageObject +from sage.misc.cachefunc import cached_method +from sage.misc.abstract_method import abstract_method +from sage.manifolds.differentiable.affine_connection import AffineConnection +from sage.manifolds.differentiable.bundle_connection import BundleConnection + +class CharacteristicCohomologyClass_Chern(IndexedFreeModuleElement): + r""" -def CharacteristicCohomologyClass(): - pass + """ + def __init__(self, parent, x, name=None, latex_name=None): + r""" -class CharacteristicCohomologyClass_base(DeRhamCohomologyClass): - pass + """ + self._name = name + if latex_name is None: + self._latex_name = self._name + else: + self._latex_name = latex_name + self._mixed_forms = {} # dict. of mixed forms w.r.t. this class + # (key: bundle connection) + super().__init__(parent, x) -class CharacteristicCohomologyClass_complex(CharacteristicCohomologyClass_base): - pass + def _repr_(self): + r""" -class CharacteristicCohomologyClass_real_even(CharacteristicCohomologyClass_base): - pass + """ + if self._name is None: + name = super()._repr_() + else: + name = self._name + vbundle = self.parent()._vbundle + return f'Characteristic cohomology class {name} over the {vbundle}' -class CharacteristicCohomologyClass_real_odd(CharacteristicCohomologyClass_base): - pass + def get_form(self, nab): + r""" + + """ + if nab not in self._mixed_forms: + dom = nab._domain + A = dom.mixed_form_algebra() + + # trivial cases + if self == 1: + self._mixed_forms[nab] = A(dom._one_scalar_field) + elif self == 0: + self._mixed_forms[nab] = A(dom._zero_scalar_field) + else: # non-trivial case + from functools import reduce + + c = ChernAlgorithm().get(nab) + parent = self.parent() + degrees = parent._degrees + grading = parent.print_options()['sorting_key'] + res = [dom.diff_form_module(i).zero() for i in range(dom._dim + 1)] + for ind, coeff in self: + deg = grading(ind) + gen_pow = [fast_wedge_power(c[d], i) for i, d in zip(ind, degrees)] + res[deg] += coeff * reduce(lambda x, y: x.wedge(y), gen_pow) + self._mixed_forms[nab] = A(res) # add result to dict + + return self._mixed_forms[nab] + + representative = get_form + +class CharacteristicCohomologyClassRing_Chern(FiniteGCAlgebra): + r""" + + """ + Element = CharacteristicCohomologyClass_Chern + + def __init__(self, base, vbundle): + r""" + + """ + self._vbundle = vbundle + self._domain = vbundle._base_space + dim = self._domain._dim + ran = min(vbundle._rank, dim // 2) + names = tuple(f'c_{i}({vbundle._name})' for i in range(1, ran + 1)) + degrees = tuple(2*i for i in range(1, ran + 1)) + super().__init__(base=base, names=names, degrees=degrees, + max_degree=dim) #***************************************************************************** # ALGORITHMS #***************************************************************************** -from sage.misc.fast_methods import Singleton -from sage.structure.sage_object import SageObject -from sage.misc.cachefunc import cached_method -from sage.misc.abstract_method import abstract_method -from sage.manifolds.differentiable.affine_connection import AffineConnection -from sage.manifolds.differentiable.bundle_connection import BundleConnection +def fast_wedge_power(form, n): + r""" + Return the wedge product power of `form` using a square-and-wedge algorithm. + """ + if n == 0: + return form._domain._one_scalar_field + elif n < 0: + raise ValueError("'n' must be non-negative") + val = form + while not (n & 1): + print(n) + val = val.wedge(val) + n >>= 1 + + # Now multiply together the correct factors form^(2^i) + res = val + n >>= 1 + while n: + val = val.wedge(val) + if n & 1: + res = val.wedge(res) + n >>= 1 + + return res class Algorithm_generic(SageObject): r""" Algorithm class to generate characteristic forms. """ @cached_method - def get(self, nabla): + def get(self, nab): r""" Return the global characteristic form w.r.t. a given connection. """ - if isinstance(nabla, AffineConnection): - vbundle = nabla._domain.tangent_bundle() - elif isinstance(nabla, BundleConnection): - vbundle = nabla._vbundle + if isinstance(nab, AffineConnection): + vbundle = nab._domain.tangent_bundle() + elif isinstance(nab, BundleConnection): + vbundle = nab._vbundle else: - raise TypeError(f'{nabla} must be a connection') - dom = nabla._domain - res = dom.mixed_form() - for frame in dom._get_min_covering(nabla._coefficients): - cmatrix = [[nabla.curvature_form(i, j, frame) + raise TypeError(f'{nab} must be a connection') + dom = nab._domain + res = [dom.diff_form(i) for i in range(dom._dim + 1)] + for frame in dom._get_min_covering(nab._coefficients): + cmatrix = [[nab.curvature_form(i, j, frame) for j in vbundle.irange()] for i in vbundle.irange()] res_loc = self.get_local(cmatrix) - res.set_restriction(res_loc) + for form, loc_form in zip(res, res_loc): + form.set_restriction(loc_form) return res @abstract_method @@ -71,20 +161,20 @@ def get_local(self, cmat): dim = dom._dim ran = min(rk, dim//2) fac = I / (2*pi) - res = dom.mixed_form_algebra().one() + res = [dom._one_scalar_field] + res += [dom.diff_form_module(i).zero() for i in range(1, dom._dim + 1)] m = cmat for k in range(1, ran): c = -sum(m[i][i] for i in range(rk)) / k - res += fac * c + res[2*k] = fac * c for i in range(rk): m[i][i] += c fac *= I / (2*pi) m = [[sum(cmat[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - res -= fac * sum(m[i][i] for i in range(rk)) / ran + res[2*ran] = -fac * sum(m[i][i] for i in range(rk)) / ran return res - class PontryaginAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Pontryagin forms. @@ -100,19 +190,20 @@ def get_local(self, cmat): dim = dom._dim ran = min(rk//2, dim//4) fac = -1 / (2*pi)**2 - res = dom.mixed_form_algebra().one() + res = [dom._one_scalar_field] + res += [dom.diff_form_module(i).zero() for i in range(1, dom._dim + 1)] m = cmat2 = [[sum(cmat[i][l].wedge(cmat[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] for k in range(1, ran): c = -sum(m[i][i] for i in range(rk)) / (2*k) - res += fac * c + res[4*k] = fac * c for i in range(rk): m[i][i] += c fac *= -1 / (2*pi)**2 m = [[sum(cmat2[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - res -= fac * sum(m[i][i] for i in range(rk)) / (2*ran) + res[4*ran] = -fac * sum(m[i][i] for i in range(rk)) / (2*ran) return res class EulerAlgorithm(Singleton, Algorithm_generic): @@ -125,8 +216,6 @@ def get_local(self, cmat): """ from sage.symbolic.constants import pi - dom = cmat[0][0]._domain - A = dom.mixed_form_algebra() rk = len(cmat) ran = rk // 2 m = a = [cmat[i].copy() for i in range(rk)] @@ -142,4 +231,4 @@ def get_local(self, cmat): for j in range(rk)] for i in range(rk)] c = -sum(m[i][i] for i in range(rk)) / (2*rk) c *= (-1/(2*pi))**rk # normalize - return A(c) + return [c] From 932e1280a55deb06b503763481815f67e4fad90d Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 17 Aug 2021 18:45:02 +0200 Subject: [PATCH 04/50] Trac #29581: element constructor --- .../characteristic_cohomology_class.py | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 70023ee5db9..d16075f2758 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1,4 +1,3 @@ -from sage.manifolds.differentiable.de_rham_cohomology import DeRhamCohomologyClass from sage.algebras.finite_gca import FiniteGCAlgebra from sage.combinat.free_module import IndexedFreeModuleElement from sage.misc.fast_methods import Singleton @@ -67,6 +66,17 @@ def get_form(self, nab): representative = get_form + def set_name(self, name=None, latex_name=None): + r""" + Set the name and latex name of ``self``. + """ + if name is not None: + self._name = name + if latex_name is None: + self._latex_name = self._name + if latex_name is not None: + self._latex_name = latex_name + class CharacteristicCohomologyClassRing_Chern(FiniteGCAlgebra): r""" @@ -86,6 +96,31 @@ def __init__(self, base, vbundle): super().__init__(base=base, names=names, degrees=degrees, max_degree=dim) + def _element_constructor_(self, x, name=None, latex_name=None): + r""" + + """ + R = self.base_ring() + + if x in R: + one_basis = self.one_basis() + d = {one_basis: R(x)} + elif x in self: + d = x._monomial_coefficients + # x is an element of the basis enumerated set; + # This is a very ugly way of testing this + elif ((hasattr(self._indices, 'element_class') and + isinstance(self._indices.element_class, type) and + isinstance(x, self._indices.element_class)) or + self.parent()(x) == self._indices): + d = {x: R.one()} + elif x in self._indices: + d = {self._indices(x): R.one()} + else: + raise TypeError(f"do not know how to make x (= {x}) an element of self (={self})") + + return self.element_class(self, d, name=name, latex_name=latex_name) + #***************************************************************************** # ALGORITHMS #***************************************************************************** @@ -152,6 +187,11 @@ class ChernAlgorithm(Singleton, Algorithm_generic): def get_local(self, cmat): r""" Return the local Chern forms for a given curvature matrix. + + .. ALGORITHM:: + + The algorithm is based on the Faddeev-LeVerrier algorithm for the + characteristic polynomial. """ from sage.symbolic.constants import pi from sage.libs.pynac.pynac import I @@ -182,6 +222,11 @@ class PontryaginAlgorithm(Singleton, Algorithm_generic): def get_local(self, cmat): r""" Return the local Pontryagin forms for a given curvature matrix. + + .. ALGORITHM:: + + The algorithm is based on the Faddeev-LeVerrier algorithm for the + characteristic polynomial. """ from sage.symbolic.constants import pi @@ -213,6 +258,11 @@ class EulerAlgorithm(Singleton, Algorithm_generic): def get_local(self, cmat): r""" Return the local Euler form for a given curvature matrix. + + .. ALGORITHM:: + + The algorithm is based on the Bär-Faddeev-LeVerrier algorithm for + the Pfaffian. """ from sage.symbolic.constants import pi From 6688740017097a79d7995101889cea097ee5c653 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 17 Aug 2021 19:13:43 +0200 Subject: [PATCH 05/50] Trac #29581: naming --- .../characteristic_cohomology_class.py | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index d16075f2758..efdc4479375 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -15,10 +15,11 @@ def __init__(self, parent, x, name=None, latex_name=None): r""" """ - self._name = name - if latex_name is None: - self._latex_name = self._name - else: + if name is not None: + self._name = name + if latex_name is None: + self._latex_name = self._name + if latex_name is not None: self._latex_name = latex_name self._mixed_forms = {} # dict. of mixed forms w.r.t. this class # (key: bundle connection) @@ -33,8 +34,22 @@ def _repr_(self): else: name = self._name vbundle = self.parent()._vbundle + name = f'({name})({vbundle._name})' return f'Characteristic cohomology class {name} over the {vbundle}' + def _latex_(self): + r""" + + """ + if self._latex_name is None: + latex = super()._latex_() + else: + latex = self._name + vbundle = self.parent()._vbundle + latex = r'\left(' + latex + r'\right)\right(' + latex += vbundle._latex_name + r'\right)' + return latex + def get_form(self, nab): r""" @@ -91,7 +106,7 @@ def __init__(self, base, vbundle): self._domain = vbundle._base_space dim = self._domain._dim ran = min(vbundle._rank, dim // 2) - names = tuple(f'c_{i}({vbundle._name})' for i in range(1, ran + 1)) + names = tuple(f'c_{i}' for i in range(1, ran + 1)) degrees = tuple(2*i for i in range(1, ran + 1)) super().__init__(base=base, names=names, degrees=degrees, max_degree=dim) From 61291f406b886abb9a509955355da749fc99a8d6 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 17 Aug 2021 19:15:38 +0200 Subject: [PATCH 06/50] Trac #29581: minor fixes --- .../differentiable/characteristic_cohomology_class.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index efdc4479375..6963f4ceb7c 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -15,11 +15,10 @@ def __init__(self, parent, x, name=None, latex_name=None): r""" """ - if name is not None: - self._name = name - if latex_name is None: - self._latex_name = self._name - if latex_name is not None: + self._name = name + if latex_name is None: + self._latex_name = self._name + else: self._latex_name = latex_name self._mixed_forms = {} # dict. of mixed forms w.r.t. this class # (key: bundle connection) @@ -120,7 +119,7 @@ def _element_constructor_(self, x, name=None, latex_name=None): if x in R: one_basis = self.one_basis() d = {one_basis: R(x)} - elif x in self: + elif isinstance(x, CharacteristicCohomologyClass_Chern): d = x._monomial_coefficients # x is an element of the basis enumerated set; # This is a very ugly way of testing this From 60318775e9c530c32280ba1bfa819cb9ac978bd1 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 17 Aug 2021 22:51:56 +0200 Subject: [PATCH 07/50] Trac #29581: return only list of generators --- .../characteristic_cohomology_class.py | 79 ++++++++++++------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 6963f4ceb7c..3aee80e4963 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -34,7 +34,7 @@ def _repr_(self): name = self._name vbundle = self.parent()._vbundle name = f'({name})({vbundle._name})' - return f'Characteristic cohomology class {name} over the {vbundle}' + return f'Characteristic cohomology class {name} over the {vbundle}' def _latex_(self): r""" @@ -43,7 +43,7 @@ def _latex_(self): if self._latex_name is None: latex = super()._latex_() else: - latex = self._name + latex = self._latex_name vbundle = self.parent()._vbundle latex = r'\left(' + latex + r'\right)\right(' latex += vbundle._latex_name + r'\right)' @@ -67,30 +67,52 @@ def get_form(self, nab): c = ChernAlgorithm().get(nab) parent = self.parent() - degrees = parent._degrees grading = parent.print_options()['sorting_key'] res = [dom.diff_form_module(i).zero() for i in range(dom._dim + 1)] for ind, coeff in self: deg = grading(ind) - gen_pow = [fast_wedge_power(c[d], i) for i, d in zip(ind, degrees)] + gen_pow = [fast_wedge_power(f, i) for f, i in zip(c, ind)] res[deg] += coeff * reduce(lambda x, y: x.wedge(y), gen_pow) - self._mixed_forms[nab] = A(res) # add result to dict + + # prepare result: + res = A(res) + + # preparse names (put brackets around) + vbundle = self.parent()._vbundle + if self._name is None: + name = f'({super()._repr_()})' + else: + name = f'({self._name})' + if self._latex_name is None: + latex_name = r'\left(' + super()._latex_() + r'\right)' + else: + latex_name = r'\left(' + self._latex_name + r'\right)' + # appendix + append_name = f'({vbundle._name}, {nab._name})' + append_latex_name = r'\left(' + vbundle._latex_name + append_latex_name += ', ' + nab._latex_name + r'\right)' + + # set names of components + for i in range(dom._dim // 2 + 1): + comp_name = name + f'_{i}' + append_name + comp_latex_name = latex_name + r'_{' + str(i) + '}' + comp_latex_name += append_latex_name + res[2*i].set_name(name=comp_name, + latex_name=comp_latex_name) + + # set global names + res._name = name + append_name + res._latex_name = latex_name + append_latex_name + + res.set_immutable() # set result immutable + + # add result to dict + self._mixed_forms[nab] = res return self._mixed_forms[nab] representative = get_form - def set_name(self, name=None, latex_name=None): - r""" - Set the name and latex name of ``self``. - """ - if name is not None: - self._name = name - if latex_name is None: - self._latex_name = self._name - if latex_name is not None: - self._latex_name = latex_name - class CharacteristicCohomologyClassRing_Chern(FiniteGCAlgebra): r""" @@ -171,7 +193,8 @@ class Algorithm_generic(SageObject): @cached_method def get(self, nab): r""" - Return the global characteristic form w.r.t. a given connection. + Return the global characteristic forms of the generators w.r.t. a given + connection. """ if isinstance(nab, AffineConnection): vbundle = nab._domain.tangent_bundle() @@ -180,14 +203,18 @@ def get(self, nab): else: raise TypeError(f'{nab} must be a connection') dom = nab._domain - res = [dom.diff_form(i) for i in range(dom._dim + 1)] + res = [] # will be specified within first iteration for frame in dom._get_min_covering(nab._coefficients): cmatrix = [[nab.curvature_form(i, j, frame) for j in vbundle.irange()] for i in vbundle.irange()] res_loc = self.get_local(cmatrix) + if not res: + # until now, degrees of generators were unknown + res = [dom.diff_form(loc_form.degree()) for loc_form in res_loc] for form, loc_form in zip(res, res_loc): form.set_restriction(loc_form) + # TODO: make res immutable? return res @abstract_method @@ -215,18 +242,17 @@ def get_local(self, cmat): dim = dom._dim ran = min(rk, dim//2) fac = I / (2*pi) - res = [dom._one_scalar_field] - res += [dom.diff_form_module(i).zero() for i in range(1, dom._dim + 1)] + res = [] m = cmat for k in range(1, ran): c = -sum(m[i][i] for i in range(rk)) / k - res[2*k] = fac * c + res.append(fac * c) for i in range(rk): m[i][i] += c fac *= I / (2*pi) m = [[sum(cmat[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - res[2*ran] = -fac * sum(m[i][i] for i in range(rk)) / ran + res.append(-fac * sum(m[i][i] for i in range(rk)) / ran) return res class PontryaginAlgorithm(Singleton, Algorithm_generic): @@ -249,20 +275,19 @@ def get_local(self, cmat): dim = dom._dim ran = min(rk//2, dim//4) fac = -1 / (2*pi)**2 - res = [dom._one_scalar_field] - res += [dom.diff_form_module(i).zero() for i in range(1, dom._dim + 1)] + res = [] m = cmat2 = [[sum(cmat[i][l].wedge(cmat[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] for k in range(1, ran): c = -sum(m[i][i] for i in range(rk)) / (2*k) - res[4*k] = fac * c + res.append(fac * c) for i in range(rk): m[i][i] += c fac *= -1 / (2*pi)**2 m = [[sum(cmat2[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - res[4*ran] = -fac * sum(m[i][i] for i in range(rk)) / (2*ran) + res.append(-fac * sum(m[i][i] for i in range(rk)) / (2*ran)) return res class EulerAlgorithm(Singleton, Algorithm_generic): @@ -293,6 +318,6 @@ def get_local(self, cmat): m[i][i] += c m = [[sum(a[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - c = -sum(m[i][i] for i in range(rk)) / (2*rk) + c = -sum(m[i][i] for i in range(rk)) / (2*rk) # Pfaffian mod sign c *= (-1/(2*pi))**rk # normalize return [c] From 0c7587f759b9555404fd417ee042e99fbb4ae02d Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 10:51:18 +0200 Subject: [PATCH 08/50] Trac #29581: parent deals with different cases, element stays generic --- .../characteristic_cohomology_class.py | 148 ++++++++++++------ .../pseudo_riemannian_submanifold.py | 14 +- 2 files changed, 115 insertions(+), 47 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 3aee80e4963..2bb387aa6e6 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1,3 +1,16 @@ +r""" +Characteristic cohomology classes +""" + +#****************************************************************************** +# Copyright (C) 2021 Michael Jung +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# https://www.gnu.org/licenses/ +#****************************************************************************** + from sage.algebras.finite_gca import FiniteGCAlgebra from sage.combinat.free_module import IndexedFreeModuleElement from sage.misc.fast_methods import Singleton @@ -7,10 +20,12 @@ from sage.manifolds.differentiable.affine_connection import AffineConnection from sage.manifolds.differentiable.bundle_connection import BundleConnection -class CharacteristicCohomologyClass_Chern(IndexedFreeModuleElement): + +class CharacteristicCohomologyClass(IndexedFreeModuleElement): r""" """ + def __init__(self, parent, x, name=None, latex_name=None): r""" @@ -20,7 +35,7 @@ def __init__(self, parent, x, name=None, latex_name=None): self._latex_name = self._name else: self._latex_name = latex_name - self._mixed_forms = {} # dict. of mixed forms w.r.t. this class + self._mixed_forms = {} # dict. of characteristic forms of `self` # (key: bundle connection) super().__init__(parent, x) @@ -34,7 +49,7 @@ def _repr_(self): name = self._name vbundle = self.parent()._vbundle name = f'({name})({vbundle._name})' - return f'Characteristic cohomology class {name} over the {vbundle}' + return f'Characteristic cohomology class {name} of the {vbundle}' def _latex_(self): r""" @@ -65,20 +80,24 @@ def get_form(self, nab): else: # non-trivial case from functools import reduce - c = ChernAlgorithm().get(nab) parent = self.parent() + + gen_algorithm = parent._algorithm # this is a list + # give list of representatives of generators + gen_forms = sum(a.get(nab) for a in gen_algorithm) grading = parent.print_options()['sorting_key'] - res = [dom.diff_form_module(i).zero() for i in range(dom._dim + 1)] - for ind, coeff in self: + res = [dom.diff_form_module(i).zero() + for i in range(dom._dim + 1)] + for ind, c in self: deg = grading(ind) - gen_pow = [fast_wedge_power(f, i) for f, i in zip(c, ind)] - res[deg] += coeff * reduce(lambda x, y: x.wedge(y), gen_pow) + gen_pow = [fast_wedge_power(f, i) + for f, i in zip(gen_forms, ind)] + res[deg] += c * reduce(lambda x, y: x.wedge(y), gen_pow) - # prepare result: - res = A(res) + res = A(res) # convert result into mixed form - # preparse names (put brackets around) - vbundle = self.parent()._vbundle + # preparse names + vbundle = parent._vbundle if self._name is None: name = f'({super()._repr_()})' else: @@ -93,12 +112,16 @@ def get_form(self, nab): append_latex_name += ', ' + nab._latex_name + r'\right)' # set names of components - for i in range(dom._dim // 2 + 1): + from sage.arith.misc import gcd + + step = gcd(parent._degrees) # step size of (possibly) non-zero + for i in range(dom._dim // step + 1): + # enumerate (possibly) non-zero components comp_name = name + f'_{i}' + append_name comp_latex_name = latex_name + r'_{' + str(i) + '}' comp_latex_name += append_latex_name - res[2*i].set_name(name=comp_name, - latex_name=comp_latex_name) + res[step * i].set_name(name=comp_name, + latex_name=comp_latex_name) # set global names res._name = name + append_name @@ -106,18 +129,18 @@ def get_form(self, nab): res.set_immutable() # set result immutable - # add result to dict - self._mixed_forms[nab] = res + self._mixed_forms[nab] = res # cache result in dict return self._mixed_forms[nab] representative = get_form -class CharacteristicCohomologyClassRing_Chern(FiniteGCAlgebra): + +class CharacteristicCohomologyClassRing(FiniteGCAlgebra): r""" """ - Element = CharacteristicCohomologyClass_Chern + Element = CharacteristicCohomologyClass def __init__(self, base, vbundle): r""" @@ -126,22 +149,39 @@ def __init__(self, base, vbundle): self._vbundle = vbundle self._domain = vbundle._base_space dim = self._domain._dim - ran = min(vbundle._rank, dim // 2) - names = tuple(f'c_{i}' for i in range(1, ran + 1)) - degrees = tuple(2*i for i in range(1, ran + 1)) + rk = vbundle._rank + # if vbundle is complex: + ran = min(rk, dim // 2) + names = [f'c_{i}' for i in range(1, ran + 1)] + degrees = [2 * i for i in range(1, ran + 1)] + self._algorithm = [ChernAlgorithm()] + # if vbundle is real: + # ran = min(rk // 2, dim // 4) + # names = [f'p_{i}' for i in range(1, ran + 1)] + # degrees = [4 * i for i in range(1, ran + 1)] + # self._algorithm = [PontryaginAlgorithm()] + # if vbundle is orientable: + # names += ['e'] + # degrees += [rk] + # # TODO: add relation e^2=p_k for dim=2*k + # # add algorithm for additional generator + # self._algorithm += [EulerAlgorithm()] + + names = tuple(names) # hashable + degrees = tuple(degrees) # hashable super().__init__(base=base, names=names, degrees=degrees, max_degree=dim) def _element_constructor_(self, x, name=None, latex_name=None): r""" - + Convert ``x`` into ``self``. """ R = self.base_ring() if x in R: one_basis = self.one_basis() d = {one_basis: R(x)} - elif isinstance(x, CharacteristicCohomologyClass_Chern): + elif isinstance(x, CharacteristicCohomologyClass): d = x._monomial_coefficients # x is an element of the basis enumerated set; # This is a very ugly way of testing this @@ -153,17 +193,20 @@ def _element_constructor_(self, x, name=None, latex_name=None): elif x in self._indices: d = {self._indices(x): R.one()} else: - raise TypeError(f"do not know how to make x (= {x}) an element of self (={self})") + raise TypeError(f"do not know how to make x (= {x}) " + f"an element of self (={self})") return self.element_class(self, d, name=name, latex_name=latex_name) -#***************************************************************************** + +# ***************************************************************************** # ALGORITHMS -#***************************************************************************** +# ***************************************************************************** def fast_wedge_power(form, n): r""" - Return the wedge product power of `form` using a square-and-wedge algorithm. + Return the wedge product power of `form` using a square-and-wedge + algorithm. """ if n == 0: return form._domain._one_scalar_field @@ -186,10 +229,12 @@ def fast_wedge_power(form, n): return res + class Algorithm_generic(SageObject): r""" - Algorithm class to generate characteristic forms. + Algorithm class to compute the characteristic forms of the generators. """ + @cached_method def get(self, nab): r""" @@ -211,7 +256,8 @@ def get(self, nab): res_loc = self.get_local(cmatrix) if not res: # until now, degrees of generators were unknown - res = [dom.diff_form(loc_form.degree()) for loc_form in res_loc] + res = [dom.diff_form(loc_form.degree()) + for loc_form in res_loc] for form, loc_form in zip(res, res_loc): form.set_restriction(loc_form) # TODO: make res immutable? @@ -219,15 +265,21 @@ def get(self, nab): @abstract_method def get_local(self, cmat): + r""" + Abstract method to get the local forms of the generators w.r.t. a given + curvature matrix `cmat`. + """ pass + class ChernAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Chern forms. """ + def get_local(self, cmat): r""" - Return the local Chern forms for a given curvature matrix. + Return the local Chern forms w.r.t. a given curvature matrix. .. ALGORITHM:: @@ -240,8 +292,8 @@ def get_local(self, cmat): dom = cmat[0][0]._domain rk = len(cmat) dim = dom._dim - ran = min(rk, dim//2) - fac = I / (2*pi) + ran = min(rk, dim // 2) + fac = I / (2 * pi) res = [] m = cmat for k in range(1, ran): @@ -249,19 +301,21 @@ def get_local(self, cmat): res.append(fac * c) for i in range(rk): m[i][i] += c - fac *= I / (2*pi) + fac *= I / (2 * pi) m = [[sum(cmat[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] res.append(-fac * sum(m[i][i] for i in range(rk)) / ran) return res + class PontryaginAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Pontryagin forms. """ + def get_local(self, cmat): r""" - Return the local Pontryagin forms for a given curvature matrix. + Return the local Pontryagin forms w.r.t. a given curvature matrix. .. ALGORITHM:: @@ -273,30 +327,32 @@ def get_local(self, cmat): dom = cmat[0][0]._domain rk = len(cmat) dim = dom._dim - ran = min(rk//2, dim//4) - fac = -1 / (2*pi)**2 + ran = min(rk // 2, dim // 4) + fac = -1 / (2 * pi) ** 2 res = [] m = cmat2 = [[sum(cmat[i][l].wedge(cmat[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] for k in range(1, ran): - c = -sum(m[i][i] for i in range(rk)) / (2*k) + c = -sum(m[i][i] for i in range(rk)) / (2 * k) res.append(fac * c) for i in range(rk): m[i][i] += c - fac *= -1 / (2*pi)**2 + fac *= -1 / (2 * pi) ** 2 m = [[sum(cmat2[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - res.append(-fac * sum(m[i][i] for i in range(rk)) / (2*ran)) + res.append(-fac * sum(m[i][i] for i in range(rk)) / (2 * ran)) return res + class EulerAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Euler forms. """ + def get_local(self, cmat): r""" - Return the local Euler form for a given curvature matrix. + Return the local Euler form for w.r.t. a given curvature matrix. .. ALGORITHM:: @@ -309,15 +365,15 @@ def get_local(self, cmat): ran = rk // 2 m = a = [cmat[i].copy() for i in range(rk)] for i in range(0, rk, 2): - m[i], m[i+1] = m[i+1], m[i] # swap entries + m[i], m[i + 1] = m[i + 1], m[i] # swap entries for k in range(rk): - m[k][i+1] = -m[k][i+1] + m[k][i + 1] = -m[k][i + 1] for k in range(1, ran): - c = -sum(m[i][i] for i in range(rk)) / (2*k) + c = -sum(m[i][i] for i in range(rk)) / (2 * k) for i in range(rk): m[i][i] += c m = [[sum(a[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - c = -sum(m[i][i] for i in range(rk)) / (2*rk) # Pfaffian mod sign - c *= (-1/(2*pi))**rk # normalize + c = -sum(m[i][i] for i in range(rk)) / (2 * rk) # Pfaffian mod sign + c *= (-1 / (2 * pi)) ** rk # normalize return [c] diff --git a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py index 5e5869d929f..42fb46700c1 100644 --- a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +++ b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py @@ -537,7 +537,7 @@ def first_fundamental_form(self): """ if self._first_fundamental_form is None: - self._first_fundamental_form = self.metric() + self._first_fundamental_form = super().metric() self._first_fundamental_form.set( self._immersion.pullback(self.ambient_metric())) self._first_fundamental_form.set_name("gamma", r"\gamma") @@ -545,6 +545,18 @@ def first_fundamental_form(self): induced_metric = first_fundamental_form + def metric(self, name=None, signature=None, latex_name=None, + dest_map=None): + r""" + Return the induced metric on ``self`` or define a new metric tensor on + the manifold. + + """ + if name is None and self._metric is None: + return self.first_fundamental_form() + return super().metric(name=name, signature=signature, + latex_name=latex_name, dest_map=dest_map) + @cached_method def difft(self): r""" From 28d2d84271a3e1516556a8364b1114dd6edaa992 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 10:54:44 +0200 Subject: [PATCH 09/50] Trac #29581: revert mistaken merge --- .../pseudo_riemannian_submanifold.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py index 42fb46700c1..5e5869d929f 100644 --- a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +++ b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py @@ -537,7 +537,7 @@ def first_fundamental_form(self): """ if self._first_fundamental_form is None: - self._first_fundamental_form = super().metric() + self._first_fundamental_form = self.metric() self._first_fundamental_form.set( self._immersion.pullback(self.ambient_metric())) self._first_fundamental_form.set_name("gamma", r"\gamma") @@ -545,18 +545,6 @@ def first_fundamental_form(self): induced_metric = first_fundamental_form - def metric(self, name=None, signature=None, latex_name=None, - dest_map=None): - r""" - Return the induced metric on ``self`` or define a new metric tensor on - the manifold. - - """ - if name is None and self._metric is None: - return self.first_fundamental_form() - return super().metric(name=name, signature=signature, - latex_name=latex_name, dest_map=dest_map) - @cached_method def difft(self): r""" From 2cabd839d55e5c0f3ba2f8d0194bcae6ce31397c Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 11:06:57 +0200 Subject: [PATCH 10/50] Trac #29581: add some comments --- .../differentiable/characteristic_cohomology_class.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 2bb387aa6e6..362811571a2 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -352,7 +352,7 @@ class EulerAlgorithm(Singleton, Algorithm_generic): def get_local(self, cmat): r""" - Return the local Euler form for w.r.t. a given curvature matrix. + Return the local Euler form w.r.t. a given curvature matrix. .. ALGORITHM:: @@ -376,4 +376,7 @@ def get_local(self, cmat): for j in range(rk)] for i in range(rk)] c = -sum(m[i][i] for i in range(rk)) / (2 * rk) # Pfaffian mod sign c *= (-1 / (2 * pi)) ** rk # normalize + + # TODO: incorporate orientation (overwrite `get`?) and metric + return [c] From 1a9b7d924d6f082cbd4ab3c96951bec0f2087a0b Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 14:41:57 +0200 Subject: [PATCH 11/50] Trac #29581: compute Euler forms for arbitrary positively oriented frames --- .../characteristic_cohomology_class.py | 89 ++++++++++++------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 362811571a2..19672a6807d 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -17,8 +17,9 @@ from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method from sage.misc.abstract_method import abstract_method -from sage.manifolds.differentiable.affine_connection import AffineConnection -from sage.manifolds.differentiable.bundle_connection import BundleConnection +from .affine_connection import AffineConnection +from .bundle_connection import BundleConnection +from .levi_civita_connection import LeviCivitaConnection class CharacteristicCohomologyClass(IndexedFreeModuleElement): @@ -83,7 +84,9 @@ def get_form(self, nab): parent = self.parent() gen_algorithm = parent._algorithm # this is a list - # give list of representatives of generators + # concatenate generators + print(gen_algorithm[0].get(nab)) + print(gen_algorithm[1].get(nab)) gen_forms = sum(a.get(nab) for a in gen_algorithm) grading = parent.print_options()['sorting_key'] res = [dom.diff_form_module(i).zero() @@ -127,7 +130,7 @@ def get_form(self, nab): res._name = name + append_name res._latex_name = latex_name + append_latex_name - res.set_immutable() # set result immutable + res.set_immutable() self._mixed_forms[nab] = res # cache result in dict @@ -150,22 +153,26 @@ def __init__(self, base, vbundle): self._domain = vbundle._base_space dim = self._domain._dim rk = vbundle._rank - # if vbundle is complex: - ran = min(rk, dim // 2) - names = [f'c_{i}' for i in range(1, ran + 1)] - degrees = [2 * i for i in range(1, ran + 1)] - self._algorithm = [ChernAlgorithm()] - # if vbundle is real: - # ran = min(rk // 2, dim // 4) - # names = [f'p_{i}' for i in range(1, ran + 1)] - # degrees = [4 * i for i in range(1, ran + 1)] - # self._algorithm = [PontryaginAlgorithm()] - # if vbundle is orientable: - # names += ['e'] - # degrees += [rk] - # # TODO: add relation e^2=p_k for dim=2*k - # # add algorithm for additional generator - # self._algorithm += [EulerAlgorithm()] + if vbundle._field_type == 'complex': + ran = min(rk, dim // 2) + names = [f'c_{i}' for i in range(1, ran + 1)] + degrees = [2 * i for i in range(1, ran + 1)] + self._algorithm = [ChernAlgorithm()] + elif vbundle._field_type == 'real': + ran = min(rk // 2, dim // 4) + names = [f'p_{i}' for i in range(1, ran + 1)] + degrees = [4 * i for i in range(1, ran + 1)] + self._algorithm = [PontryaginAlgorithm()] + if vbundle.has_orientation(): + # add Euler class generator + names += ['e'] + degrees += [rk] + self._algorithm += [EulerAlgorithm()] + # TODO: add relation e^2=p_k for dim=2*k + else: + raise TypeError(f'Characteristic cohomology classes not supported ' + f'for vector bundles with ' + f'field type {vbundle._field_type}') names = tuple(names) # hashable degrees = tuple(degrees) # hashable @@ -248,8 +255,14 @@ def get(self, nab): else: raise TypeError(f'{nab} must be a connection') dom = nab._domain + # if the bundle is orientable, we always opt for positively + # oriented frames due to unification between Pontryagin and Euler class + if vbundle.has_orientation(): + frames = vbundle.orientation() + else: + frames = nab._coefficients res = [] # will be specified within first iteration - for frame in dom._get_min_covering(nab._coefficients): + for frame in dom._get_min_covering(frames): cmatrix = [[nab.curvature_form(i, j, frame) for j in vbundle.irange()] for i in vbundle.irange()] @@ -260,7 +273,7 @@ def get(self, nab): for loc_form in res_loc] for form, loc_form in zip(res, res_loc): form.set_restriction(loc_form) - # TODO: make res immutable? + # TODO: make `res` immutable? return res @abstract_method @@ -350,9 +363,26 @@ class EulerAlgorithm(Singleton, Algorithm_generic): Algorithm class to generate Euler forms. """ + @cached_method + def get(self, nab): + r""" + Return the global characteristic forms of the generators w.r.t. a given + connection. + """ + if not isinstance(nab, LeviCivitaConnection): + raise TypeError('Euler forms are currently only supported for ' + 'Levi-Civita connections') + [e] = super().get(nab) # apply algorithm; not the Euler form yet + g = nab._metric + det = g.det() + if det.is_trivial_zero(): + raise ValueError(f'metric {g} must be non-degenerate') + res = e / abs(det.sqrt()) # this is the Euler form + return [res] + def get_local(self, cmat): r""" - Return the local Euler form w.r.t. a given curvature matrix. + Return the normalized Pfaffian w.r.t. a given curvature matrix. .. ALGORITHM:: @@ -369,14 +399,11 @@ def get_local(self, cmat): for k in range(rk): m[k][i + 1] = -m[k][i + 1] for k in range(1, ran): - c = -sum(m[i][i] for i in range(rk)) / (2 * k) + e = -sum(m[i][i] for i in range(rk)) / (2 * k) for i in range(rk): - m[i][i] += c + m[i][i] += e m = [[sum(a[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - c = -sum(m[i][i] for i in range(rk)) / (2 * rk) # Pfaffian mod sign - c *= (-1 / (2 * pi)) ** rk # normalize - - # TODO: incorporate orientation (overwrite `get`?) and metric - - return [c] + e = -sum(m[i][i] for i in range(rk)) / (2 * rk) # Pfaffian mod sign + e *= (-1 / (2 * pi)) ** rk # normalize + return [e] From 815392c4bcf2a6177cecbcc24ae3dc73372fe743 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 18:58:49 +0200 Subject: [PATCH 12/50] Trac #29581: fix some bugs + treat Euler case --- .../characteristic_cohomology_class.py | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 19672a6807d..9b4bc49d5c3 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -80,14 +80,14 @@ def get_form(self, nab): self._mixed_forms[nab] = A(dom._zero_scalar_field) else: # non-trivial case from functools import reduce + from itertools import chain parent = self.parent() gen_algorithm = parent._algorithm # this is a list # concatenate generators - print(gen_algorithm[0].get(nab)) - print(gen_algorithm[1].get(nab)) - gen_forms = sum(a.get(nab) for a in gen_algorithm) + gen_forms = list(chain.from_iterable(a.get(nab) + for a in gen_algorithm)) grading = parent.print_options()['sorting_key'] res = [dom.diff_form_module(i).zero() for i in range(dom._dim + 1)] @@ -255,14 +255,8 @@ def get(self, nab): else: raise TypeError(f'{nab} must be a connection') dom = nab._domain - # if the bundle is orientable, we always opt for positively - # oriented frames due to unification between Pontryagin and Euler class - if vbundle.has_orientation(): - frames = vbundle.orientation() - else: - frames = nab._coefficients res = [] # will be specified within first iteration - for frame in dom._get_min_covering(frames): + for frame in dom._get_min_covering(nab._coefficients): cmatrix = [[nab.curvature_form(i, j, frame) for j in vbundle.irange()] for i in vbundle.irange()] @@ -306,6 +300,8 @@ def get_local(self, cmat): rk = len(cmat) dim = dom._dim ran = min(rk, dim // 2) + if ran < 1: + return [] # nothing to compute fac = I / (2 * pi) res = [] m = cmat @@ -341,6 +337,8 @@ def get_local(self, cmat): rk = len(cmat) dim = dom._dim ran = min(rk // 2, dim // 4) + if ran < 2: + return [] # nothing to compute fac = -1 / (2 * pi) ** 2 res = [] m = cmat2 = [[sum(cmat[i][l].wedge(cmat[l][j]) @@ -372,12 +370,28 @@ def get(self, nab): if not isinstance(nab, LeviCivitaConnection): raise TypeError('Euler forms are currently only supported for ' 'Levi-Civita connections') - [e] = super().get(nab) # apply algorithm; not the Euler form yet + dom = nab._domain + vbundle = dom.tangent_bundle() + rk = vbundle._rank + if not vbundle.has_orientation(): + raise ValueError('Euler forms can only be defined for orientable ' + 'vector bundles') + if rk % 2 != 0: + raise ValueError('Euler forms are currently only supported for ' + 'vector bundles with odd rank') + res = dom.diff_form(rk) g = nab._metric - det = g.det() - if det.is_trivial_zero(): - raise ValueError(f'metric {g} must be non-degenerate') - res = e / abs(det.sqrt()) # this is the Euler form + for frame in dom._get_min_covering(vbundle.orientation()): + cmatrix = [[nab.curvature_form(i, j, frame) + for j in vbundle.irange()] + for i in vbundle.irange()] + res_loc = self.get_local(cmatrix) # not the local Euler form + det = g.det(frame) + if det.is_trivial_zero(): + raise ValueError(f'metric {g} must be non-degenerate') + sqrt_det = det.abs().sqrt() + res.set_restriction(res_loc / sqrt_det) # local Euler form + # TODO: make `res` immutable? return [res] def get_local(self, cmat): @@ -405,5 +419,5 @@ def get_local(self, cmat): m = [[sum(a[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] e = -sum(m[i][i] for i in range(rk)) / (2 * rk) # Pfaffian mod sign - e *= (-1 / (2 * pi)) ** rk # normalize - return [e] + e *= (-1 / (2 * pi)) ** ran # normalize + return e From 62ff48af88ee24979df68297551a540a47dbc1a3 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 18:59:38 +0200 Subject: [PATCH 13/50] Trac #29581: leave comment on to-do --- .../manifolds/differentiable/characteristic_cohomology_class.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 9b4bc49d5c3..7db5b66d33b 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -386,6 +386,7 @@ def get(self, nab): for j in vbundle.irange()] for i in vbundle.irange()] res_loc = self.get_local(cmatrix) # not the local Euler form + # TODO: multiply cmatrix with metric tensor det = g.det(frame) if det.is_trivial_zero(): raise ValueError(f'metric {g} must be non-degenerate') From 5b8632d7b5ac52bfe943660b26ffabe35b2c7a8f Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 22:22:13 +0200 Subject: [PATCH 14/50] Trac #29581: euler form algorithm finished --- .../characteristic_cohomology_class.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 7db5b66d33b..782ab80a921 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -257,10 +257,9 @@ def get(self, nab): dom = nab._domain res = [] # will be specified within first iteration for frame in dom._get_min_covering(nab._coefficients): - cmatrix = [[nab.curvature_form(i, j, frame) - for j in vbundle.irange()] - for i in vbundle.irange()] - res_loc = self.get_local(cmatrix) + cmat = [[nab.curvature_form(i, j, frame) for j in vbundle.irange()] + for i in vbundle.irange()] + res_loc = self.get_local(cmat) if not res: # until now, degrees of generators were unknown res = [dom.diff_form(loc_form.degree()) @@ -382,11 +381,12 @@ def get(self, nab): res = dom.diff_form(rk) g = nab._metric for frame in dom._get_min_covering(vbundle.orientation()): - cmatrix = [[nab.curvature_form(i, j, frame) - for j in vbundle.irange()] - for i in vbundle.irange()] - res_loc = self.get_local(cmatrix) # not the local Euler form - # TODO: multiply cmatrix with metric tensor + # (G_s * Ω_s)_ij = g(R(.,.)s_i, s_j) + gcmat = [[sum(g[[frame, i, j]] * nab.curvature_form(j, k, frame) + for j in vbundle.irange()) + for k in vbundle.irange()] for i in vbundle.irange()] + res_loc = self.get_local(gcmat) # Pf(G_s * Ω_s) mod const. + # e = 1 / sqrt(|det(G_s)|) * Pf(G_s * Ω_s) mod const. det = g.det(frame) if det.is_trivial_zero(): raise ValueError(f'metric {g} must be non-degenerate') @@ -419,6 +419,6 @@ def get_local(self, cmat): m[i][i] += e m = [[sum(a[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] - e = -sum(m[i][i] for i in range(rk)) / (2 * rk) # Pfaffian mod sign + e = -sum(m[i][i] for i in range(rk)) / (2 * ran) # Pfaffian mod sign e *= (-1 / (2 * pi)) ** ran # normalize return e From 4afc9f0d0ce1212516c67f6d04617151ef4d0ea3 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 23:01:17 +0200 Subject: [PATCH 15/50] Trac #29581: compute forms only on demand --- .../characteristic_cohomology_class.py | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 782ab80a921..974aa3afe96 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -80,21 +80,17 @@ def get_form(self, nab): self._mixed_forms[nab] = A(dom._zero_scalar_field) else: # non-trivial case from functools import reduce - from itertools import chain parent = self.parent() + algorithm = parent._algorithm - gen_algorithm = parent._algorithm # this is a list - # concatenate generators - gen_forms = list(chain.from_iterable(a.get(nab) - for a in gen_algorithm)) grading = parent.print_options()['sorting_key'] res = [dom.diff_form_module(i).zero() for i in range(dom._dim + 1)] for ind, c in self: deg = grading(ind) - gen_pow = [fast_wedge_power(f, i) - for f, i in zip(gen_forms, ind)] + gen_pow = [algorithm.get_gen_pow(nab, i, ind[i]) + for i in range(len(ind))] res[deg] += c * reduce(lambda x, y: x.wedge(y), gen_pow) res = A(res) # convert result into mixed form @@ -157,17 +153,17 @@ def __init__(self, base, vbundle): ran = min(rk, dim // 2) names = [f'c_{i}' for i in range(1, ran + 1)] degrees = [2 * i for i in range(1, ran + 1)] - self._algorithm = [ChernAlgorithm()] + self._algorithm = ChernAlgorithm() elif vbundle._field_type == 'real': ran = min(rk // 2, dim // 4) names = [f'p_{i}' for i in range(1, ran + 1)] degrees = [4 * i for i in range(1, ran + 1)] - self._algorithm = [PontryaginAlgorithm()] + self._algorithm = PontryaginAlgorithm() if vbundle.has_orientation(): # add Euler class generator - names += ['e'] - degrees += [rk] - self._algorithm += [EulerAlgorithm()] + names = ['e'] + names + degrees = [rk] + degrees + self._algorithm = PontryaginEulerAlgorithm() # TODO: add relation e^2=p_k for dim=2*k else: raise TypeError(f'Characteristic cohomology classes not supported ' @@ -277,6 +273,14 @@ def get_local(self, cmat): """ pass + @cached_method + def get_gen_pow(self, nab, i, n): + r""" + Return the `n`-th power of the `i`-th generator. + """ + if n == 0: + return nab._domain._one_scalar_field # no computation necessary + return fast_wedge_power(self.get(nab)[i], n) class ChernAlgorithm(Singleton, Algorithm_generic): r""" @@ -385,7 +389,7 @@ def get(self, nab): gcmat = [[sum(g[[frame, i, j]] * nab.curvature_form(j, k, frame) for j in vbundle.irange()) for k in vbundle.irange()] for i in vbundle.irange()] - res_loc = self.get_local(gcmat) # Pf(G_s * Ω_s) mod const. + [res_loc] = self.get_local(gcmat) # Pf(G_s * Ω_s) mod const. # e = 1 / sqrt(|det(G_s)|) * Pf(G_s * Ω_s) mod const. det = g.det(frame) if det.is_trivial_zero(): @@ -421,4 +425,28 @@ def get_local(self, cmat): for j in range(rk)] for i in range(rk)] e = -sum(m[i][i] for i in range(rk)) / (2 * ran) # Pfaffian mod sign e *= (-1 / (2 * pi)) ** ran # normalize - return e + return [e] + + +class PontryaginEulerAlgorithm(Singleton, Algorithm_generic): + r""" + + """ + def get_local(self, cmat): + r""" + + """ + res = [EulerAlgorithm().get_local(cmat)] # first entry is Euler class + res += PontryaginAlgorithm().get_local(cmat) # rest Pontryagin + return res + + @cached_method + def get_gen_pow(self, nab, i, n): + r""" + Return the `n`-th power of the `i`-th generator. + """ + if n == 0: + return nab._domain._one_scalar_field # no computation necessary + if i == 0: + return fast_wedge_power(EulerAlgorithm().get(nab)[0], n) + return fast_wedge_power(PontryaginAlgorithm().get(nab)[i-1], n) From 201831f1338973a6c5eefb6c9ee61592f9e38f0f Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 18 Aug 2021 23:25:53 +0200 Subject: [PATCH 16/50] Trac #29581: commenting improved --- .../characteristic_cohomology_class.py | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 974aa3afe96..a13c2047f5f 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -276,7 +276,7 @@ def get_local(self, cmat): @cached_method def get_gen_pow(self, nab, i, n): r""" - Return the `n`-th power of the `i`-th generator. + Return the `n`-th power of the `i`-th generator's characteristic form. """ if n == 0: return nab._domain._one_scalar_field # no computation necessary @@ -430,13 +430,34 @@ def get_local(self, cmat): class PontryaginEulerAlgorithm(Singleton, Algorithm_generic): r""" - + Algorithm class to generate Euler and Pontryagin forms. """ + @cached_method + def get(self, nab): + r""" + Return the global characteristic forms of the generators w.r.t. a given + connection. + + OUTPUT: + + - a list containing the global Euler form in the first entry, and the + global Pontryagin forms in the remaining entries. + + """ + return EulerAlgorithm().get(nab) + PontryaginAlgorithm().get(nab) + def get_local(self, cmat): r""" + Return the local Euler and Pontryagin forms w.r.t. a given curvature + matrix. + + OUTPUT: + + - a list containing the local Euler form in the first entry, and the + local Pontryagin forms in the remaining entries. """ - res = [EulerAlgorithm().get_local(cmat)] # first entry is Euler class + res = EulerAlgorithm().get_local(cmat) # first entry is Euler class res += PontryaginAlgorithm().get_local(cmat) # rest Pontryagin return res From e17fe37d0ea5ecdd1e9932145b908037ed0261cd Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 19 Aug 2021 11:09:51 +0200 Subject: [PATCH 17/50] Trac #29581: comments further improved + _repr_ method --- .../characteristic_cohomology_class.py | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index a13c2047f5f..3e2ff7c8500 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -161,6 +161,7 @@ def __init__(self, base, vbundle): self._algorithm = PontryaginAlgorithm() if vbundle.has_orientation(): # add Euler class generator + # Euler should be first entry; see `PontryaginEulerAlgorithm` names = ['e'] + names degrees = [rk] + degrees self._algorithm = PontryaginEulerAlgorithm() @@ -170,6 +171,9 @@ def __init__(self, base, vbundle): f'for vector bundles with ' f'field type {vbundle._field_type}') + if not names or not degrees: + raise ValueError(f'cannot find any generators') + names = tuple(names) # hashable degrees = tuple(degrees) # hashable super().__init__(base=base, names=names, degrees=degrees, @@ -201,6 +205,14 @@ def _element_constructor_(self, x, name=None, latex_name=None): return self.element_class(self, d, name=name, latex_name=latex_name) + def _repr_(self): + r""" + + """ + vbundle = self.parent()._vbundle + repr = f'Algebra of characteristic cohomology classes of the {vbundle}' + return repr + # ***************************************************************************** # ALGORITHMS @@ -243,6 +255,11 @@ def get(self, nab): r""" Return the global characteristic forms of the generators w.r.t. a given connection. + + OUTPUT: + + - a list containing the generator's global characteristic forms + """ if isinstance(nab, AffineConnection): vbundle = nab._domain.tangent_bundle() @@ -269,7 +286,12 @@ def get(self, nab): def get_local(self, cmat): r""" Abstract method to get the local forms of the generators w.r.t. a given - curvature matrix `cmat`. + curvature matrix ``cmat``. + + OUTPUT: + + - a list containing the generator's local characteristic forms + """ pass @@ -282,6 +304,7 @@ def get_gen_pow(self, nab, i, n): return nab._domain._one_scalar_field # no computation necessary return fast_wedge_power(self.get(nab)[i], n) + class ChernAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Chern forms. @@ -291,6 +314,10 @@ def get_local(self, cmat): r""" Return the local Chern forms w.r.t. a given curvature matrix. + OUTPUT: + + - a list containing the local characteristic Chern forms + .. ALGORITHM:: The algorithm is based on the Faddeev-LeVerrier algorithm for the @@ -329,6 +356,10 @@ def get_local(self, cmat): r""" Return the local Pontryagin forms w.r.t. a given curvature matrix. + OUTPUT: + + - a list containing the local characteristic Pontryagin forms + .. ALGORITHM:: The algorithm is based on the Faddeev-LeVerrier algorithm for the @@ -369,6 +400,11 @@ def get(self, nab): r""" Return the global characteristic forms of the generators w.r.t. a given connection. + + OUTPUT: + + - a list containing the global characteristic Euler form + """ if not isinstance(nab, LeviCivitaConnection): raise TypeError('Euler forms are currently only supported for ' @@ -403,6 +439,19 @@ def get_local(self, cmat): r""" Return the normalized Pfaffian w.r.t. a given curvature matrix. + The normalization is given by the factor + `\left(\frac{1}{2 \pi}\right)^{\frac{k}{2}}`, where `k` is the + dimension of the curvature matrix. + + OUTPUT: + + - a list containing the normalized Pfaffian of a given curvature form + + .. NOTE:: + + The result is the local Euler form if ``cmat`` is given w.r.t. an + orthonormal oriented frame. + .. ALGORITHM:: The algorithm is based on the Bär-Faddeev-LeVerrier algorithm for @@ -432,6 +481,7 @@ class PontryaginEulerAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Euler and Pontryagin forms. """ + @cached_method def get(self, nab): r""" From 8e3263c69f8099d8dcbf9faf07999304642c7222 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 19 Aug 2021 15:07:19 +0200 Subject: [PATCH 18/50] Trac #29581: deprecate alias and more --- .../characteristic_cohomology_class.py | 19 +++++++++- .../manifolds/differentiable/vector_bundle.py | 38 ++++++++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 3e2ff7c8500..7ca8e99f448 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -132,7 +132,22 @@ def get_form(self, nab): return self._mixed_forms[nab] - representative = get_form + def representative(self, nab=None): + r""" + Return any representative of ``self``. + + INPUT: + + - ``nab`` -- (default: ``None``) if stated, return the representative + w.r.t. to the connection ``nab``; otherwise an arbitrary already + computed representative will be chosen + + """ + if nab is None: + if not self._mixed_forms: + raise AttributeError('cannot pick a representative') + return next(iter(self._mixed_forms.values())) + return self.get_form(nab) class CharacteristicCohomologyClassRing(FiniteGCAlgebra): @@ -209,7 +224,7 @@ def _repr_(self): r""" """ - vbundle = self.parent()._vbundle + vbundle = self._vbundle repr = f'Algebra of characteristic cohomology classes of the {vbundle}' return repr diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py index c40d4d2acf3..709ff479196 100644 --- a/src/sage/manifolds/differentiable/vector_bundle.py +++ b/src/sage/manifolds/differentiable/vector_bundle.py @@ -35,6 +35,8 @@ from sage.rings.real_mpfr import RR from sage.manifolds.vector_bundle import TopologicalVectorBundle from sage.rings.infinity import infinity +from sage.misc.superseded import deprecated_function_alias +from sage.rings.rational_field import QQ class DifferentiableVectorBundle(TopologicalVectorBundle): r""" @@ -163,7 +165,37 @@ def bundle_connection(self, name, latex_name=None): from .bundle_connection import BundleConnection return BundleConnection(self, name, latex_name) - def characteristic_class(self, func, **kwargs): + def characteristic_cohomology_class_ring(self, base=QQ): + r""" + Return the characteristic cohomology class ring of ``self`` over + a given base. + + INPUT: + + - ``base`` -- (default: ``QQ``) base over which the ring should be + constructed; typically that would be `\ZZ`, `\QQ`, `\RR` or the + symbolic ring + + EXAMPLES:: + + sage: M = Manifold(4, 'M', start_index=1) + sage: R = M.tangent_bundle().characteristic_cohomology_class_ring() + sage: R + Algebra of characteristic cohomology classes of the Tangent bundle + TM over the 4-dimensional differentiable manifold M + sage: p1 = R.gen(0); p1 + Characteristic cohomology class (p_1)(TM) of the Tangent bundle TM + over the 4-dimensional differentiable manifold M + sage: 1 + p1 + Characteristic cohomology class (1 + p_1)(TM) of the Tangent bundle + TM over the 4-dimensional differentiable manifold M + + """ + from .characteristic_cohomology_class import CharacteristicCohomologyClassRing + + return CharacteristicCohomologyClassRing(base, self) + + def characteristic_cohomology_class(self, func, **kwargs): r""" Return a characteristic class of the given type with respect to the given function. @@ -233,7 +265,7 @@ def characteristic_class(self, func, **kwargs): sage: TM = M.tangent_bundle(); TM Tangent bundle TM over the 4-dimensional Lorentzian manifold M - sage: p = TM.characteristic_class('Pontryagin'); p + sage: p = TM.characteristic_cohomology_class('Pontryagin'); p Characteristic class p of multiplicative type associated to x + 1 on the Tangent bundle TM over the 4-dimensional Lorentzian manifold M @@ -270,6 +302,8 @@ def characteristic_class(self, func, **kwargs): return CharacteristicClass(self, func, class_type=class_type, name=name, latex_name=latex_name) + characteristic_class = deprecated_function_alias(29581, characteristic_cohomology_class) + def diff_degree(self): r""" Return the vector bundle's degree of differentiability. From 74d324f37e20c80488d38f1f4e8c22c0a3e02108 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 29 Aug 2021 17:59:58 +0200 Subject: [PATCH 19/50] Trac #29851: add sequences --- .../characteristic_cohomology_class.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 7ca8e99f448..e623c53a2d3 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -228,6 +228,50 @@ def _repr_(self): repr = f'Algebra of characteristic cohomology classes of the {vbundle}' return repr + def multiplicative_sequence(self, q): + r""" + Turn the polynomial ``q`` into its multiplicative sequence. + + OUTPUT: + + - A symmetric polynomial representing the multiplicative sequence. + """ + from sage.combinat.sf.sf import SymmetricFunctions + from sage.combinat.partition import Partitions + from sage.misc.misc_c import prod + + R = q.parent().base_ring() + Sym = SymmetricFunctions(R) + m = Sym.m() + + # Get the multiplicative sequence in the monomial basis: + mon_pol = m._from_dict({p: prod(q[i] for i in p) + for k in range(len(q.degree())) + for p in Partitions(k)}) + return Sym.e()(mon_pol) + + def additive_sequence(self, q): + r""" + Turn the polynomial ``q`` into its additive sequence. + + OUTPUT: + + - A symmetric polynomial representing the additive sequence. + """ + from sage.combinat.sf.sf import SymmetricFunctions + from sage.combinat.partition import Partitions + + R = q.parent().base_ring() + Sym = SymmetricFunctions(R) + m = Sym.m() + rk = self._vbundle._rank + + # Express the additive sequence in the monomial basis, the 0th + # order term must be treated separately: + m_dict = {Partitions(0)([]): rk * q[0]} + m_dict.update({Partitions(k)([k]): q[k] for k in range(1, q.degree())}) + mon_pol = m._from_dict(m_dict) + return Sym.e()(mon_pol) # ***************************************************************************** # ALGORITHMS From 1f51ca73c5e5ab0ac1a5e5361b1100198c765dd1 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 29 Aug 2021 18:04:20 +0200 Subject: [PATCH 20/50] Trac #29851: sequences as static functions --- .../characteristic_cohomology_class.py | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index e623c53a2d3..265ed251e1d 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -228,54 +228,59 @@ def _repr_(self): repr = f'Algebra of characteristic cohomology classes of the {vbundle}' return repr - def multiplicative_sequence(self, q): - r""" - Turn the polynomial ``q`` into its multiplicative sequence. +# ***************************************************************************** +# ALGORITHMS +# ***************************************************************************** - OUTPUT: +def multiplicative_sequence(q): + r""" + Turn the polynomial ``q`` into its multiplicative sequence. - - A symmetric polynomial representing the multiplicative sequence. - """ - from sage.combinat.sf.sf import SymmetricFunctions - from sage.combinat.partition import Partitions - from sage.misc.misc_c import prod + OUTPUT: - R = q.parent().base_ring() - Sym = SymmetricFunctions(R) - m = Sym.m() + - A symmetric polynomial representing the multiplicative sequence. + """ + from sage.combinat.sf.sf import SymmetricFunctions + from sage.combinat.partition import Partitions + from sage.misc.misc_c import prod - # Get the multiplicative sequence in the monomial basis: - mon_pol = m._from_dict({p: prod(q[i] for i in p) - for k in range(len(q.degree())) - for p in Partitions(k)}) - return Sym.e()(mon_pol) + R = q.parent().base_ring() + Sym = SymmetricFunctions(R) + m = Sym.m() - def additive_sequence(self, q): - r""" - Turn the polynomial ``q`` into its additive sequence. + # Get the multiplicative sequence in the monomial basis: + mon_pol = m._from_dict({p: prod(q[i] for i in p) + for k in range(len(q.degree())) + for p in Partitions(k)}) + return Sym.e()(mon_pol) - OUTPUT: +def additive_sequence(q, rk): + r""" + Turn the polynomial ``q`` into its additive sequence. - - A symmetric polynomial representing the additive sequence. - """ - from sage.combinat.sf.sf import SymmetricFunctions - from sage.combinat.partition import Partitions + INPUT: - R = q.parent().base_ring() - Sym = SymmetricFunctions(R) - m = Sym.m() - rk = self._vbundle._rank + - ``q`` -- polynomial to turn into its additive sequence. + - ``rk`` -- rank of the underlying vector bundle - # Express the additive sequence in the monomial basis, the 0th - # order term must be treated separately: - m_dict = {Partitions(0)([]): rk * q[0]} - m_dict.update({Partitions(k)([k]): q[k] for k in range(1, q.degree())}) - mon_pol = m._from_dict(m_dict) - return Sym.e()(mon_pol) + OUTPUT: + + - A symmetric polynomial representing the additive sequence. + """ + from sage.combinat.sf.sf import SymmetricFunctions + from sage.combinat.partition import Partitions + + R = q.parent().base_ring() + Sym = SymmetricFunctions(R) + m = Sym.m() + + # Express the additive sequence in the monomial basis, the 0th + # order term must be treated separately: + m_dict = {Partitions(0)([]): rk * q[0]} + m_dict.update({Partitions(k)([k]): q[k] for k in range(1, q.degree())}) + mon_pol = m._from_dict(m_dict) + return Sym.e()(mon_pol) -# ***************************************************************************** -# ALGORITHMS -# ***************************************************************************** def fast_wedge_power(form, n): r""" From ffa79bb0e94248699da3b73946310aa8eefa9516 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 29 Aug 2021 18:10:04 +0200 Subject: [PATCH 21/50] Trac #29851: improve doc --- .../differentiable/characteristic_cohomology_class.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 265ed251e1d..68c865f834e 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -275,7 +275,7 @@ def additive_sequence(q, rk): m = Sym.m() # Express the additive sequence in the monomial basis, the 0th - # order term must be treated separately: + # order term must be treated separately; here comes ``rk`` into play: m_dict = {Partitions(0)([]): rk * q[0]} m_dict.update({Partitions(k)([k]): q[k] for k in range(1, q.degree())}) mon_pol = m._from_dict(m_dict) @@ -362,7 +362,8 @@ def get_local(self, cmat): @cached_method def get_gen_pow(self, nab, i, n): r""" - Return the `n`-th power of the `i`-th generator's characteristic form. + Return the `n`-th power of the `i`-th generator's characteristic form + w.r.t ``nab``. """ if n == 0: return nab._domain._one_scalar_field # no computation necessary @@ -578,7 +579,7 @@ def get_local(self, cmat): @cached_method def get_gen_pow(self, nab, i, n): r""" - Return the `n`-th power of the `i`-th generator. + Return the `n`-th power of the `i`-th generator w.r.t ``nab``. """ if n == 0: return nab._domain._one_scalar_field # no computation necessary From 54cf11fa0f701f76523b1da5bf0e1434d9ab1723 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 12:40:40 +0200 Subject: [PATCH 22/50] Trac #29851: factory class --- .../characteristic_cohomology_class.py | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 68c865f834e..0457d2e6361 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -20,9 +20,10 @@ from .affine_connection import AffineConnection from .bundle_connection import BundleConnection from .levi_civita_connection import LeviCivitaConnection +from sage.rings.rational_field import QQ -class CharacteristicCohomologyClass(IndexedFreeModuleElement): +class CharacteristicCohomologyClassRingElement(IndexedFreeModuleElement): r""" """ @@ -154,7 +155,7 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): r""" """ - Element = CharacteristicCohomologyClass + Element = CharacteristicCohomologyClassRingElement def __init__(self, base, vbundle): r""" @@ -250,7 +251,7 @@ def multiplicative_sequence(q): # Get the multiplicative sequence in the monomial basis: mon_pol = m._from_dict({p: prod(q[i] for i in p) - for k in range(len(q.degree())) + for k in range(q.degree() + 1) for p in Partitions(k)}) return Sym.e()(mon_pol) @@ -277,11 +278,29 @@ def additive_sequence(q, rk): # Express the additive sequence in the monomial basis, the 0th # order term must be treated separately; here comes ``rk`` into play: m_dict = {Partitions(0)([]): rk * q[0]} - m_dict.update({Partitions(k)([k]): q[k] for k in range(1, q.degree())}) + m_dict.update({Partitions(k)([k]): q[k] for k in range(1, q.degree() + 1)}) mon_pol = m._from_dict(m_dict) return Sym.e()(mon_pol) +def CharacteristicCohomologyClass(*args, **kwargs): + r""" + + """ + pass + # name, latex_name = kwargs.get('name'), kwargs.get('latex_name') + # base_ring = kwargs.get('base_ring', QQ) + # class_type = kwargs.get('class_type') + # vbundle = args[0] + # input = args[1] + # R = CharacteristicCohomologyClassRing(base_ring, vbundle) + # w_vec = R._weighted_vectors + # if isinstance(input, Expression): + # + # + # return R(element, name=name, latex_name=latex_name) + + def fast_wedge_power(form, n): r""" Return the wedge product power of `form` using a square-and-wedge From ba71d70d8b94d8e6ffb9d59a5a9969b98d540ab7 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 13:00:29 +0200 Subject: [PATCH 23/50] Trac #29581: turn polynomial into dictionary --- .../characteristic_cohomology_class.py | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 0457d2e6361..a6a0beba7c5 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -287,18 +287,34 @@ def CharacteristicCohomologyClass(*args, **kwargs): r""" """ - pass - # name, latex_name = kwargs.get('name'), kwargs.get('latex_name') - # base_ring = kwargs.get('base_ring', QQ) - # class_type = kwargs.get('class_type') - # vbundle = args[0] - # input = args[1] - # R = CharacteristicCohomologyClassRing(base_ring, vbundle) - # w_vec = R._weighted_vectors - # if isinstance(input, Expression): - # - # - # return R(element, name=name, latex_name=latex_name) + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + name, latex_name = kwargs.get('name'), kwargs.get('latex_name') + base_ring = kwargs.get('base_ring', QQ) + class_type = kwargs.get('class_type') + vbundle = args[0] + input = args[1] + R = CharacteristicCohomologyClassRing(base_ring, vbundle) + if is_PolynomialRing(input.parent()): + from sage.misc.misc_c import prod + if class_type is None: + raise AttributeError(f'class_type must be stated since {input} ' + f'is a polynomial') + if class_type == 'additive': + sym = additive_sequence(input) + elif class_type == 'multiplicative': + sym = multiplicative_sequence(input) + + input = {} + zero = [0] * R.__ngens + w_vec = R._weighted_vectors + for p, c in sym: + vec = zero.copy() + for i in p: + vec[i] += 1 + key = w_vec(vec) + input[key] = c + + return R(input, name=name, latex_name=latex_name) def fast_wedge_power(form, n): From d0f14dc7caeb6fb7a225d73939af6f5a95e06ca7 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 16:12:26 +0200 Subject: [PATCH 24/50] Trac #29581: constructor class finished --- .../manifolds/diff_vector_bundle.rst | 2 +- .../characteristic_cohomology_class.py | 206 +++++++++++++++--- 2 files changed, 180 insertions(+), 28 deletions(-) diff --git a/src/doc/en/reference/manifolds/diff_vector_bundle.rst b/src/doc/en/reference/manifolds/diff_vector_bundle.rst index 5cfb22dae90..fdfdf39df61 100644 --- a/src/doc/en/reference/manifolds/diff_vector_bundle.rst +++ b/src/doc/en/reference/manifolds/diff_vector_bundle.rst @@ -8,4 +8,4 @@ Differentiable Vector Bundles sage/manifolds/differentiable/bundle_connection - sage/manifolds/differentiable/characteristic_class + sage/manifolds/differentiable/characteristic_cohomology_class diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index a6a0beba7c5..fed9a6e276d 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -15,7 +15,7 @@ from sage.combinat.free_module import IndexedFreeModuleElement from sage.misc.fast_methods import Singleton from sage.structure.sage_object import SageObject -from sage.misc.cachefunc import cached_method +from sage.misc.cachefunc import cached_method, cached_function from sage.misc.abstract_method import abstract_method from .affine_connection import AffineConnection from .bundle_connection import BundleConnection @@ -150,6 +150,27 @@ def representative(self, nab=None): return next(iter(self._mixed_forms.values())) return self.get_form(nab) + def set_name(self, name=None, latex_name=None): + r""" + Set (or change) the text name and LaTeX name of the characteristic + cohomology class. + + INPUT: + + - ``name`` -- (string; default: ``None``) name given to the + characteristic cohomology class + - ``latex_name`` -- (string; default: ``None``) LaTeX symbol to denote + the characteristic cohomology class; if ``None`` while ``name`` is + provided, the LaTeX symbol is set to ``name`` + + """ + if name is not None: + self._name = name + if latex_name is None: + self._latex_name = self._name + if latex_name is not None: + self._latex_name = latex_name + class CharacteristicCohomologyClassRing(FiniteGCAlgebra): r""" @@ -204,7 +225,7 @@ def _element_constructor_(self, x, name=None, latex_name=None): if x in R: one_basis = self.one_basis() d = {one_basis: R(x)} - elif isinstance(x, CharacteristicCohomologyClass): + elif isinstance(x, CharacteristicCohomologyClassRingElement): d = x._monomial_coefficients # x is an element of the basis enumerated set; # This is a very ugly way of testing this @@ -233,10 +254,16 @@ def _repr_(self): # ALGORITHMS # ***************************************************************************** -def multiplicative_sequence(q): +def multiplicative_sequence(q, max_order=None): r""" Turn the polynomial ``q`` into its multiplicative sequence. + INPUT: + + - ``q`` -- polynomial to turn into its multiplicative sequence. + - ``max_order`` -- (default: ``None``) the highest order of the sequence; + if ``None``, the order of ``q`` is assumed. + OUTPUT: - A symmetric polynomial representing the multiplicative sequence. @@ -245,17 +272,20 @@ def multiplicative_sequence(q): from sage.combinat.partition import Partitions from sage.misc.misc_c import prod + if max_order is None: + max_order = q.degree() + R = q.parent().base_ring() Sym = SymmetricFunctions(R) m = Sym.m() # Get the multiplicative sequence in the monomial basis: mon_pol = m._from_dict({p: prod(q[i] for i in p) - for k in range(q.degree() + 1) + for k in range(max_order + 1) for p in Partitions(k)}) return Sym.e()(mon_pol) -def additive_sequence(q, rk): +def additive_sequence(q, rk, max_order=None): r""" Turn the polynomial ``q`` into its additive sequence. @@ -263,6 +293,8 @@ def additive_sequence(q, rk): - ``q`` -- polynomial to turn into its additive sequence. - ``rk`` -- rank of the underlying vector bundle + - ``max_order`` -- (default: ``None``) the highest order of the sequence; + if ``None``, the order of ``q`` is assumed. OUTPUT: @@ -271,50 +303,170 @@ def additive_sequence(q, rk): from sage.combinat.sf.sf import SymmetricFunctions from sage.combinat.partition import Partitions + if max_order is None: + max_order = q.degree() + R = q.parent().base_ring() Sym = SymmetricFunctions(R) m = Sym.m() - # Express the additive sequence in the monomial basis, the 0th + # Express the additive sequence in the monomial basis, the 0-th # order term must be treated separately; here comes ``rk`` into play: m_dict = {Partitions(0)([]): rk * q[0]} - m_dict.update({Partitions(k)([k]): q[k] for k in range(1, q.degree() + 1)}) + m_dict.update({Partitions(k)([k]): q[k] for k in range(1, max_order + 1)}) mon_pol = m._from_dict(m_dict) return Sym.e()(mon_pol) - +@cached_function def CharacteristicCohomologyClass(*args, **kwargs): r""" + Construct a characteristic cohomology class. + + INPUT: + + - ``vbundle`` -- the vector bundle over which the characteristic + cohomology class shall be defined + - ``val`` -- input data; could be a string, polynomial, symbolic + expression or characteristic cohomology class + - ``base_ring`` -- (default: ``QQ``) base ring over which the + characteristic cohomology class ring shall be defined + - ``name`` -- (default: ``None``) string representation given to the + characteristic cohomology class; if ``None`` the default algebra + representation is used + - ``latex_name`` -- (default: ``None``) LaTeX name given to the + characteristic class; if ``None`` the value of ``name`` is used + - ``class_type`` -- (default: ``None``) class type of the characteristic + cohomology class; the following options are possible: + + - ``'multiplicative'`` -- returns a class of multiplicative type + - ``'additive'`` -- returns a class of additive type + - ``'Pfaffian'`` -- returns a class of Pfaffian type + + This argument must be stated if ``val`` is a polynomial or symbolic + expression. """ from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.symbolic.expression import Expression + name, latex_name = kwargs.get('name'), kwargs.get('latex_name') base_ring = kwargs.get('base_ring', QQ) class_type = kwargs.get('class_type') vbundle = args[0] - input = args[1] + val = args[1] # input value R = CharacteristicCohomologyClassRing(base_ring, vbundle) - if is_PolynomialRing(input.parent()): - from sage.misc.misc_c import prod + dim = vbundle._base_space._dim + + # predefined classes accessible via class names + if isinstance(val, str): + from sage.arith.misc import factorial, bernoulli + + P = PolynomialRing(base_ring, 'x') + x = P.gen() + if val == 'Chern': + if vbundle._field_type != 'complex': + raise ValueError(f'total Chern class not defined on {vbundle}') + class_type = 'multiplicative' + val = 1 + x + if val == 'Pontryagin': + if vbundle._field_type != 'real': + raise ValueError(f'total Pontryagin class not defined on {vbundle}') + class_type = 'multiplicative' + val = 1 + x + elif val == 'ChernChar': + if vbundle._field_type != 'complex': + raise ValueError(f'Chern character not defined on {vbundle}') + if name is None: + name = f'ch' + if latex_name is None: + latex_name = r'\mathrm{ch}' + class_type = 'additive' + coeff = [1 / factorial(k) for k in range(dim // 2 + 1)] # exp(x) + val = P(coeff) + elif val == 'Todd': + if vbundle._field_type != 'complex': + raise ValueError(f'Todd class not defined on {vbundle}') + if name is None: + name = f'Td({vbundle._name})' + if latex_name is None: + latex_name = r'\mathrm{Td}' + class_type = 'multiplicative' + val = 1 + x / 2 + for k in range(1, dim // 2 + 1): + val += (-1)**(k+1) / factorial(2*k) * bernoulli(2*k) * x**(2*k) + elif val == 'Hirzebruch': + if vbundle._field_type != 'real': + raise ValueError(f'Hirzebruch class not defined on {vbundle}') + if name is None: + name = f'L' + if latex_name is None: + latex_name = r'\mathrm{L}' + class_type = 'multiplicative' + coeff = [2**(2*k) * bernoulli(2*k) / factorial(2*k) + for k in range(dim // 4 + 1)] + val = P(coeff) + elif val == 'Euler': + if not vbundle._field_type != 'real' or not vbundle.has_orientation(): + raise ValueError(f'Euler class not defined on {vbundle}') + val = x + else: + ValueError(f'predefined class "{val}" unknown') + + # turn symbolic expression into a polynomial via Taylor expansion + if isinstance(val, Expression): + x = val.default_variable() + P = PolynomialRing(base_ring, x) + + if vbundle._field_type == 'real': + pow_range = dim // 4 + elif vbundle._field_type == 'complex': + pow_range = dim // 2 + else: + ValueError(f'field type of {vbundle} must be real or complex') + + val = P(val.taylor(x, 0, pow_range)) + + # turn polynomial into a characteristic cohomology class via sequences + if is_PolynomialRing(val.parent()): if class_type is None: - raise AttributeError(f'class_type must be stated since {input} ' - f'is a polynomial') + raise TypeError(f'class_type must be stated if {val} ' + f'is a polynomial') + max_order = R.ngens() + s = 0 # shift; important in case of Euler class generator + if R._algorithm is PontryaginEulerAlgorithm(): + s = 1 # skip Euler class + max_order -= 1 # ignore Euler class + + d = {} + w_vec = R._weighted_vectors + if class_type == 'additive': - sym = additive_sequence(input) + sym = additive_sequence(val, vbundle._rank, max_order=max_order) elif class_type == 'multiplicative': - sym = multiplicative_sequence(input) - - input = {} - zero = [0] * R.__ngens - w_vec = R._weighted_vectors + sym = multiplicative_sequence(val, max_order=max_order) + elif class_type == 'Pfaffian': + P = val.parent() + x = P.gen() + val = (val(x) + val(-x)) / 2 # project to odd functions + val = P([(-1)**k * val[2*k+1] for k in range(max_order + 1)]) + sym = multiplicative_sequence(val, max_order=max_order) + else: + AttributeError('unkown class type') + for p, c in sym: - vec = zero.copy() + vec = [0] * R.ngens() + if class_type == 'Pfaffian': + vec[0] = 1 # always multiply with e for i in p: - vec[i] += 1 + vec[i - 1 + s] += 1 key = w_vec(vec) - input[key] = c + d[key] = c + res = R._from_dict(d) + res.set_name(name=name, latex_name=latex_name) + return res - return R(input, name=name, latex_name=latex_name) + return R(val, name=name, latex_name=latex_name) def fast_wedge_power(form, n): @@ -418,7 +570,7 @@ def get_local(self, cmat): - a list containing the local characteristic Chern forms - .. ALGORITHM:: + ALGORITHM:: The algorithm is based on the Faddeev-LeVerrier algorithm for the characteristic polynomial. @@ -460,7 +612,7 @@ def get_local(self, cmat): - a list containing the local characteristic Pontryagin forms - .. ALGORITHM:: + ALGORITHM:: The algorithm is based on the Faddeev-LeVerrier algorithm for the characteristic polynomial. @@ -517,7 +669,7 @@ def get(self, nab): 'vector bundles') if rk % 2 != 0: raise ValueError('Euler forms are currently only supported for ' - 'vector bundles with odd rank') + 'vector bundles with even rank') res = dom.diff_form(rk) g = nab._metric for frame in dom._get_min_covering(vbundle.orientation()): @@ -552,7 +704,7 @@ def get_local(self, cmat): The result is the local Euler form if ``cmat`` is given w.r.t. an orthonormal oriented frame. - .. ALGORITHM:: + ALGORITHM:: The algorithm is based on the Bär-Faddeev-LeVerrier algorithm for the Pfaffian. From 4b371bd075fc8c82328cffeff08c7cee91bfa9b5 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 16:56:35 +0200 Subject: [PATCH 25/50] Trac #29581: add AHat class + minor fixes --- .../differentiable/characteristic_cohomology_class.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index fed9a6e276d..6e5488c1897 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -406,9 +406,15 @@ def CharacteristicCohomologyClass(*args, **kwargs): coeff = [2**(2*k) * bernoulli(2*k) / factorial(2*k) for k in range(dim // 4 + 1)] val = P(coeff) + elif val == 'AHat': + class_type = 'multiplicative' + coeff = [- (2**(2*k) - 2) / 2**(2*k) * bernoulli(2*k) / factorial(2*k) + for k in range(dim // 4 + 1)] + val = P(coeff) elif val == 'Euler': - if not vbundle._field_type != 'real' or not vbundle.has_orientation(): + if vbundle._field_type != 'real' or not vbundle.has_orientation(): raise ValueError(f'Euler class not defined on {vbundle}') + class_type = 'Pfaffian' val = x else: ValueError(f'predefined class "{val}" unknown') @@ -448,7 +454,7 @@ def CharacteristicCohomologyClass(*args, **kwargs): elif class_type == 'Pfaffian': P = val.parent() x = P.gen() - val = (val(x) + val(-x)) / 2 # project to odd functions + val = (val(x) - val(-x)) / 2 # project to odd functions val = P([(-1)**k * val[2*k+1] for k in range(max_order + 1)]) sym = multiplicative_sequence(val, max_order=max_order) else: From e82168f67b13aec1f32208eef2a2bf17b7e6fc04 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 17:06:14 +0200 Subject: [PATCH 26/50] Trac #29581: minor improvements --- .../differentiable/characteristic_cohomology_class.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 6e5488c1897..ed307d12818 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -444,9 +444,6 @@ def CharacteristicCohomologyClass(*args, **kwargs): s = 1 # skip Euler class max_order -= 1 # ignore Euler class - d = {} - w_vec = R._weighted_vectors - if class_type == 'additive': sym = additive_sequence(val, vbundle._rank, max_order=max_order) elif class_type == 'multiplicative': @@ -460,6 +457,8 @@ def CharacteristicCohomologyClass(*args, **kwargs): else: AttributeError('unkown class type') + d = {} + w_vec = R._weighted_vectors for p, c in sym: vec = [0] * R.ngens() if class_type == 'Pfaffian': @@ -472,6 +471,7 @@ def CharacteristicCohomologyClass(*args, **kwargs): res.set_name(name=name, latex_name=latex_name) return res + # last resort: try coercion return R(val, name=name, latex_name=latex_name) From 9f53f25b684540bf08e1d64d8a31b9420ebcce25 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 17:11:56 +0200 Subject: [PATCH 27/50] Trac #29581: adapt names --- .../characteristic_cohomology_class.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index ed307d12818..5baae591109 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -378,7 +378,7 @@ def CharacteristicCohomologyClass(*args, **kwargs): if vbundle._field_type != 'complex': raise ValueError(f'Chern character not defined on {vbundle}') if name is None: - name = f'ch' + name = 'ch' if latex_name is None: latex_name = r'\mathrm{ch}' class_type = 'additive' @@ -388,7 +388,7 @@ def CharacteristicCohomologyClass(*args, **kwargs): if vbundle._field_type != 'complex': raise ValueError(f'Todd class not defined on {vbundle}') if name is None: - name = f'Td({vbundle._name})' + name = 'Td' if latex_name is None: latex_name = r'\mathrm{Td}' class_type = 'multiplicative' @@ -399,14 +399,20 @@ def CharacteristicCohomologyClass(*args, **kwargs): if vbundle._field_type != 'real': raise ValueError(f'Hirzebruch class not defined on {vbundle}') if name is None: - name = f'L' + name = 'L' if latex_name is None: - latex_name = r'\mathrm{L}' + latex_name = 'L' class_type = 'multiplicative' coeff = [2**(2*k) * bernoulli(2*k) / factorial(2*k) for k in range(dim // 4 + 1)] val = P(coeff) elif val == 'AHat': + if vbundle._field_type != 'real': + raise ValueError(f'AHat class not defined on {vbundle}') + if name is None: + name = 'A^' + if latex_name is None: + latex_name = r'\hat{A}' class_type = 'multiplicative' coeff = [- (2**(2*k) - 2) / 2**(2*k) * bernoulli(2*k) / factorial(2*k) for k in range(dim // 4 + 1)] From 86b4d28a2aceb68e36b6fed8ca4ee1d8dcbc754c Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 21:17:34 +0200 Subject: [PATCH 28/50] Trac #29581: add coercion and further improvements --- .../characteristic_cohomology_class.py | 36 +++++-- .../differentiable/de_rham_cohomology.py | 24 ++++- .../manifolds/differentiable/vector_bundle.py | 94 +++++++------------ 3 files changed, 84 insertions(+), 70 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 5baae591109..e442f7eb6e5 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -46,11 +46,11 @@ def _repr_(self): """ if self._name is None: - name = super()._repr_() + name = f'({super()._repr_()})' else: name = self._name vbundle = self.parent()._vbundle - name = f'({name})({vbundle._name})' + name = f'{name}({vbundle._name})' return f'Characteristic cohomology class {name} of the {vbundle}' def _latex_(self): @@ -58,12 +58,11 @@ def _latex_(self): """ if self._latex_name is None: - latex = super()._latex_() + latex = r'\left(' + super()._latex_() + r'\right)' else: latex = self._latex_name vbundle = self.parent()._vbundle - latex = r'\left(' + latex + r'\right)\right(' - latex += vbundle._latex_name + r'\right)' + latex += latex + r'\left(' + vbundle._latex_name + r'\right)' return latex def get_form(self, nab): @@ -101,11 +100,11 @@ def get_form(self, nab): if self._name is None: name = f'({super()._repr_()})' else: - name = f'({self._name})' + name = self._name if self._latex_name is None: latex_name = r'\left(' + super()._latex_() + r'\right)' else: - latex_name = r'\left(' + self._latex_name + r'\right)' + latex_name = self._latex_name # appendix append_name = f'({vbundle._name}, {nab._name})' append_latex_name = r'\left(' + vbundle._latex_name @@ -326,8 +325,19 @@ def CharacteristicCohomologyClass(*args, **kwargs): - ``vbundle`` -- the vector bundle over which the characteristic cohomology class shall be defined - - ``val`` -- input data; could be a string, polynomial, symbolic - expression or characteristic cohomology class + - ``val`` -- the input data corresponding to the characteristic class + using the Chern-Weil homomorphism; this argument can be either a + symbolic expression, a polynomial or one of the following predefined + classes: + + - ``'Chern'`` -- total Chern class, + - ``'ChernChar'`` -- Chern character, + - ``'Todd'`` -- Todd class, + - ``'Pontryagin'`` -- total Pontryagin class, + - ``'Hirzebruch'`` -- Hirzebruch class, + - ``'AHat'`` -- `\hat{A}` class, + - ``'Euler'`` -- Euler class. + - ``base_ring`` -- (default: ``QQ``) base ring over which the characteristic cohomology class ring shall be defined - ``name`` -- (default: ``None``) string representation given to the @@ -367,11 +377,15 @@ def CharacteristicCohomologyClass(*args, **kwargs): if val == 'Chern': if vbundle._field_type != 'complex': raise ValueError(f'total Chern class not defined on {vbundle}') + if name is None: + name = 'c' class_type = 'multiplicative' val = 1 + x if val == 'Pontryagin': if vbundle._field_type != 'real': raise ValueError(f'total Pontryagin class not defined on {vbundle}') + if name is None: + name = 'p' class_type = 'multiplicative' val = 1 + x elif val == 'ChernChar': @@ -420,6 +434,8 @@ def CharacteristicCohomologyClass(*args, **kwargs): elif val == 'Euler': if vbundle._field_type != 'real' or not vbundle.has_orientation(): raise ValueError(f'Euler class not defined on {vbundle}') + if name is None: + name = 'e' class_type = 'Pfaffian' val = x else: @@ -635,7 +651,7 @@ def get_local(self, cmat): rk = len(cmat) dim = dom._dim ran = min(rk // 2, dim // 4) - if ran < 2: + if ran < 1: return [] # nothing to compute fac = -1 / (2 * pi) ** 2 res = [] diff --git a/src/sage/manifolds/differentiable/de_rham_cohomology.py b/src/sage/manifolds/differentiable/de_rham_cohomology.py index 3f6cc7aa9c0..a29dbedd574 100644 --- a/src/sage/manifolds/differentiable/de_rham_cohomology.py +++ b/src/sage/manifolds/differentiable/de_rham_cohomology.py @@ -51,6 +51,8 @@ from sage.structure.parent import Parent from sage.structure.element import AlgebraElement from sage.categories.algebras import Algebras +from .characteristic_cohomology_class import (CharacteristicCohomologyClassRing, + CharacteristicCohomologyClassRingElement) class DeRhamCohomologyClass(AlgebraElement): r""" @@ -412,13 +414,33 @@ def _element_constructor_(self, x): differentiable manifold M must be a closed form """ - if x not in self._module: + if isinstance(x, CharacteristicCohomologyClassRingElement): + x = x.representative() + elif x not in self._module: raise TypeError(f"{x} must be an element of {self._module}") x = self._module(x) if x.derivative() != 0: raise ValueError(f"{x} must be a closed form") return self.element_class(self, x) + def _coerce_map_from_(self, other): + r""" + Determine whether coercion to ``self`` exists from other parent. + + TESTS:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: C = M.de_rham_complex() + sage: H = C.cohomology() + sage: H.has_coerce_map_from(QQ) + True + + """ + if isinstance(other, CharacteristicCohomologyClassRing): + return other._vbundle._base_space == self._manifold + return super()._coerce_map_from_(other) + def _repr_(self): r""" Return a string representation of the object. diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py index 709ff479196..ba9d362509b 100644 --- a/src/sage/manifolds/differentiable/vector_bundle.py +++ b/src/sage/manifolds/differentiable/vector_bundle.py @@ -195,45 +195,42 @@ def characteristic_cohomology_class_ring(self, base=QQ): return CharacteristicCohomologyClassRing(base, self) - def characteristic_cohomology_class(self, func, **kwargs): + def characteristic_cohomology_class(self, *args, **kwargs): r""" - Return a characteristic class of the given type with respect to the - given function. + Return a characteristic cohomology class associated with the input + data. INPUT: - - ``func`` -- the function corresponding to the characteristic class - using the Chern-Weil homomorphism; this argument can be either one of - the predefined classes, in the following specified by - (field type, class type, name, LaTeX name, function): - - - ``'Chern'`` -- (complex, multiplicative, ``c``, `c`, `1+x`), - - ``'ChernChar'`` -- (complex, additive, ``ch``, `\mathrm{ch}`, - `\exp(x)`), - - ``'Todd'`` -- (complex, additive, ``Td``, `\mathrm{Td}`, - `\frac{x}{1-\exp(-x)}`), - - ``'Pontryagin'`` -- (real, multiplicative, ``p``, `p`, `1+x`), - - ``'Hirzebruch'`` -- (real, multiplicative, ``L``, `L`, - `\frac{\sqrt{x}}{\tanh(\sqrt{x})}`), - - ``'AHat'`` -- (real, multiplicative, ``A^``, `\hat{A}`, - `\frac{\sqrt{x}/2}{\sinh(\sqrt{x}/2)}`), - - ``'Euler'`` -- (real, Pfaffian, ``e``, `e`, `x`), - - or a symbolic expression. If ``func`` is one of the predefined classes, - the following arguments are obsolete. - - - ``class_type`` -- (default: ``'multiplicative'``) the type of the - characteristic class; possible values are: - - - ``'multiplicative'`` -- returns a class of multiplicative type, - using the determinant - - ``'additive'`` -- returns a class of additive type, using the trace - - ``'Pfaffian'`` -- returns a class of Pfaffian type, using the - Pfaffian - - - ``name`` -- string representation given to the characteristic class + - ``val`` -- the input data associated with the characteristic class + using the Chern-Weil homomorphism; this argument can be either a + symbolic expression, a polynomial or one of the following predefined + classes: + + - ``'Chern'`` -- total Chern class, + - ``'ChernChar'`` -- Chern character, + - ``'Todd'`` -- Todd class, + - ``'Pontryagin'`` -- total Pontryagin class, + - ``'Hirzebruch'`` -- Hirzebruch class, + - ``'AHat'`` -- `\hat{A}` class, + - ``'Euler'`` -- Euler class. + + - ``base_ring`` -- (default: ``QQ``) base ring over which the + characteristic cohomology class ring shall be defined + - ``name`` -- (default: ``None``) string representation given to the + characteristic cohomology class; if ``None`` the default algebra + representation is used - ``latex_name`` -- (default: ``None``) LaTeX name given to the - characteristic class + characteristic class; if ``None`` the value of ``name`` is used + - ``class_type`` -- (default: ``None``) class type of the characteristic + cohomology class; the following options are possible: + + - ``'multiplicative'`` -- returns a class of multiplicative type + - ``'additive'`` -- returns a class of additive type + - ``'Pfaffian'`` -- returns a class of Pfaffian type + + This argument must be stated if ``val`` is a polynomial or symbolic + expression. EXAMPLES: @@ -266,11 +263,8 @@ def characteristic_cohomology_class(self, func, **kwargs): sage: TM = M.tangent_bundle(); TM Tangent bundle TM over the 4-dimensional Lorentzian manifold M sage: p = TM.characteristic_cohomology_class('Pontryagin'); p - Characteristic class p of multiplicative type associated to x + 1 - on the Tangent bundle TM over the 4-dimensional Lorentzian - manifold M - sage: p.function() - x + 1 + Characteristic cohomology class p(TM) of the Tangent bundle TM over + the 4-dimensional Lorentzian manifold M sage: p_form = p.get_form(nab); p_form.display_expansion() p(TM, nabla_g) = 1 @@ -280,27 +274,9 @@ def characteristic_cohomology_class(self, func, **kwargs): :class:`~sage.manifolds.differentiable.characteristic_class.CharacteristicClass`. """ - if self._field_type == 'neither_real_nor_complex': - raise ValueError("the vector bundle must be real or complex") - from .characteristic_class import CharacteristicClass, _get_predefined_class - # Is func a predefined class? - if isinstance(func, str): - func_str = func - # Get predefined class: - (field_type, class_type, name, - latex_name, func) = _get_predefined_class(func_str) - # The fields must be equal: - if field_type != self._field_type: - raise ValueError("base field must be {} ".format(field_type) + - "for class '{}'".format(func_str)) - else: - # Get arguments: - class_type = kwargs.pop('class_type', 'multiplicative') - name = kwargs.pop('name', None) - latex_name = kwargs.pop('latex_name', None) + from .characteristic_cohomology_class import CharacteristicCohomologyClass - return CharacteristicClass(self, func, class_type=class_type, - name=name, latex_name=latex_name) + return CharacteristicCohomologyClass(self, *args, **kwargs) characteristic_class = deprecated_function_alias(29581, characteristic_cohomology_class) From f81b25d80d4b0b9c692b25af037f1fe7623e96e8 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 21:21:16 +0200 Subject: [PATCH 29/50] Trac #39581: add example for coercion --- .../manifolds/differentiable/de_rham_cohomology.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/de_rham_cohomology.py b/src/sage/manifolds/differentiable/de_rham_cohomology.py index a29dbedd574..d511f44fd5d 100644 --- a/src/sage/manifolds/differentiable/de_rham_cohomology.py +++ b/src/sage/manifolds/differentiable/de_rham_cohomology.py @@ -430,12 +430,23 @@ def _coerce_map_from_(self, other): TESTS:: sage: M = Manifold(2, 'M') - sage: X. = M.chart() sage: C = M.de_rham_complex() sage: H = C.cohomology() sage: H.has_coerce_map_from(QQ) True + :: + + sage: M = Manifold(4, 'M') + sage: C = M.de_rham_complex() + sage: H = C.cohomology() + sage: TM = M.tangent_bundle() + sage: C = TM.characteristic_cohomology_class_ring(); C + Algebra of characteristic cohomology classes of the Tangent bundle + TM over the 4-dimensional differentiable manifold M + sage: H.has_coerce_map_from(C) + True + """ if isinstance(other, CharacteristicCohomologyClassRing): return other._vbundle._base_space == self._manifold From 69dd5301343ea310bbdec34b65585a837c18be1b Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 21:23:42 +0200 Subject: [PATCH 30/50] Trac #39581: improve doc --- .../manifolds/differentiable/characteristic_cohomology_class.py | 2 +- src/sage/manifolds/differentiable/vector_bundle.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index e442f7eb6e5..f339067c521 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -342,7 +342,7 @@ def CharacteristicCohomologyClass(*args, **kwargs): characteristic cohomology class ring shall be defined - ``name`` -- (default: ``None``) string representation given to the characteristic cohomology class; if ``None`` the default algebra - representation is used + representation or predefined name is used - ``latex_name`` -- (default: ``None``) LaTeX name given to the characteristic class; if ``None`` the value of ``name`` is used - ``class_type`` -- (default: ``None``) class type of the characteristic diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py index ba9d362509b..e0a2f4ee440 100644 --- a/src/sage/manifolds/differentiable/vector_bundle.py +++ b/src/sage/manifolds/differentiable/vector_bundle.py @@ -219,7 +219,7 @@ def characteristic_cohomology_class(self, *args, **kwargs): characteristic cohomology class ring shall be defined - ``name`` -- (default: ``None``) string representation given to the characteristic cohomology class; if ``None`` the default algebra - representation is used + representation or predefined name is used - ``latex_name`` -- (default: ``None``) LaTeX name given to the characteristic class; if ``None`` the value of ``name`` is used - ``class_type`` -- (default: ``None``) class type of the characteristic From 00203e394d5afd7b61802a39dcf6cde13f73efb7 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 5 Sep 2021 21:28:07 +0200 Subject: [PATCH 31/50] Trac #29581: use cup-product for mul_symbol --- .../differentiable/characteristic_cohomology_class.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index f339067c521..6d0a51be774 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -213,7 +213,8 @@ def __init__(self, base, vbundle): names = tuple(names) # hashable degrees = tuple(degrees) # hashable super().__init__(base=base, names=names, degrees=degrees, - max_degree=dim) + max_degree=dim, mul_symbol='⌣', + mul_latex_symbol=r'\smile') def _element_constructor_(self, x, name=None, latex_name=None): r""" From 7555772e7b26039de4796997cd6f812a287ba49e Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 7 Sep 2021 19:38:20 +0200 Subject: [PATCH 32/50] Trac #29581: copy doc from old file + add some doc for CharacteristicCohomologyRing --- .../characteristic_cohomology_class.py | 355 ++++++++++++++++++ 1 file changed, 355 insertions(+) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 6d0a51be774..98bd53f103e 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1,5 +1,272 @@ r""" Characteristic cohomology classes + +A *characteristic class* `\kappa` is a natural transformation that +associates to each vector bundle `E \to M` a cohomology class +`\kappa(E) \in H^*(M;R)` such that for any continuous map `f\colon N \to M` +from another topological manifold `N`, the *naturality condition* is +satisfied: + +.. MATH:: + + f^*\kappa(E) = \kappa(f^* E) \in H^*(N;R) + +The cohomology class `\kappa(E)` is called *characteristic cohomology class*. +Roughly speaking, characteristic cohomology classes measure the non-triviality +of vector bundles. + +One way to obtain and compute characteristic classes in the de Rham cohomology +with coefficients in the ring `\CC` is via the so-called *Chern-Weil theory* +using the curvature of a differentiable vector bundle. + +For that let `\nabla` be a connection on `E`, `e` a local frame on +`E` and `\Omega` be the corresponding curvature matrix +(see: :meth:`~sage.manifolds.differentiable.bundle_connection.BundleConnection.curvature_form`). + +Namely, if `P: \mathrm{Mat}_{n \times n}(\CC) \to \CC` is an invariant +polynomial, the object + +.. MATH:: + + \left[ P \left( \Omega \right) \right] \in H^{2*}_{\mathrm{dR}}(M, \CC) + +is well-defined, independent of the choice of `\nabla` (the proof can be +found in [Roe1988]_ pp. 31) and fulfills the naturality condition. +This is the foundation of the Chern-Weil theory and therefore the following +definitions. + +.. NOTE:: + + This documentation is rich of examples, but sparse in explanations. Please + consult the references for more details. + +AUTHORS: + +- Michael Jung (2021) : initial version + +REFERENCES: + +- [Mil1974]_ +- [Roe1988]_ + +Contents +-------- + +We consider the following three types of classes: + +- :ref:`additive` +- :ref:`multiplicative` +- :ref:`Pfaffian` + +.. _additive: + +Additive Classes +---------------- + +In the **complex** case, let `f` be a holomorphic function around zero. Then +we call + +.. MATH:: + + \left[\mathrm{tr}\left( f\left( \frac{\Omega}{2 \pi i} \right) + \right)\right] \in H^{2*}_{\mathrm{dR}}(M, \CC) + +the *additive characteristic class associated to* `f` of the complex vector +bundle `E`. + +Important and predefined additive classes are: + +- *Chern Character* with `f(x) = \exp(x)` + +In the **real** case, let `g` be a holomorphic function around zero with +`g(0)=0`. Then we call + +.. MATH:: + + \left[\mathrm{tr}\left( \frac{1}{2} g\left( -\frac{\Omega^2}{4 \pi^2} + \right) \right)\right] \in H^{4*}_{\mathrm{dR}}(M, \CC) + +the *additive characteristic class associated to* `g` of the **real** vector +bundle `E`. + +EXAMPLES: + +Consider the **Chern character** on some 2-dimensional spacetime:: + + sage: M = Manifold(2, 'M', structure='Lorentzian') + sage: X. = M.chart() + sage: E = M.vector_bundle(1, 'E', field='complex'); E + Differentiable complex vector bundle E -> M of rank 1 over the base space + 2-dimensional Lorentzian manifold M + sage: e = E.local_frame('e') + +Let us define the connection `\nabla^E` in terms of an electro-magnetic +potential `A(t)`:: + + sage: nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E') + sage: omega = M.one_form(name='omega') + sage: A = function('A') + sage: nab.set_connection_form(0, 0)[1] = I*A(t) + sage: nab[0, 0].display() + connection (0,0) of bundle connection nabla^E w.r.t. Local frame + (E|_M, (e_0)) = I*A(t) dx + sage: nab.set_immutable() + +The Chern character is then given by:: + + sage: ch = E.characteristic_cohomology_class('ChernChar'); ch + Characteristic cohomology class ch(E) of the Differentiable complex vector + bundle E -> M of rank 1 over the base space 2-dimensional Lorentzian + manifold M + +The corresponding characteristic form w.r.t. the bundle connection can be +obtained via :meth:`get_form`. + + sage: ch_form = ch.get_form(nab); ch_form.display_expansion() + ch(E, nabla^E) = 1 + 1/2*d(A)/dt/pi dt∧dx + +.. _multiplicative: + +Multiplicative Classes +---------------------- + +In the **complex** case, let `f` be a holomorphic function around zero. +Then we call + +.. MATH:: + + \left[\det\left( f\left( \frac{\Omega}{2 \pi i} \right) + \right)\right] \in H^{2*}_{\mathrm{dR}}(M, \CC) + +the *multiplicative characteristic class associated to* `f` of the complex +vector bundle `E`. + +Important and predefined multiplicative classes on complex vector bundles are: + +- *Chern class* with `f(x) = 1+x` +- *Todd class* with `f(x) = \frac{x}{1-\exp(-x)}` + +In the **real** case, let `g` be a holomorphic function around zero with +`g(0)=1`. Then we call + +.. MATH:: + + \left[\det\left( \sqrt{ g \left( -\frac{\Omega^2}{4 \pi^2} \right) } \right) + \right] \in H^{4*}_{\mathrm{dR}}(M, \CC) + +the *multiplicative characteristic class associated to* `g` on the **real** +vector bundle `E`. + +Important and predefined multiplicative classes on real vector bundles are: + +- *Pontryagin class* with `g(x) = 1+x` +- `\hat{A}` *class* with `g(x) = \frac{\sqrt{x}/2}{\sinh(\sqrt{x}/2)}` +- *Hirzebruch class* with `g(x) = \frac{\sqrt{x}}{\tanh(\sqrt{x})}` + +EXAMPLES: + +We consider the **Chern class** of the tautological line bundle `\gamma^1` over +`\CC\mathbf{P}^1`:: + + sage: M = Manifold(2, 'CP^1', start_index=1) + sage: U = M.open_subset('U') + sage: c_cart. = U.chart() # homogeneous coordinates in real terms + sage: c_comp. = U.chart(r'z:z zbar:\bar{z}') # complexification + sage: cart_to_comp = c_cart.transition_map(c_comp, (x+I*y, x-I*y)) + sage: comp_to_cart = cart_to_comp.inverse() + sage: E = M.vector_bundle(1, 'gamma^1', field='complex') + sage: e = E.local_frame('e', domain=U) + +To apply the Chern-Weil approach, we need a bundle connection in terms of a +connection one form. To achieve this, we take the connection induced from the +hermitian metric on the trivial bundle +`\CC^2 \times \CC\mathbf{P}^1 \supset \gamma^1`. In this the frame `e` +corresponds to the section `[z:1] \mapsto (z,1)` and its magnitude-squared +is given by `1+|z|^2`:: + + sage: nab = E.bundle_connection('nabla') + sage: omega = U.one_form(name='omega') + sage: omega[c_comp.frame(),1,c_comp] = zbar/(1+z*zbar) + sage: nab[e, 1, 1] = omega + sage: nab.set_immutable() + +Now, the Chern class can be constructed:: + + sage: c = E.characteristic_cohomology_class('Chern'); c + Characteristic cohomology class c(gamma^1) of the Differentiable complex + vector bundle gamma^1 -> CP^1 of rank 1 over the base space 2-dimensional + differentiable manifold CP^1 + sage: c_form = c.get_form(nab) + sage: c_form.display_expansion(c_comp.frame(), chart=c_comp) + c(gamma^1, nabla) = 1 + 1/2*I/(pi + pi*z^2*zbar^2 + 2*pi*z*zbar) dz∧dzbar + +Since `U` and `\CC\mathbf{P}^1` differ only by a point and therefore a null +set, it is enough to integrate the top form over the domain `U`:: + + sage: integrate(integrate(c_form[2][[1,2]].expr(c_cart), x, -infinity, infinity).full_simplify(), + ....: y, -infinity, infinity) + 1 + +The result shows that `c_1(\gamma^1)` generates the second integer +cohomology of `\CC\mathbf{P}^1`. + +.. _Pfaffian: + +Pfaffian Classes +---------------- + +Usually, there is no such thing as "Pfaffian classes" in literature. However, +using the matrix' Pfaffian and inspired by the aforementioned definitions, +such classes can be defined as follows. + +Let `E` be a real vector bundle of rank `2n` and `f` an odd real function +being analytic at zero. Furthermore, let `\Omega` be skew-symmetric, which +certainly will be true if `\nabla` is metric and `e` is orthonormal. Then +we call + +.. MATH:: + + \left[\mathrm{Pf}\left( f\left( \frac{\Omega}{2 \pi} \right) \right)\right] + \in H^{2n*}(M,\RR) + +the *Pfaffian class associated to f*. + +The most important Pfaffian class is the *Euler class* which is simply given by +`f(x)=x`. + +EXAMPLES: + +We consider the **Euler class** of `S^2`:: + + sage: M. = manifolds.Sphere(2, coordinates='stereographic') + sage: TM = M.tangent_bundle() + sage: e_class = TM.characteristic_cohomology_class('Euler'); e_class + Characteristic cohomology class e(TS^2) of the Tangent bundle TS^2 over the + 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3 + +To compute a particular representative of the Euler class, we need to determine +a connection, which is in this case given by the standard metric:: + + sage: g = M.metric('g') # standard metric on S2 + sage: nab = g.connection() + sage: nab.set_immutable() + +Now the representative of the Euler class with respect to the connection +`\nabla_g` induced by the standard metric can be computed:: + + sage: e_class_form = e_class.get_form(nab) + sage: e_class_form.display_expansion() + e(TS^2, nabla_g) = 2/(pi + pi*x^4 + pi*y^4 + 2*pi*x^2 + 2*(pi + pi*x^2)*y^2) dx∧dy + +Let us check whether this form represents the Euler class correctly:: + + sage: integrate(integrate(e_class_form[2][[1,2]].expr(), x, -infinity, infinity).simplify_full(), + ....: y, -infinity, infinity) + 2 + +As we can see, the integral coincides with the Euler characteristic of `S^2` so +that our form actually represents the Euler class appropriately. + """ #****************************************************************************** @@ -173,7 +440,95 @@ def set_name(self, name=None, latex_name=None): class CharacteristicCohomologyClassRing(FiniteGCAlgebra): r""" + Characteristic cohomology class ring. + + Let `E \to M` be a real or complex vector bundle of rank `k` and `R` be a + torsion-free subring of `\CC`. + + Let `BG` be the classifying space of the group `G`. As for vector bundles, + we consider + + - `G = O(k)` if `E` is real, + - `G = SO(k)` if `E` is real and oriented, + - `G = U(k)` if `E` is complex. + + The cohomology ring `H^*(BG; R)` can be explicitly expressed for the + aforementioned cases: + + .. MATH:: + + H^*(BG; R) \cong \begin{cases} + R[c_1, \ldots c_k] & \text{if } G = U(k), \\ + R[p_1, \ldots p_{\lfloor \frac{k}{2}\rfloor}] & \text{if } G = O(k), \\ + R[p_1, \ldots p_k, e] \big/ (p_k^2-e) & \text{if } G = SO(2k), \\ + R[p_1, \ldots p_k, e] & \text{if } G = O(2k+1). \\ + \end{cases} + + The Chern-Weil homomorphism relates the generators in the de Rham cohomology + as follows. For the Chern classes, we have + + .. MATH:: + + \left[ \det\left( 1 + \frac{t \Omega}{2 \pi i} \right) = 1 + + \sum^k_{n=1} c_n(E) t^n, + + for the Pontryagin classes we have + + .. MATH:: + + \left[ \det\left( 1 - \frac{t \Omega}{2 \pi} \right) = 1 + \sum^{ + \lfloor\frac{k}{2} \rfloor}_{n=1} p_n(E) t^n, + + and for the Euler class we obtain + + .. MATH:: + + \left[ \mathrm{Pf}\left(\frac{\Omega}{2 \pi} \right) = e(E). + + Consequently, the cohomology `H^*(BG; R)` can be considered being a + subring of `H_\mathrm{dR}(M, \CC)`. + + INPUT: + - ``base`` -- base ring + - ``vbundle`` -- vector bundle + + EXAMPLES: + + Characteristic cohomology class ring over the tangent bundle of an + 8-dimensional manifold:: + + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: CR = TM.characteristic_cohomology_class_ring(); CR + Algebra of characteristic cohomology classes of the Tangent bundle TM + over the 8-dimensional differentiable manifold M + sage: CR.gens() + [Characteristic cohomology class (p_1)(TM) of the Tangent bundle TM over + the 8-dimensional differentiable manifold M, + Characteristic cohomology class (p_2)(TM) of the Tangent bundle TM + over the 8-dimensional differentiable manifold M] + + The default base ring is `\QQ`:: + + sage: CR.base_ring() + Rational Field + + Characteristic cohomology class ring over a complex vector bundle:: + + sage: M = Manifold(4, 'M') + sage: E = M.vector_bundle(2, 'E', field='complex') + sage: CR_E = E.characteristic_cohomology_class_ring(); CR_E + Algebra of characteristic cohomology classes of the Differentiable + complex vector bundle E -> M of rank 2 over the base space + 4-dimensional differentiable manifold M + sage: CR_E.gens() + [Characteristic cohomology class (c_1)(E) of the Differentiable complex + vector bundle E -> M of rank 2 over the base space 4-dimensional + differentiable manifold M, + Characteristic cohomology class (c_2)(E) of the Differentiable + complex vector bundle E -> M of rank 2 over the base space + 4-dimensional differentiable manifold M] """ Element = CharacteristicCohomologyClassRingElement From 5f542e15b88799d5e37df351d32a6389e237e913 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 7 Sep 2021 19:47:11 +0200 Subject: [PATCH 33/50] Trac #29581: improve doc CharacteristicCohomologyClassRing --- .../characteristic_cohomology_class.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 98bd53f103e..e1f7dfcd9c5 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -120,7 +120,7 @@ manifold M The corresponding characteristic form w.r.t. the bundle connection can be -obtained via :meth:`get_form`. +obtained via :meth:`get_form`:: sage: ch_form = ch.get_form(nab); ch_form.display_expansion() ch(E, nabla^E) = 1 + 1/2*d(A)/dt/pi dt∧dx @@ -461,32 +461,34 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): R[c_1, \ldots c_k] & \text{if } G = U(k), \\ R[p_1, \ldots p_{\lfloor \frac{k}{2}\rfloor}] & \text{if } G = O(k), \\ R[p_1, \ldots p_k, e] \big/ (p_k^2-e) & \text{if } G = SO(2k), \\ - R[p_1, \ldots p_k, e] & \text{if } G = O(2k+1). \\ + R[p_1, \ldots p_k, e] & \text{if } G = SO(2k+1). \\ \end{cases} The Chern-Weil homomorphism relates the generators in the de Rham cohomology - as follows. For the Chern classes, we have + as follows. If `\Omega` is a curvature form matrix on `E`, for the Chern + classes + we get .. MATH:: - \left[ \det\left( 1 + \frac{t \Omega}{2 \pi i} \right) = 1 + + \left[ \det\left( 1 + \frac{t \Omega}{2 \pi i} \right) \right] = 1 + \sum^k_{n=1} c_n(E) t^n, for the Pontryagin classes we have .. MATH:: - \left[ \det\left( 1 - \frac{t \Omega}{2 \pi} \right) = 1 + \sum^{ - \lfloor\frac{k}{2} \rfloor}_{n=1} p_n(E) t^n, + \left[ \det\left( 1 - \frac{t \Omega}{2 \pi} \right) \right] = 1 + + \sum^{\lfloor\frac{k}{2} \rfloor}_{n=1} p_n(E) t^n, and for the Euler class we obtain .. MATH:: - \left[ \mathrm{Pf}\left(\frac{\Omega}{2 \pi} \right) = e(E). + \left[ \mathrm{Pf}\left(\frac{\Omega}{2 \pi} \right) \right] = e(E). - Consequently, the cohomology `H^*(BG; R)` can be considered being a - subring of `H_\mathrm{dR}(M, \CC)`. + Consequently, the cohomology ring `H^*(BG; R)` can be considered being a + subring of `H^*_\mathrm{dR}(M, \CC)`. INPUT: From 0ca6eb9fac729f9cfe68f61a5a41bf8e3d1f6504 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 8 Sep 2021 14:20:09 +0200 Subject: [PATCH 34/50] Trac #29581: add doc to sequences --- .../characteristic_cohomology_class.py | 116 ++++++++++++++---- 1 file changed, 95 insertions(+), 21 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index e1f7dfcd9c5..cd14de47586 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -487,8 +487,10 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): \left[ \mathrm{Pf}\left(\frac{\Omega}{2 \pi} \right) \right] = e(E). - Consequently, the cohomology ring `H^*(BG; R)` can be considered being a - subring of `H^*_\mathrm{dR}(M, \CC)`. + Consequently, the cohomology ring `H^*(BG; R)` is mapped (not + necessarily injectively) to a subring of `H^*_\mathrm{dR}(M, \CC)` via + the Chern-Weil homomorphism. This implementation attempts to represent this + subring. INPUT: @@ -536,7 +538,14 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): def __init__(self, base, vbundle): r""" + Construct a characteristic cohomology ring. + TESTS:: + + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: CR = TM.characteristic_cohomology_class_ring() + sage: TestSuite(CR).run() """ self._vbundle = vbundle self._domain = vbundle._base_space @@ -576,6 +585,10 @@ def __init__(self, base, vbundle): def _element_constructor_(self, x, name=None, latex_name=None): r""" Convert ``x`` into ``self``. + + TESTS:: + + """ R = self.base_ring() @@ -601,7 +614,19 @@ def _element_constructor_(self, x, name=None, latex_name=None): def _repr_(self): r""" - + String representation of the object. + + TESTS:: + + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: CR = TM.characteristic_cohomology_class_ring() + sage: CR._repr_() + 'Algebra of characteristic cohomology classes of the Tangent bundle + TM over the 8-dimensional differentiable manifold M' + sage: CR # indirect doctest + Algebra of characteristic cohomology classes of the Tangent bundle + TM over the 8-dimensional differentiable manifold M """ vbundle = self._vbundle repr = f'Algebra of characteristic cohomology classes of the {vbundle}' @@ -611,26 +636,51 @@ def _repr_(self): # ALGORITHMS # ***************************************************************************** -def multiplicative_sequence(q, max_order=None): +def multiplicative_sequence(q, n=None): r""" Turn the polynomial ``q`` into its multiplicative sequence. + Let `q` be a polynomial and `x_1, \ldots x_n` indeterminates. The + *multiplicative sequence of* `q` is then given by the polynomials `K_j` + + .. MATH:: + + \sum_{j=0}^n K_j(\sigma_1, \ldots, \sigma_j) z^j = + \prod_{i=1}^{n} q(z \,x_i), + + where `\sigma_i` is the `i`-th elementary symmetric polynomial in the + indeterminates `x_i`. + INPUT: - ``q`` -- polynomial to turn into its multiplicative sequence. - - ``max_order`` -- (default: ``None``) the highest order of the sequence; + - ``n`` -- (default: ``None``) the highest order `n` of the sequence; if ``None``, the order of ``q`` is assumed. OUTPUT: - A symmetric polynomial representing the multiplicative sequence. + + EXAMPLES:: + + sage: P. = PolynomialRing(QQ) + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import multiplicative_sequence + sage: f = 1 + x - x^2 + sage: sym = multiplicative_sequence(f); sym + e[] + e[1] - e[1, 1] + 3*e[2] + + The maximal order of the result can be stated with ``n``:: + + sage: sym_5 = multiplicative_sequence(f, n=5); sym_5 + e[] + e[1] - e[1, 1] + 3*e[2] - e[2, 1] + e[2, 2] + 4*e[3] - 3*e[3, 1] + + e[3, 2] + 7*e[4] - 4*e[4, 1] + 11*e[5] """ from sage.combinat.sf.sf import SymmetricFunctions from sage.combinat.partition import Partitions from sage.misc.misc_c import prod - if max_order is None: - max_order = q.degree() + if n is None: + n = q.degree() R = q.parent().base_ring() Sym = SymmetricFunctions(R) @@ -638,30 +688,54 @@ def multiplicative_sequence(q, max_order=None): # Get the multiplicative sequence in the monomial basis: mon_pol = m._from_dict({p: prod(q[i] for i in p) - for k in range(max_order + 1) + for k in range(n + 1) for p in Partitions(k)}) return Sym.e()(mon_pol) -def additive_sequence(q, rk, max_order=None): +def additive_sequence(q, k, n=None): r""" Turn the polynomial ``q`` into its additive sequence. + Let `q` be a polynomial and `x_1, \ldots x_n` indeterminates. The + *additive sequence of* `q` is then given by the polynomials `Q_j` + + .. MATH:: + + \sum_{j=0}^n Q_j(\sigma_1, \ldots, \sigma_j) z^j = + \sum_{i=1}^{k} q(z \,x_i), + + where `\sigma_i` is the `i`-th elementary symmetric polynomial in the + indeterminates `x_i`. + INPUT: - ``q`` -- polynomial to turn into its additive sequence. - - ``rk`` -- rank of the underlying vector bundle - - ``max_order`` -- (default: ``None``) the highest order of the sequence; + - ``k`` -- maximal index `k` of the sum + - ``n`` -- (default: ``None``) the highest order of the sequence `n`; if ``None``, the order of ``q`` is assumed. OUTPUT: - A symmetric polynomial representing the additive sequence. + + EXAMPLES:: + + sage: P. = PolynomialRing(QQ) + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import additive_sequence + sage: f = 1 + x - x^2 + sage: sym = additive_sequence(f, 2); sym + 2*e[] + e[1] - e[1, 1] + 2*e[2] + + The maximal order of the result can be stated with ``n``:: + + sage: sym_1 = additive_sequence(f, 2, 1); sym_1 + 2*e[] + e[1] """ from sage.combinat.sf.sf import SymmetricFunctions from sage.combinat.partition import Partitions - if max_order is None: - max_order = q.degree() + if n is None: + n = q.degree() R = q.parent().base_ring() Sym = SymmetricFunctions(R) @@ -669,8 +743,8 @@ def additive_sequence(q, rk, max_order=None): # Express the additive sequence in the monomial basis, the 0-th # order term must be treated separately; here comes ``rk`` into play: - m_dict = {Partitions(0)([]): rk * q[0]} - m_dict.update({Partitions(k)([k]): q[k] for k in range(1, max_order + 1)}) + m_dict = {Partitions(0)([]): k * q[0]} + m_dict.update({Partitions(k)([k]): q[k] for k in range(1, n + 1)}) mon_pol = m._from_dict(m_dict) return Sym.e()(mon_pol) @@ -818,22 +892,22 @@ def CharacteristicCohomologyClass(*args, **kwargs): if class_type is None: raise TypeError(f'class_type must be stated if {val} ' f'is a polynomial') - max_order = R.ngens() + n = R.ngens() s = 0 # shift; important in case of Euler class generator if R._algorithm is PontryaginEulerAlgorithm(): s = 1 # skip Euler class - max_order -= 1 # ignore Euler class + n -= 1 # ignore Euler class if class_type == 'additive': - sym = additive_sequence(val, vbundle._rank, max_order=max_order) + sym = additive_sequence(val, vbundle._rank, n) elif class_type == 'multiplicative': - sym = multiplicative_sequence(val, max_order=max_order) + sym = multiplicative_sequence(val, n) elif class_type == 'Pfaffian': P = val.parent() x = P.gen() val = (val(x) - val(-x)) / 2 # project to odd functions - val = P([(-1)**k * val[2*k+1] for k in range(max_order + 1)]) - sym = multiplicative_sequence(val, max_order=max_order) + val = P([(-1)**k * val[2*k+1] for k in range(n + 1)]) + sym = multiplicative_sequence(val, n) else: AttributeError('unkown class type') From e7169f81d27242663c168350b203c2d8d51861ef Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 8 Sep 2021 14:46:37 +0200 Subject: [PATCH 35/50] Trac #29581: doc of fast_wedge_power --- .../characteristic_cohomology_class.py | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index cd14de47586..021c6c5ab97 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -756,7 +756,9 @@ def CharacteristicCohomologyClass(*args, **kwargs): INPUT: - ``vbundle`` -- the vector bundle over which the characteristic - cohomology class shall be defined + cohomology class shall be defined; this argument is skipped when invoked + directly from the vector bundle (see + :meth:`sage.manifolds.differentiable.vector_bundle.characteristic_cohomology_class`) - ``val`` -- the input data corresponding to the characteristic class using the Chern-Weil homomorphism; this argument can be either a symbolic expression, a polynomial or one of the following predefined @@ -787,6 +789,33 @@ def CharacteristicCohomologyClass(*args, **kwargs): This argument must be stated if ``val`` is a polynomial or symbolic expression. + EXAMPLES: + + Total Pontryagin class of an 8-dimensional manifold:: + + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: p = TM.characteristic_cohomology_class('Pontryagin'); p + Characteristic cohomology class p(TM) of the Tangent bundle TM over the + 8-dimensional differentiable manifold M + + Define a multiplicative class (see :func:`multiplicative_sequence`):: + + sage: P. = PolynomialRing(QQ) + sage: f = 1 + x - x^2 + sage: f_class = TM.characteristic_cohomology_class(f, class_type='multiplicative'); f_class + Characteristic cohomology class (1 + p_1 - p_1^2 + 3*p_2)(TM) of the + Tangent bundle TM over the 8-dimensional differentiable manifold M + + Pass a symbolic expression, whose Taylor expansion at zero will be used:: + + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: x = var('x') + sage: f = cos(x) + sage: f_class = TM.characteristic_cohomology_class(f, class_type='multiplicative'); f_class + Characteristic cohomology class (1 - 1/2*p_1^2 + p_2)(TM) of the Tangent + bundle TM over the 8-dimensional differentiable manifold M """ from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -933,6 +962,26 @@ def fast_wedge_power(form, n): r""" Return the wedge product power of `form` using a square-and-wedge algorithm. + + INPUT: + + - ``form`` -- a differential form + - ``n`` -- a non-negative integer + + EXAMPLES:: + + sage: M = Manifold(4, 'M') + sage: X. = M.chart() + sage: omega = M.diff_form(2, name='omega') + sage: omega[0,1] = t*y^2 + 2*x + sage: omega[0,3] = z - 2*y + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import fast_wedge_power + sage: fast_wedge_power(omega, 0) + Scalar field 1 on the 4-dimensional differentiable manifold M + sage: fast_wedge_power(omega, 1) + 4-form omega on the 4-dimensional differentiable manifold M + sage: fast_wedge_power(omega, 2) + 4-form omega∧omega on the 4-dimensional differentiable manifold M """ if n == 0: return form._domain._one_scalar_field @@ -940,7 +989,6 @@ def fast_wedge_power(form, n): raise ValueError("'n' must be non-negative") val = form while not (n & 1): - print(n) val = val.wedge(val) n >>= 1 @@ -959,6 +1007,8 @@ def fast_wedge_power(form, n): class Algorithm_generic(SageObject): r""" Algorithm class to compute the characteristic forms of the generators. + + This is an abstract class and only meant for developers. """ @cached_method @@ -971,6 +1021,9 @@ def get(self, nab): - a list containing the generator's global characteristic forms + ALGORITHM: + + """ if isinstance(nab, AffineConnection): vbundle = nab._domain.tangent_bundle() From 114b483325526810c504935048e87b1e813985df Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 9 Sep 2021 14:17:11 +0200 Subject: [PATCH 36/50] Trac #29581: add more docstring --- .../characteristic_cohomology_class.py | 208 +++++++++++++++--- 1 file changed, 182 insertions(+), 26 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 021c6c5ab97..c8beb749d7d 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -32,8 +32,7 @@ is well-defined, independent of the choice of `\nabla` (the proof can be found in [Roe1988]_ pp. 31) and fulfills the naturality condition. -This is the foundation of the Chern-Weil theory and therefore the following -definitions. +This is the foundation of the Chern-Weil theory and the following definitions. .. NOTE:: @@ -292,12 +291,19 @@ class CharacteristicCohomologyClassRingElement(IndexedFreeModuleElement): r""" + Characteristic cohomology class. """ - def __init__(self, parent, x, name=None, latex_name=None): r""" + Construct a characteristic cohomology class. + + TESTS:: + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: p = TM.characteristic_cohomology_class('Pontryagin') + sage: TestSuite(p).run() """ self._name = name if latex_name is None: @@ -310,7 +316,30 @@ def __init__(self, parent, x, name=None, latex_name=None): def _repr_(self): r""" + String representation of the object. + + TESTS:: + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: p = TM.characteristic_cohomology_class('Pontryagin') + sage: p._repr_() + 'Characteristic cohomology class p(TM) of the Tangent bundle TM + over the 8-dimensional differentiable manifold M' + sage: p # indirect doctest + Characteristic cohomology class p(TM) of the Tangent bundle TM over + the 8-dimensional differentiable manifold M + + :: + + sage: x = var('x') + sage: k = TM.characteristic_cohomology_class(1+x^2, class_type='multiplicative') + sage: k._repr_() + 'Characteristic cohomology class (1 + p_1^2 - 2*p_2)(TM) of the + Tangent bundle TM over the 8-dimensional differentiable manifold M' + sage: k # indirect doctest + Characteristic cohomology class (1 + p_1^2 - 2*p_2)(TM) of the + Tangent bundle TM over the 8-dimensional differentiable manifold M """ if self._name is None: name = f'({super()._repr_()})' @@ -322,19 +351,77 @@ def _repr_(self): def _latex_(self): r""" + LaTeX representation of the object. + + TESTS:: + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: p = TM.characteristic_cohomology_class('Pontryagin') + sage: p._latex_() + 'p\\left(TM\\right)' + sage: latex(p) # indirect doctest + p\left(TM\right) + + :: + + sage: x = var('x') + sage: k = TM.characteristic_cohomology_class(1+x^2, class_type='multiplicative') + sage: k._latex_() + '\\left(1 + p_1^{2} - 2p_2\\right)\\left(TM\\right)' + sage: latex(k) + \left(1 + p_1^{2} - 2p_2\right)\left(TM\right) """ if self._latex_name is None: latex = r'\left(' + super()._latex_() + r'\right)' else: latex = self._latex_name vbundle = self.parent()._vbundle - latex += latex + r'\left(' + vbundle._latex_name + r'\right)' + latex += r'\left(' + vbundle._latex_name + r'\right)' return latex def get_form(self, nab): r""" + Return the characteristic form of ``self``. + + INPUT: + + - ``nab`` -- get the characteristic form w.r.t. to the + connection ``nab`` + OUTPUT: + + - an instance of `sage.manifolds.differentiable.mixed_form.MixedForm` + + EXAMPLES: + + Trivial characteristic form on Euclidean space:: + + sage: M = manifolds.EuclideanSpace(4) + sage: TM = M.tangent_bundle() + sage: one = TM.characteristic_cohomology_class_ring().one() + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + sage: one.get_form(nab) + Mixed differential form one on the 4-dimensional Euclidean space E^4 + + Pontryagin form on the 4-sphere:: + + sage: M = manifolds.Sphere(4) + sage: TM = M.tangent_bundle() + sage: p = TM.characteristic_cohomology_class('Pontryagin'); p + Characteristic cohomology class p(TS^4) of the Tangent bundle TS^4 + over the 4-sphere S^4 of radius 1 smoothly embedded in the + 5-dimensional Euclidean space E^5 + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + sage: p_form = p.get_form(nab); p_form + Mixed differential form p(TS^4, nabla_g) on the 4-sphere S^4 of + radius 1 smoothly embedded in the 5-dimensional Euclidean space E^5 + sage: p_form.display_expansion() + p(TS^4, nabla_g) = 1 """ if nab not in self._mixed_forms: dom = nab._domain @@ -407,8 +494,49 @@ def representative(self, nab=None): - ``nab`` -- (default: ``None``) if stated, return the representative w.r.t. to the connection ``nab``; otherwise an arbitrary already - computed representative will be chosen + computed representative will be chosen. + + OUTPUT: + + - an instance of `sage.manifolds.differentiable.mixed_form.MixedForm` + + EXAMPLES: + + Define the 4-dimensional Euclidean space:: + + sage: M = manifolds.EuclideanSpace(4) + sage: TM = M.tangent_bundle() + sage: one = TM.characteristic_cohomology_class_ring().one() + No characteristic form has been computed so far, thus we get an error:: + + sage: one.representative() + Traceback (most recent call last): + ... + AttributeError: cannot pick a representative + + Get a characteristic form:: + + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + sage: one.get_form(nab) + Mixed differential form one on the 4-dimensional Euclidean space E^4 + + Now, the result is cached and `representative` returns a form:: + + sage: one.representative() + Mixed differential form one on the 4-dimensional Euclidean space E^4 + + Alternatively, the option ``nab`` can be used to return the + characteristic form w.r.t. a fixed connection:: + + sage: one.representative(nab) + Mixed differential form one on the 4-dimensional Euclidean space E^4 + + .. SEEALSO:: + + :meth:`CharacteristicCohomologyClassRingElement.get_form` """ if nab is None: if not self._mixed_forms: @@ -419,7 +547,7 @@ def representative(self, nab=None): def set_name(self, name=None, latex_name=None): r""" Set (or change) the text name and LaTeX name of the characteristic - cohomology class. + class. INPUT: @@ -429,6 +557,21 @@ def set_name(self, name=None, latex_name=None): the characteristic cohomology class; if ``None`` while ``name`` is provided, the LaTeX symbol is set to ``name`` + EXAMPLES:: + + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: x = var('x') + sage: k = TM.characteristic_cohomology_class(1+x^2, + ....: class_type='multiplicative'); k + Characteristic cohomology class (1 + p_1^2 - 2*p_2)(TM) of the + Tangent bundle TM over the 8-dimensional differentiable manifold M + sage: k.set_name(name='k', latex_name=r'\kappa') + sage: k + Characteristic cohomology class k(TM) of the Tangent bundle TM over + the 8-dimensional differentiable manifold M + sage: latex(k) + \kappa\left(TM\right) """ if name is not None: self._name = name @@ -464,10 +607,9 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): R[p_1, \ldots p_k, e] & \text{if } G = SO(2k+1). \\ \end{cases} - The Chern-Weil homomorphism relates the generators in the de Rham cohomology - as follows. If `\Omega` is a curvature form matrix on `E`, for the Chern - classes - we get + The Chern-Weil homomorphism relates the generators in the de Rham + cohomology as follows. If `\Omega` is a curvature form matrix on `E`, for + the Chern classes we get .. MATH:: @@ -533,6 +675,18 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): Characteristic cohomology class (c_2)(E) of the Differentiable complex vector bundle E -> M of rank 2 over the base space 4-dimensional differentiable manifold M] + + Characteristic cohomology class ring over an oriented manifold:: + + sage: S2 = manifolds.Sphere(2, coordinates='stereographic') + sage: TS2 = S2.tangent_bundle() + sage: S2.has_orientation() + True + sage: CR = TS2.characteristic_cohomology_class_ring() + sage: CR.gens() + [Characteristic cohomology class (e)(TS^2) of the Tangent bundle TS^2 + over the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean + space E^3] """ Element = CharacteristicCohomologyClassRingElement @@ -588,7 +742,13 @@ def _element_constructor_(self, x, name=None, latex_name=None): TESTS:: - + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: CR = TM.characteristic_cohomology_class_ring() + sage: p = TM.characteristic_cohomology_class('Pontryagin') + sage: CR(p, name='pontr') + Characteristic cohomology class pontr(TM) of the Tangent bundle + TM over the 8-dimensional differentiable manifold M """ R = self.base_ring() @@ -753,6 +913,8 @@ def CharacteristicCohomologyClass(*args, **kwargs): r""" Construct a characteristic cohomology class. + The result is cached. + INPUT: - ``vbundle`` -- the vector bundle over which the characteristic @@ -775,8 +937,8 @@ def CharacteristicCohomologyClass(*args, **kwargs): - ``base_ring`` -- (default: ``QQ``) base ring over which the characteristic cohomology class ring shall be defined - ``name`` -- (default: ``None``) string representation given to the - characteristic cohomology class; if ``None`` the default algebra - representation or predefined name is used + characteristic class; if ``None`` the default algebra representation or + predefined name is used - ``latex_name`` -- (default: ``None``) LaTeX name given to the characteristic class; if ``None`` the value of ``name`` is used - ``class_type`` -- (default: ``None``) class type of the characteristic @@ -979,7 +1141,7 @@ def fast_wedge_power(form, n): sage: fast_wedge_power(omega, 0) Scalar field 1 on the 4-dimensional differentiable manifold M sage: fast_wedge_power(omega, 1) - 4-form omega on the 4-dimensional differentiable manifold M + 2-form omega on the 4-dimensional differentiable manifold M sage: fast_wedge_power(omega, 2) 4-form omega∧omega on the 4-dimensional differentiable manifold M """ @@ -1006,24 +1168,20 @@ def fast_wedge_power(form, n): class Algorithm_generic(SageObject): r""" - Algorithm class to compute the characteristic forms of the generators. - - This is an abstract class and only meant for developers. + Abstract algorithm class to compute the characteristic forms of the + generators. """ - @cached_method def get(self, nab): r""" Return the global characteristic forms of the generators w.r.t. a given connection. + The result is cached. + OUTPUT: - a list containing the generator's global characteristic forms - - ALGORITHM: - - """ if isinstance(nab, AffineConnection): vbundle = nab._domain.tangent_bundle() @@ -1055,7 +1213,6 @@ def get_local(self, cmat): OUTPUT: - a list containing the generator's local characteristic forms - """ pass @@ -1064,6 +1221,8 @@ def get_gen_pow(self, nab, i, n): r""" Return the `n`-th power of the `i`-th generator's characteristic form w.r.t ``nab``. + + The result is cached. """ if n == 0: return nab._domain._one_scalar_field # no computation necessary @@ -1074,7 +1233,6 @@ class ChernAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Chern forms. """ - def get_local(self, cmat): r""" Return the local Chern forms w.r.t. a given curvature matrix. @@ -1116,7 +1274,6 @@ class PontryaginAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Pontryagin forms. """ - def get_local(self, cmat): r""" Return the local Pontryagin forms w.r.t. a given curvature matrix. @@ -1159,7 +1316,6 @@ class EulerAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Euler forms. """ - @cached_method def get(self, nab): r""" From bafd78dcbb8e5b385051d64cc0a68a2e14908ad6 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 9 Sep 2021 14:48:49 +0200 Subject: [PATCH 37/50] Trac #29581: remove old file + add more doc --- .../differentiable/characteristic_class.py | 962 ------------------ .../characteristic_cohomology_class.py | 44 +- 2 files changed, 37 insertions(+), 969 deletions(-) delete mode 100644 src/sage/manifolds/differentiable/characteristic_class.py diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py deleted file mode 100644 index 547ca44260d..00000000000 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ /dev/null @@ -1,962 +0,0 @@ -r""" -Characteristic Classes - -A *characteristic class* `\kappa` is a natural transformation that -associates to each vector bundle `E \to M` a cohomology class -`\kappa(E) \in H^*(M;R)` such that for any continuous map `f\colon N \to M` -from another topological manifold `N`, the *naturality condition* is -satisfied: - -.. MATH:: - - f^*\kappa(E) = \kappa(f^* E) \in H^*(N;R) - -Roughly speaking, characteristic classes measure the non-triviality of -vector bundles. - -One way to obtain and compute characteristic classes in the de Rham cohomology -with coefficients in the ring `\CC` is via the so-called *Chern-Weil theory* -using the curvature of a differentiable vector bundle. - -For that let `\nabla` be a connection on `E`, `e` a local frame on -`E` and `\Omega` be the corresponding curvature matrix -(see: :meth:`~sage.manifolds.differentiable.bundle_connection.BundleConnection.curvature_form`). - -Namely, if `P: \mathrm{Mat}_{n \times n}(\CC) \to \CC` is an invariant -polynomial, the object - -.. MATH:: - - \left[ P \left( \Omega \right) \right] \in H^{2*}_{\mathrm{dR}}(M, \CC) - -is well-defined, independent of the choice of `\nabla` (the proof can be -found in [Roe1988]_ pp. 31) and fulfills the naturality condition. -This is the foundation of the Chern-Weil theory and therefore the following -definitions. - -.. NOTE:: - - This documentation is rich of examples, but sparse in explanations. Please - consult the references for more details. - -AUTHORS: - -- Michael Jung (2019) : initial version - -REFERENCES: - -- [Mil1974]_ -- [Roe1988]_ - -Contents --------- - -We consider the following three types of classes: - -- :ref:`additive` -- :ref:`multiplicative` -- :ref:`Pfaffian` - -.. _additive: - -Additive Classes ----------------- - -In the **complex** case, let `f` be a holomorphic function around zero. Then -we call - -.. MATH:: - - \left[\mathrm{tr}\left( f\left( \frac{\Omega}{2 \pi i} \right) - \right)\right] \in H^{2*}_{\mathrm{dR}}(M, \CC) - -the *additive characteristic class associated to* `f` of the complex vector -bundle `E`. - -Important and predefined additive classes are: - -- *Chern Character* with `f(x) = \exp(x)` - -In the **real** case, let `g` be a holomorphic function around zero with -`g(0)=0`. Then we call - -.. MATH:: - - \left[\mathrm{tr}\left( \frac{1}{2} g\left( -\frac{\Omega^2}{4 \pi^2} - \right) \right)\right] \in H^{4*}_{\mathrm{dR}}(M, \CC) - -the *additive characteristic class associated to* `g` of the **real** vector -bundle `E`. - -EXAMPLES: - -Consider the **Chern character** on some 2-dimensional spacetime:: - - sage: M = Manifold(2, 'M', structure='Lorentzian') - sage: X. = M.chart() - sage: E = M.vector_bundle(1, 'E', field='complex'); E - Differentiable complex vector bundle E -> M of rank 1 over the base space - 2-dimensional Lorentzian manifold M - sage: e = E.local_frame('e') - -Let us define the connection `\nabla^E` in terms of an electro-magnetic -potential `A(t)`:: - - sage: nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E') - sage: omega = M.one_form(name='omega') - sage: A = function('A') - sage: nab.set_connection_form(0, 0)[1] = I*A(t) - sage: nab[0, 0].display() - connection (0,0) of bundle connection nabla^E w.r.t. Local frame - (E|_M, (e_0)) = I*A(t) dx - sage: nab.set_immutable() - -The Chern character is then given by:: - - sage: ch = E.characteristic_class('ChernChar'); ch - Characteristic class ch of additive type associated to e^x on the - Differentiable complex vector bundle E -> M of rank 1 over the base space - 2-dimensional Lorentzian manifold M - -The corresponding characteristic form w.r.t. the bundle connection can be -obtained via :meth:`get_form`. - - sage: ch_form = ch.get_form(nab); ch_form.display_expansion() - ch(E, nabla^E) = 1 + 1/2*d(A)/dt/pi dt∧dx - -.. _multiplicative: - -Multiplicative Classes ----------------------- - -In the **complex** case, let `f` be a holomorphic function around zero. -Then we call - -.. MATH:: - - \left[\det\left( f\left( \frac{\Omega}{2 \pi i} \right) - \right)\right] \in H^{2*}_{\mathrm{dR}}(M, \CC) - -the *multiplicative characteristic class associated to* `f` of the complex -vector bundle `E`. - -Important and predefined multiplicative classes on complex vector bundles are: - -- *Chern class* with `f(x) = 1+x` -- *Todd class* with `f(x) = \frac{x}{1-\exp(-x)}` - -In the **real** case, let `g` be a holomorphic function around zero with -`g(0)=1`. Then we call - -.. MATH:: - - \left[\det\left( \sqrt{ g \left( -\frac{\Omega^2}{4 \pi^2} \right) } \right) - \right] \in H^{4*}_{\mathrm{dR}}(M, \CC) - -the *multiplicative characteristic class associated to* `g` on the **real** -vector bundle `E`. - -Important and predefined multiplicative classes on real vector bundles are: - -- *Pontryagin class* with `g(x) = 1+x` -- `\hat{A}` *class* with `g(x) = \frac{\sqrt{x}/2}{\sinh(\sqrt{x}/2)}` -- *Hirzebruch class* with `g(x) = \frac{\sqrt{x}}{\tanh(\sqrt{x})}` - -EXAMPLES: - -We consider the **Chern class** of the tautological line bundle `\gamma^1` over -`\CC\mathbf{P}^1`:: - - sage: M = Manifold(2, 'CP^1', start_index=1) - sage: U = M.open_subset('U') - sage: c_cart. = U.chart() # homogeneous coordinates in real terms - sage: c_comp. = U.chart(r'z:z zbar:\bar{z}') # complexification - sage: cart_to_comp = c_cart.transition_map(c_comp, (x+I*y, x-I*y)) - sage: comp_to_cart = cart_to_comp.inverse() - sage: E = M.vector_bundle(1, 'gamma^1', field='complex') - sage: e = E.local_frame('e', domain=U) - -To apply the Chern-Weil approach, we need a bundle connection in terms of a -connection one form. To achieve this, we take the connection induced from the -hermitian metric on the trivial bundle -`\CC^2 \times \CC\mathbf{P}^1 \supset \gamma^1`. In this the frame `e` -corresponds to the section `[z:1] \mapsto (z,1)` and its magnitude-squared -is given by `1+|z|^2`:: - - sage: nab = E.bundle_connection('nabla') - sage: omega = U.one_form(name='omega') - sage: omega[c_comp.frame(),1,c_comp] = zbar/(1+z*zbar) - sage: nab[e, 1, 1] = omega - sage: nab.set_immutable() - -Now, the Chern class can be constructed:: - - sage: c = E.characteristic_class('Chern'); c - Characteristic class c of multiplicative type associated to x + 1 on the - Differentiable complex vector bundle gamma^1 -> CP^1 of rank 1 over the - base space 2-dimensional differentiable manifold CP^1 - sage: c_form = c.get_form(nab) - sage: c_form.display_expansion(c_comp.frame(), chart=c_comp) - c(gamma^1, nabla) = 1 + 1/2*I/(pi + pi*z^2*zbar^2 + 2*pi*z*zbar) dz∧dzbar - -Since `U` and `\CC\mathbf{P}^1` differ only by a point and therefore a null -set, it is enough to integrate the top form over the domain `U`:: - - sage: integrate(integrate(c_form[2][[1,2]].expr(c_cart), x, -infinity, infinity).full_simplify(), - ....: y, -infinity, infinity) - 1 - -The result shows that `c_1(\gamma^1)` generates the second integer -cohomology of `\CC\mathbf{P}^1`. - -.. _Pfaffian: - -Pfaffian Classes ----------------- - -Usually, there is no such thing as "Pfaffian classes" in literature. However, -using the matrix' Pfaffian and inspired by the aforementioned definitions, -such classes can be defined as follows. - -Let `E` be a real vector bundle of rank `2n` and `f` an odd real function -being analytic at zero. Furthermore, let `\Omega` be skew-symmetric, which -certainly will be true if `\nabla` is metric and `e` is orthonormal. Then -we call - -.. MATH:: - - \left[\mathrm{Pf}\left( f\left( \frac{\Omega}{2 \pi} \right) \right)\right] - \in H^{2n*}(M,\RR) - -the *Pfaffian class associated to f*. - -The most important Pfaffian class is the *Euler class* which is simply given by -`f(x)=x`. - -EXAMPLES: - -We consider the **Euler class** of `S^2`:: - - sage: M = Manifold(2, name='S2', latex_name=r'S^2', start_index=1) - sage: U = M.open_subset('U') ; V = M.open_subset('V') - sage: M.declare_union(U,V) # M is the union of U and V - sage: c_xy. = U.chart() ; c_uv. = V.chart() - sage: xy_to_uv = c_xy.transition_map(c_uv, - ....: (x/(x^2+y^2), y/(x^2+y^2)), - ....: intersection_name='W', - ....: restrictions1= x^2+y^2!=0, - ....: restrictions2= u^2+v^2!=0) - sage: uv_to_xy = xy_to_uv.inverse() - sage: eU = c_xy.frame() ; eV = c_uv.frame() - sage: TM = M.tangent_bundle() - sage: e_class = TM.characteristic_class('Euler'); e_class - Characteristic class e of Pfaffian type associated to x on the Tangent - bundle TS2 over the 2-dimensional differentiable manifold S2 - -To compute a particular representative of the Euler class, we need to determine -a connection:: - - sage: g = M.metric('g') # standard metric on S2 - sage: g[eU,1,1], g[eU,2,2] = 4/(1+x^2+y^2)^2, 4/(1+x^2+y^2)^2 - sage: g[eV,1,1], g[eV,2,2] = 4/(1+u^2+v^2)^2, 4/(1+u^2+v^2)^2 - sage: nab = g.connection() - -In case of the Euler class, skew-symmetric curvature matrices are needed -for the Pfaffian. For this, we need to define the curvature matrices by -hand:: - - sage: cmatrix_U = [[nab.curvature_form(i,j,eU) for j in TM.irange()] - ....: for i in TM.irange()] - sage: cmatrix_V = [[nab.curvature_form(i,j,eV) for j in TM.irange()] - ....: for i in TM.irange()] - -Fortunately, both curvature matrices are already skew-symmetric:: - - sage: for i in range(TM.rank()): - ....: for j in range(TM.rank()): - ....: print(cmatrix_U[i][j].display()) - curvature (1,1) of connection nabla_g w.r.t. Coordinate frame - (U, (∂/∂x,∂/∂y)) = 0 - curvature (1,2) of connection nabla_g w.r.t. Coordinate frame - (U, (∂/∂x,∂/∂y)) = 4/(x^4 + y^4 + 2*(x^2 + 1)*y^2 + 2*x^2 + 1) dx∧dy - curvature (2,1) of connection nabla_g w.r.t. Coordinate frame - (U, (∂/∂x,∂/∂y)) = -4/(x^4 + y^4 + 2*(x^2 + 1)*y^2 + 2*x^2 + 1) dx∧dy - curvature (2,2) of connection nabla_g w.r.t. Coordinate frame - (U, (∂/∂x,∂/∂y)) = 0 - sage: for i in range(TM.rank()): - ....: for j in range(TM.rank()): - ....: print(cmatrix_V[i][j].display()) - curvature (1,1) of connection nabla_g w.r.t. Coordinate frame - (V, (∂/∂u,∂/∂v)) = 0 - curvature (1,2) of connection nabla_g w.r.t. Coordinate frame - (V, (∂/∂u,∂/∂v)) = 4/(u^4 + v^4 + 2*(u^2 + 1)*v^2 + 2*u^2 + 1) du∧dv - curvature (2,1) of connection nabla_g w.r.t. Coordinate frame - (V, (∂/∂u,∂/∂v)) = -4/(u^4 + v^4 + 2*(u^2 + 1)*v^2 + 2*u^2 + 1) du∧dv - curvature (2,2) of connection nabla_g w.r.t. Coordinate frame - (V, (∂/∂u,∂/∂v)) = 0 - sage: nab.set_immutable() # make nab immutable - -Now the representative of the Euler class with respect to the connection -`\nabla_g` induced by the standard metric can be computed:: - - sage: cmatrices = {eU: cmatrix_U, eV: cmatrix_V} - sage: e_class_form = e_class.get_form(nab, cmatrices) - sage: e_class_form.display_expansion() - e(TS2, nabla_g) = 2/(pi + pi*x^4 + pi*y^4 + 2*pi*x^2 + 2*(pi + pi*x^2)*y^2) dx∧dy - -Let us check whether this form represents the Euler class correctly:: - - sage: integrate(integrate(e_class_form[2][[1,2]].expr(), x, -infinity, infinity).simplify_full(), - ....: y, -infinity, infinity) - 2 - -As we can see, the integral coincides with the Euler characteristic of `S^2` so -that our form actually represents the Euler class appropriately. - -""" - -#****************************************************************************** -# Copyright (C) 2019 Michael Jung -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# https://www.gnu.org/licenses/ -#****************************************************************************** - -from sage.structure.unique_representation import UniqueRepresentation -from sage.misc.cachefunc import cached_method -from sage.structure.sage_object import SageObject -from sage.symbolic.ring import SR -from sage.rings.rational_field import QQ - -################################################################################ -## Separate functions - -def _get_predefined_class(arg): - r""" - Return the signature of the predefined class given by the string ``arg``. - - The signature is given by a tuple following the syntax - (base field, class type, name, LaTeX name, function). - - Modify this method to add new predefined characteristic classes. - - TESTS:: - - sage: from sage.manifolds.differentiable.characteristic_class import _get_predefined_class - sage: _get_predefined_class('Chern') - ('complex', 'multiplicative', 'c', 'c', x + 1) - sage: _get_predefined_class('Pontryagin') - ('real', 'multiplicative', 'p', 'p', x + 1) - sage: _get_predefined_class('Euler') - ('real', 'Pfaffian', 'e', 'e', x) - - """ - if not isinstance(arg, str): - raise TypeError("argument 'arg' must be string") - # Define variable: - x = SR.symbol('x') - # Define dictionary. The syntax is as follows: - # (field_type, class_type, name, latex_name, func) - if arg == 'ChernChar': - return ('complex', 'additive', 'ch', r'\mathrm{ch}', x.exp()) - elif arg == 'Todd': - return ('complex', 'additive', 'Td', r'\mathrm{Td}', - x / (1 - (-x).exp())) - elif arg == 'Chern': - return ('complex', 'multiplicative', 'c', 'c', 1 + x) - elif arg == 'Pontryagin': - return ('real', 'multiplicative', 'p', 'p', 1 + x) - elif arg == 'AHat': - return ('real', 'multiplicative', 'A^', r'\hat{A}', - x.sqrt() / (2 * (x.sqrt() / 2).sinh())) - elif arg == 'Hirzebruch': - return ('real', 'multiplicative', 'L', 'L', - x.sqrt() / x.sqrt().tanh()) - elif arg == 'Euler': - return ('real', 'Pfaffian', 'e', 'e', x) - else: - raise ValueError("the characteristic class '{}' is ".format(arg) + - "not predefined yet.") - -################################################################################ -## Classes - -class CharacteristicClass(UniqueRepresentation, SageObject): - r""" - An instance of this class represents a characteristic class on some - differentiable vector bundle over the field `\RR` or `\CC`. - - INPUT: - - - vbundle -- vector bundle on which the characteristic class should be - defined - - func -- symbolic expression representing the function to which ``self`` - should be associated to - - class_type -- (default: ``'multiplicative'``) class type of the - characteristic class; at this stage, the following options are possible: - - - ``'multiplicative'`` -- returns a class of multiplicative type, - using the determinant - - ``'additive'`` -- returns a class of additive type, using the trace - - ``'Pfaffian'`` -- returns a class of Pfaffian type, using the - Pfaffian - - - ``name`` -- string representation given to the characteristic class - - ``latex_name`` -- (default: ``None``) LaTeX name given to the - characteristic class - - EXAMPLES: - - Get characteristic classes using predefined ones:: - - sage: M = Manifold(4, 'M') - sage: TM = M.tangent_bundle() - sage: TM.characteristic_class('Pontryagin') - Characteristic class p of multiplicative type associated to x + 1 on the - Tangent bundle TM over the 4-dimensional differentiable manifold M - sage: TM.characteristic_class('Hirzebruch') - Characteristic class L of multiplicative type associated to - sqrt(x)/tanh(sqrt(x)) on the Tangent bundle TM over the 4-dimensional - differentiable manifold M - sage: TM.characteristic_class('AHat') - Characteristic class A^ of multiplicative type associated to - 1/2*sqrt(x)/sinh(1/2*sqrt(x)) on the Tangent bundle TM over the - 4-dimensional differentiable manifold M - - The vector bundle's base field and definition domain of the characteristic - class must fit together, otherwise an error message occurs:: - - sage: TM.characteristic_class('Chern') - Traceback (most recent call last): - ... - ValueError: base field must be complex for class 'Chern' - - If your favourite class is not predefined yet, the associated function can - be put manually:: - - sage: cl = TM.characteristic_class(1+x^2, name='cl'); cl - Characteristic class cl of multiplicative type associated to x^2 + 1 on - the Tangent bundle TM over the 4-dimensional differentiable manifold M - - """ - def __init__(self, vbundle, func, class_type='multiplicative', name=None, - latex_name=None): - r""" - Construct a characteristic class. - - TESTS:: - - sage: M = Manifold(3, 'M') - sage: TM = M.tangent_bundle() - sage: from sage.manifolds.differentiable.characteristic_class import CharacteristicClass - sage: c = CharacteristicClass(TM, 1+x, name='c'); c - Characteristic class c of multiplicative type associated to x + 1 on - the Tangent bundle TM over the 3-dimensional differentiable - manifold M - sage: TestSuite(c).run() - - """ - if vbundle._field_type == 'neither_real_nor_complex': - raise ValueError("the vector bundle must either be real or complex") - if class_type not in ['additive', 'multiplicative', 'Pfaffian']: - raise ValueError("the argument 'class_type' must either be " - "'additive', 'multiplicative' or 'Pfaffian'") - if class_type == 'Pfaffian': - if vbundle._field_type != 'real' or vbundle._rank % 2 != 0: - raise ValueError("Pfaffian classes can only be defined for real" - " vector bundles of even rank") - self._name = name - if latex_name is None: - self._latex_name = name - else: - self._latex_name = latex_name - self._func = func - self._class_type = class_type - self._vbundle = vbundle - self._base_space = vbundle._base_space - self._rank = vbundle._rank - self._coeff_list = self._get_coeff_list() - self._init_derived() - - def _get_coeff_list(self, distinct_real=True): - r""" - Return the list of coefficients of the Taylor expansion at zero of the - function. - - TESTS:: - - sage: M = Manifold(2, 'M') - sage: E = M.vector_bundle(1, 'E', field='complex') - sage: c = E.characteristic_class(1+x) - sage: c._get_coeff_list() - [1, 1] - - """ - pow_range = self._base_space._dim // 2 - def_var = self._func.default_variable() - # Use a complex variable without affecting the old one: - with SR.temp_var(domain='complex') as new_var: - if self._vbundle._field_type == 'real' and distinct_real: - if self._class_type == 'additive': - func = self._func.subs({def_var: new_var ** 2}) / 2 - elif self._class_type == 'multiplicative': - # This could case problems in the real domain, where sqrt(x^2) - # is simplified to |x|. However, the variable must be complex - # anyway. - func = self._func.subs({def_var : new_var**2}).sqrt() - elif self._class_type == 'Pfaffian': - # There are no canonical Pfaffian classes, however, consider the - # projection onto the odd part of the function to keep the - # matrices skew: - func = (self._func.subs({def_var: new_var}) - - self._func.subs({def_var: -new_var})) / 2 - else: - func = self._func.subs({def_var: new_var}) - - if self._vbundle._field_type == 'real' and not distinct_real: - pow_range = pow_range // 2 - - return func.taylor(new_var, 0, pow_range).coefficients(sparse=False) - - def _init_derived(self): - r""" - Initialize the derived quantities. - - TESTS:: - - sage: M = Manifold(2, 'M') - sage: TM = M.tangent_bundle() - sage: c = TM.characteristic_class(1+x) - sage: c._init_derived() - - """ - self._mixed_forms = {} # dict. of mixed forms corresponding this - # characteristic class - # (key: bundle connection) - - def _del_derived(self): - r""" - Delete the derived quantities. - - TESTS:: - - sage: M = Manifold(2, 'M') - sage: TM = M.tangent_bundle() - sage: c = TM.characteristic_class(1+x) - sage: c._del_derived() - - """ - self._mixed_forms.clear() - - def _repr_(self): - r""" - String representation of the object. - - TESTS:: - - sage: M = Manifold(2, 'M') - sage: TM = M.tangent_bundle() - sage: c = TM.characteristic_class(1+x, name='c') - sage: c # indirect doctest - Characteristic class c of multiplicative type associated to x + 1 on - the Tangent bundle TM over the 2-dimensional differentiable - manifold M - sage: repr(c) # indirect doctest - 'Characteristic class c of multiplicative type associated to x + 1 - on the Tangent bundle TM over the 2-dimensional differentiable - manifold M' - sage: c._repr_() - 'Characteristic class c of multiplicative type associated to x + 1 - on the Tangent bundle TM over the 2-dimensional differentiable - manifold M' - - """ - desc = "Characteristic class " - if self._name is not None: - desc += self._name + " " - desc += "of {} type ".format(self._class_type) - desc += "associated to {} on the {}".format(self._func, self._vbundle) - return desc - - def _latex_(self): - r""" - LaTeX representation of the object. - - TESTS:: - - sage: M = Manifold(2, 'M') - sage: TM = M.tangent_bundle() - sage: ch = TM.characteristic_class(exp(x), class_type='additive', - ....: name='ch', latex_name=r'\mathrm{ch}') - sage: ch._latex_() - '\\mathrm{ch}(TM)' - - """ - return self._latex_name + "(" + self._vbundle._latex_name + ")" - - def class_type(self): - r""" - Return the class type of ``self``. - - EXAMPLES:: - - sage: M = Manifold(2, 'M') - sage: TM = M.tangent_bundle() - sage: ch = TM.characteristic_class(exp(x), class_type='additive', - ....: name='ch', latex_name=r'\mathrm{ch}') - sage: ch.class_type() - 'additive' - - """ - return self._class_type - - def function(self): - r""" - Return the function corresponding to this characteristic class. - - EXAMPLES:: - - sage: M = Manifold(2, 'M') - sage: TM = M.tangent_bundle() - sage: e_class = TM.characteristic_class('Euler') - sage: e_class.function() - x - sage: AHat = TM.characteristic_class('AHat') - sage: AHat.function() - 1/2*sqrt(x)/sinh(1/2*sqrt(x)) - sage: c = TM.characteristic_class(1+x, name='c') - sage: c.function() - x + 1 - - """ - return self._func - - @cached_method - def sequence(self, ring=QQ): - r""" - Return the multiplicative/additive sequence (depending on the class - type of ``self``) of ``self.function`` in terms of elementary symmetric - functions `e_i`. - - If `f(x)` is the function with respect to ``self`` then its - multiplicative sequence is given by - - .. MATH:: - - \Pi_{i = 1}^n f(x_i) = \sum^n_{i=0} c_i \, e_i(x_1, \ldots, x_n) - - whereas its additive sequence is given by - - .. MATH:: - - \sum_{i = 1}^n f(x_i) = \sum^n_{i=0} c_i \, e_i(x_1, \ldots, x_n). - - Here, `e_i` denotes the `i`-th elementary symmetric function. - - INPUT: - - - ``ring`` -- (default: ``QQ``) the base ring of the symmetric - function ring; in most cases, one can assume ``QQ`` which is - supposed to work faster, if it doesn't work, try ``SR`` instead. - - OUTPUT: - - - a symmetric function in the elementary symmetric basis represented - by an instance of - :class:`~sage.combinat.sf.elementary.SymmetricFunctionAlgebra_elementary` - - EXAMPLES: - - Consider the multiplicative sequence of the `\hat{A}` class:: - - sage: M = Manifold(8, 'M') - sage: A = M.tangent_bundle().characteristic_class('AHat') - sage: A.sequence() - e[] - 1/24*e[1] + 7/5760*e[1, 1] - 1/1440*e[2] - - This is an element of the symmetric functions over the rational field:: - - sage: A.sequence().parent() - Symmetric Functions over Rational Field in the elementary basis - - To get the sequence as an element of usual polynomial ring, we can do - the following:: - - sage: P = PolynomialRing(QQ, 'e', 3) - sage: poly = P(sum(c * prod(P.gens()[i] for i in p) - ....: for p, c in A.sequence())) - sage: poly - 7/5760*e1^2 - 1/24*e1 - 1/1440*e2 + 1 - - Get an additive sequence:: - - sage: E = M.vector_bundle(2, 'E', field='complex') - sage: ch = E.characteristic_class('ChernChar') - sage: ch.sequence() - 2*e[] + e[1] + 1/2*e[1, 1] + 1/6*e[1, 1, 1] + 1/24*e[1, 1, 1, 1] - - e[2] - 1/2*e[2, 1] - 1/6*e[2, 1, 1] + 1/12*e[2, 2] + 1/2*e[3] - + 1/6*e[3, 1] - 1/6*e[4] - - .. SEEALSO:: - - See :class:`~sage.combinat.sf.elementary.SymmetricFunctionAlgebra_elementary` - for detailed information about elementary symmetric functions. - - """ - if self._class_type == 'Pfaffian': - return NotImplementedError('this functionality is not supported ' - 'for characteristic classes of ' - 'Pfaffian type') - - from sage.combinat.sf.sf import SymmetricFunctions - from sage.misc.misc_c import prod - - Sym = SymmetricFunctions(ring) - - coeff = self._get_coeff_list(distinct_real=False) - from sage.combinat.partition import Partitions - m = Sym.m() - if self._class_type == 'multiplicative': - # Get the multiplicative sequence in the monomial basis: - mon_pol = m._from_dict({p: prod(ring(coeff[i]) for i in p) - for k in range(len(coeff)) - for p in Partitions(k)}) - elif self._class_type == 'additive': - # Express the additive sequence in the monomial basis, the 0th - # order term must be treated separately: - - m_dict = {Partitions(0)([]): self._vbundle._rank * ring(coeff[0])} - m_dict.update({Partitions(k)([k]): ring(coeff[k]) for k in range(1, len(coeff))}) - mon_pol = m._from_dict(m_dict) - # Convert to elementary symmetric polynomials: - return Sym.e()(mon_pol) - - def get_form(self, connection, cmatrices=None): - r""" - Return the form representing ``self`` with respect to the given - connection ``connection``. - - INPUT: - - - ``connection`` -- connection to which the form should be associated to; - this can be either a bundle connection as an instance of - :class:`~sage.manifolds.differentiable.bundle_connection.BundleConnection` - or, in case of the tensor bundle, an affine connection as an instance - of :class:`~sage.manifolds.differentiable.affine_connection.AffineConnection` - - ``cmatrices`` -- (default: ``None``) a dictionary of curvature - matrices with local frames as keys and curvature matrices as items; if - ``None``, Sage tries to get the curvature matrices from the connection - - OUTPUT: - - - mixed form as an instance of - :class:`~sage.manifolds.differentiable.mixed_form.MixedForm` - representing the total characteristic class - - .. NOTE:: - - Be aware that depending on the characteristic class and complexity - of the manifold, computation times may vary a lot. In addition, if - not done before, the curvature form is computed from the connection, - here. If this behaviour is not wanted and the curvature form is - already known, please use the argument ``cmatrices``. - - EXAMPLES: - - Again, consider the Chern character on some 2-dimensional spacetime:: - - sage: M = Manifold(2, 'M', structure='Lorentzian') - sage: X. = M.chart() - sage: E = M.vector_bundle(1, 'E', field='complex'); E - Differentiable complex vector bundle E -> M of rank 1 over the base - space 2-dimensional Lorentzian manifold M - sage: e = E.local_frame('e') - - And again, we define the connection `\nabla^E` in terms of an - electro-magnetic potential `A(t)`:: - - sage: nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E') - sage: omega = M.one_form(name='omega') - sage: A = function('A') - sage: nab.set_connection_form(0, 0)[1] = I*A(t) - sage: nab.set_immutable() - sage: nab[0, 0].display() - connection (0,0) of bundle connection nabla^E w.r.t. Local frame - (E|_M, (e_0)) = I*A(t) dx - - .. NOTE:: - - The characteristic form is strongly linked to the connection - which is why we must make the connection unchangeable, - i.e. immutable, with the command - :meth:`sage.manifolds.differentiable.bundle_connection.BundleConnection.set_immutable` - before we can use :meth:`get_form`. - - The Chern character is then given by:: - - sage: ch = E.characteristic_class('ChernChar'); ch - Characteristic class ch of additive type associated to e^x on the - Differentiable complex vector bundle E -> M of rank 1 over the base - space 2-dimensional Lorentzian manifold M - - Inserting the connection, the result is a mixed differential form with - a priori non-zero components in even degrees:: - - sage: ch_form = ch.get_form(nab); ch_form - Mixed differential form ch(E, nabla^E) on the 2-dimensional - Lorentzian manifold M - sage: ch_form.display() - ch(E, nabla^E) = ch_0(E, nabla^E) + zero + ch_1(E, nabla^E) - sage: ch_form.display_expansion() - ch(E, nabla^E) = 1 + 1/2*d(A)/dt/pi dt∧dx - - Due to long computation times, the form is saved:: - - sage: ch_form is ch.get_form(nab) - True - - """ - from .bundle_connection import BundleConnection - from .affine_connection import AffineConnection - if not isinstance(connection, (AffineConnection, BundleConnection)): - raise TypeError("argument must be an affine connection on the " - "manifold or bundle connection on the vector " - "bundle") - if connection not in self._mixed_forms: - base_space = self._base_space - if cmatrices is None: - if self._class_type == 'Pfaffian': - raise NotImplementedError( - "At this stage, Pfaffian forms cannot be derived from " - "(metric) connections. Please use the argument " - "'cmatrices' to insert a dictionary of skew-symmetric " - "curvature matrices by hand, instead.") - cmatrices = {} - for frame in base_space._get_min_covering(connection._coefficients): - cmatrix = [[connection.curvature_form(i, j, frame) - for j in self._vbundle.irange()] - for i in self._vbundle.irange()] - cmatrices[frame] = cmatrix - # Prepare mixed form: - name, latex_name = self._name, self._latex_name - if name is not None and connection._name is not None: - name += "(" + self._vbundle._name + ", " + connection._name + ")" - if latex_name is not None and connection._latex_name is not None: - latex_name += "(" + self._vbundle._latex_name + ", " + \ - connection._latex_name + ")" - res = base_space.mixed_form(name=name, latex_name=latex_name) - # BEGIN computation: - from sage.matrix.matrix_space import MatrixSpace - for frame, cmatrix in cmatrices.items(): - # Define matrix space: - dom = frame._domain - alg = dom.mixed_form_algebra() - mspace = MatrixSpace(alg, self._rank) - # Insert "normalized" curvature matrix into polynomial: - cmatrix = mspace(cmatrix) # convert curvature matrix - ncmatrix = self._normalize_matrix(cmatrix) - rmatrix = self._insert_in_polynomial(ncmatrix) - # Compute classes: - if self._class_type == 'additive': - rst = rmatrix.trace() # mixed form - elif self._class_type == 'multiplicative': - rst = rmatrix.det() # mixed form - elif self._class_type == 'Pfaffian': - rst = rmatrix.pfaffian() # mixed form - # Set restriction: - res.set_restriction(rst) - # END of computation - # - # Preparation to name each homogeneous component; only even (or in - # the real case, by four divisible) degrees are non-zero: - if self._class_type == 'Pfaffian': - deg_dist = self._rank - elif self._vbundle._field_type == 'real': - deg_dist = 4 - elif self._vbundle._field_type == 'complex': - deg_dist = 2 - else: - # You never know... - deg_dist = 1 - # Now, define the name for each form: - for k in res.irange(): - if k % deg_dist != 0 or (self._class_type == 'Pfaffian' and - k == 0): - res[k] = 0 # this form is zero anyway - else: - # String representation: - if self._name is not None: - name = self._name + "_" + str(k // deg_dist) + \ - "(" + self._vbundle._name - if connection._name is not None: - name += ", " + connection._name - name += ")" - # LaTeX name: - if self._latex_name is not None: - latex_name = self._latex_name + \ - r"_{" + str(k // deg_dist) + r"}" + \ - r"(" + self._vbundle._latex_name - if connection._latex_name is not None: - latex_name += r", " + connection._latex_name - latex_name += r")" - # Set name: - res[k].set_name(name=name, latex_name=latex_name) - # Add the result to the dictionary: - self._mixed_forms[connection] = res - - return self._mixed_forms[connection] - - def _insert_in_polynomial(self, cmatrix): - r""" - Return the matrix after inserting `cmatrix` into the polynomial given by - the taylor expansion of `self._func`. - - TESTS:: - - sage: M = Manifold(4, 'M') - sage: c = M.tangent_bundle().characteristic_class('Pontryagin') - sage: c._insert_in_polynomial(x) - 1/2*x^2 + 1 - - """ - mspace = cmatrix.parent() - # Compute matrix powers: - power_list = [mspace.one()] - for pow in range(len(self._coeff_list) - 1): - power_list.append(cmatrix * power_list[pow]) - # Put things together: - rmatrix = sum(self._coeff_list[k] * power_list[k] - for k in range(len(self._coeff_list))) - - return rmatrix - - def _normalize_matrix(self, cmatrix): - r""" - Return the curvature matrix "normalized" with `i/(2 \pi)` or `1/(2 \pi)` - respectively. - - INPUT: - - - ``cmatrix`` -- curvature matrix - - OUTPUT: - - - ``I/(2*pi)*cmatrix`` - - TESTS:: - - sage: M = Manifold(2, 'M') - sage: TM = M.tangent_bundle() - sage: c = TM.characteristic_class(1+x) - sage: c._normalize_matrix(x) - -1/2*I*x/pi - - """ - from sage.symbolic.constants import pi - fac = 1 / (2 * pi) - if self._class_type != 'Pfaffian': - from sage.libs.pynac.pynac import I - fac = fac / I - return fac * cmatrix diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index c8beb749d7d..0f0cd10b8ff 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -2,7 +2,7 @@ Characteristic cohomology classes A *characteristic class* `\kappa` is a natural transformation that -associates to each vector bundle `E \to M` a cohomology class +associates with each vector bundle `E \to M` a cohomology class `\kappa(E) \in H^*(M;R)` such that for any continuous map `f\colon N \to M` from another topological manifold `N`, the *naturality condition* is satisfied: @@ -41,7 +41,8 @@ AUTHORS: -- Michael Jung (2021) : initial version +- Michael Jung (2019) : initial version +- Michael Jung (2021) : new algorithm; complete refactoring REFERENCES: @@ -293,6 +294,35 @@ class CharacteristicCohomologyClassRingElement(IndexedFreeModuleElement): r""" Characteristic cohomology class. + Let `E \to M` be a real/complex vector bundle of rank `k`. A characteristic + cohomology class of `E` is generated by either + + - Chern classes if `E` is complex, + - Pontryagin classes if `E` is real, + - Pontryagin classes and the Euler class if `E` is real and orientable, + + via the Chern-Weil homomorphism. + + For details about the ring structure, see + :class:`CharacteristicCohomologyClassRing`. + + To construct a characteristic cohomology class, please use + :func:`CharacteristicCohomologyClass`. + + EXAMPLES:: + + sage: M = Manifold(12, 'M') + sage: TM = M.tangent_bundle() + sage: CR = TM.characteristic_cohomology_class_ring() + sage: p1, p2, p3 = CR.gens() + sage: p1*p2+p3 + Characteristic cohomology class (p_1⌣p_2 + p_3)(TM) of the Tangent + bundle TM over the 12-dimensional differentiable manifold M + sage: A = TM.characteristic_cohomology_class('AHat'); A + Characteristic cohomology class A^(TM) of the Tangent bundle TM over + the 12-dimensional differentiable manifold M + sage: A == 1 - p1/24 + (7*p1^2-4*p2)/5760 + (44*p1*p2-31*p1^3-16*p3)/967680 + True """ def __init__(self, parent, x, name=None, latex_name=None): r""" @@ -1208,7 +1238,7 @@ def get(self, nab): def get_local(self, cmat): r""" Abstract method to get the local forms of the generators w.r.t. a given - curvature matrix ``cmat``. + curvature form matrix ``cmat``. OUTPUT: @@ -1235,7 +1265,7 @@ class ChernAlgorithm(Singleton, Algorithm_generic): """ def get_local(self, cmat): r""" - Return the local Chern forms w.r.t. a given curvature matrix. + Return the local Chern forms w.r.t. a given curvature form matrix. OUTPUT: @@ -1276,7 +1306,7 @@ class PontryaginAlgorithm(Singleton, Algorithm_generic): """ def get_local(self, cmat): r""" - Return the local Pontryagin forms w.r.t. a given curvature matrix. + Return the local Pontryagin forms w.r.t. a given curvature form matrix. OUTPUT: @@ -1358,7 +1388,7 @@ def get(self, nab): def get_local(self, cmat): r""" - Return the normalized Pfaffian w.r.t. a given curvature matrix. + Return the normalized Pfaffian w.r.t. a given curvature form matrix. The normalization is given by the factor `\left(\frac{1}{2 \pi}\right)^{\frac{k}{2}}`, where `k` is the @@ -1420,7 +1450,7 @@ def get(self, nab): def get_local(self, cmat): r""" Return the local Euler and Pontryagin forms w.r.t. a given curvature - matrix. + form matrix. OUTPUT: From 265f918b8045a6b93142130f9fa4147fc7bd1f4c Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 9 Sep 2021 16:24:16 +0200 Subject: [PATCH 38/50] Trac #29581: fix sign mistake --- .../differentiable/characteristic_cohomology_class.py | 4 ++-- src/sage/manifolds/differentiable/de_rham_cohomology.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 0f0cd10b8ff..bce8f79c26e 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1325,7 +1325,7 @@ def get_local(self, cmat): ran = min(rk // 2, dim // 4) if ran < 1: return [] # nothing to compute - fac = -1 / (2 * pi) ** 2 + fac = 1 / (2 * pi) ** 2 res = [] m = cmat2 = [[sum(cmat[i][l].wedge(cmat[l][j]) for l in range(rk)) @@ -1335,7 +1335,7 @@ def get_local(self, cmat): res.append(fac * c) for i in range(rk): m[i][i] += c - fac *= -1 / (2 * pi) ** 2 + fac *= 1 / (2 * pi) ** 2 m = [[sum(cmat2[i][l].wedge(m[l][j]) for l in range(rk)) for j in range(rk)] for i in range(rk)] res.append(-fac * sum(m[i][i] for i in range(rk)) / (2 * ran)) diff --git a/src/sage/manifolds/differentiable/de_rham_cohomology.py b/src/sage/manifolds/differentiable/de_rham_cohomology.py index d511f44fd5d..403f9f37f00 100644 --- a/src/sage/manifolds/differentiable/de_rham_cohomology.py +++ b/src/sage/manifolds/differentiable/de_rham_cohomology.py @@ -449,6 +449,7 @@ def _coerce_map_from_(self, other): """ if isinstance(other, CharacteristicCohomologyClassRing): + # TODO: we need to be careful if manifolds have boundary! return other._vbundle._base_space == self._manifold return super()._coerce_map_from_(other) From a3555e13332e0879adae188179120f4311462c6e Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 9 Sep 2021 17:49:31 +0200 Subject: [PATCH 39/50] Trac #29581: more docstring --- .../characteristic_cohomology_class.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index bce8f79c26e..8b48baafaac 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1262,6 +1262,42 @@ def get_gen_pow(self, nab, i, n): class ChernAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Chern forms. + + EXAMPLES: + + Define a complex line bundle over a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='Lorentzian') + sage: X. = M.chart() + sage: E = M.vector_bundle(1, 'E', field='complex'); E + Differentiable complex vector bundle E -> M of rank 1 over the base space + 2-dimensional Lorentzian manifold M + sage: e = E.local_frame('e') + sage: nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E') + sage: omega = M.one_form(name='omega') + sage: A = function('A') + sage: nab.set_connection_form(0, 0)[1] = I*A(t) + sage: nab[0, 0].display() + connection (0,0) of bundle connection nabla^E w.r.t. Local frame + (E|_M, (e_0)) = I*A(t) dx + sage: nab.set_immutable() + + Import the algorithm and apply ``nab`` to it:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import ChernAlgorithm + sage: algorithm = ChernAlgorithm() + sage: algorithm.get(nab) + [2-form on the 2-dimensional Lorentzian manifold M] + sage: algorithm.get(nab)[0].display() + 1/2*d(A)/dt/pi dt∧dx + + Check some equalities:: + + sage: cmat = [[nab.curvature_form(0, 0, e)]] + sage: algorithm.get(nab)[0] == algorithm.get_local(cmat)[0] # bundle trivial + True + sage: algorithm.get_gen_pow(nab, 0, 1) == algorithm.get(nab)[0] + True """ def get_local(self, cmat): r""" From 939cfd9d21342527dc0442f653dc50791ce73ad8 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sat, 11 Sep 2021 22:11:57 +0200 Subject: [PATCH 40/50] Trac #29581: more doc EulerAlgorithm --- src/doc/en/reference/references/index.rst | 4 + .../characteristic_cohomology_class.py | 175 +++++++++++++++++- 2 files changed, 173 insertions(+), 6 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index c022be01e1e..9f68fa63cbf 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1280,6 +1280,10 @@ REFERENCES: Coinvariants Quasi-Symétriques*, Electronic Journal of Combinatorics Vol 12(1) (2005) N16. +.. [Che1944] \S. Chern, *A simple intrinsic proof of the Gauss-Bonnet formula + for closed Riemannian manifolds*, Ann. of Math. (2) 45 (1944), + 747–752. + .. [CQ2019] \A. Cassella and C. Quadrelli. *Right-angled Artin groups and enhanced Koszul properties*. Preprint, :arxiv:`1907.03824`, (2019). diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 8b48baafaac..ee158b8d3bf 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -421,7 +421,8 @@ def get_form(self, nab): OUTPUT: - - an instance of `sage.manifolds.differentiable.mixed_form.MixedForm` + - an instance of + :class:`sage.manifolds.differentiable.mixed_form.MixedForm` EXAMPLES: @@ -528,7 +529,8 @@ def representative(self, nab=None): OUTPUT: - - an instance of `sage.manifolds.differentiable.mixed_form.MixedForm` + - an instance of + :class:`sage.manifolds.differentiable.mixed_form.MixedForm` EXAMPLES: @@ -1211,7 +1213,13 @@ def get(self, nab): OUTPUT: - - a list containing the generator's global characteristic forms + - a list containing the generator's global characteristic forms as + instances of + :class:`sage.manifolds.differentiable.diff_form.DiffForm` + + EXAMPLES: + + sage: """ if isinstance(nab, AffineConnection): vbundle = nab._domain.tangent_bundle() @@ -1243,6 +1251,10 @@ def get_local(self, cmat): OUTPUT: - a list containing the generator's local characteristic forms + + ALGORITHM: + + The inherited class determines the algorithm. """ pass @@ -1305,12 +1317,41 @@ def get_local(self, cmat): OUTPUT: - - a list containing the local characteristic Chern forms + - a list containing the local characteristic Chern forms as + instances of + :class:`sage.manifolds.differentiable.diff_form.DiffForm` ALGORITHM:: The algorithm is based on the Faddeev-LeVerrier algorithm for the characteristic polynomial. + + EXAMPLES: + + Define a complex line bundle over a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='Lorentzian') + sage: X. = M.chart() + sage: E = M.vector_bundle(1, 'E', field='complex'); E + Differentiable complex vector bundle E -> M of rank 1 over the base + space 2-dimensional Lorentzian manifold M + sage: e = E.local_frame('e') + sage: nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E') + sage: omega = M.one_form(name='omega') + sage: A = function('A') + sage: nab.set_connection_form(0, 0)[1] = I*A(t) + sage: nab[0, 0].display() + connection (0,0) of bundle connection nabla^E w.r.t. Local frame + (E|_M, (e_0)) = I*A(t) dx + sage: cmat = [[nab.curvature_form(i, j, e) for j in E.irange()] + ....: for i in E.irange()] + + Import the algorithm and apply ``cmat`` to it:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import ChernAlgorithm + sage: algorithm = ChernAlgorithm() + sage: algorithm.get_local(cmat) + [2-form on the 2-dimensional Lorentzian manifold M] """ from sage.symbolic.constants import pi from sage.libs.pynac.pynac import I @@ -1381,6 +1422,57 @@ def get_local(self, cmat): class EulerAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Euler forms. + + EXAMPLES: + + Consider the 2-dimensional Euclidean space:: + + sage: M. = manifolds.Sphere(2, coordinates='stereographic') + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + + Import the algorithm and apply ``nab`` to it:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import EulerAlgorithm + sage: algorithm = EulerAlgorithm() + sage: algorithm.get(nab) + [2-form on the 2-sphere S^2 of radius 1 smoothly embedded in the + Euclidean space E^3] + sage: algorithm.get(nab)[0].display() + 2/(pi + pi*x^4 + pi*y^4 + 2*pi*x^2 + 2*(pi + pi*x^2)*y^2) dx∧dy + + Get the local (normalized) Pfaffian w.r.t. spherical coordinates:: + + sage: spher.<ϑ,ϕ> = M.spherical_coordinates() + sage: cmat = [[nab.curvature_form(i, j, spher.frame()) + ....: for j in TM.irange()] + ....: for i in TM.irange()] + sage: [euler_spher] = algorithm.get_local(cmat) + sage: euler_spher.display() + + + Get the local (normalized) Pfaffian w.r.t. stereographic coordinates:: + + sage: stereoN = M.stereographic_coordinates() + sage: cmat = [[nab.curvature_form(i, j, stereoN.frame()) + ....: for j in TM.irange()] + ....: for i in TM.irange()] + sage: [euler_stereo] = algorithm.get_local(cmat) + + sage: euler_stereo.display() + 2/(pi + pi*x^4 + pi*y^4 + 2*pi*x^2 + 2*(pi + pi*x^2)*y^2) dx∧dy + + In general, the result of :meth:`get_local` does not yield the Euler form:: + + sage: W = spher.domain().intersection(stereoN.domain()) + sage: euler_stereo.restrict(W) == euler_spher.restrict(W) # should be False + False + + :: + + """ @cached_method def get(self, nab): @@ -1388,10 +1480,59 @@ def get(self, nab): Return the global characteristic forms of the generators w.r.t. a given connection. + INPUT: + + - a metric connection `\nabla` + OUTPUT: - a list containing the global characteristic Euler form + ALGORITHM: + + Assume that `\nabla` is compatible with the metric `g`, and let + `(s_1, \ldots, s_n)` be any oriented frame. Denote by + `G_s = (g(s_i, s_j))_{ij}` the metric tensor and let + `\Omega_s` be the curvature form matrix of `\nabla` w.r.t. `s`. + Then, we get: + + .. MATH:: + + \left(G_s \cdot \Omega_s \right)_{ij} = g\!\left(R(.,.)s_i, s_j\right), + + where `R` is the Riemannian curvature tensor w.r.t. `\nabla`. + + The characteristic Euler form is now obtained by the expression + + .. MATH:: + + \frac{1}{\sqrt{\left|\det(G_s)\right|}} \ + \mathrm{Pf}\!\left(G_s \cdot \frac{\Omega_s}{2 \pi}\right). + + EXAMPLES: + + Consider the 2-sphere:: + + sage: M. = manifolds.Sphere(2, coordinates='stereographic') + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + + Import the algorithm and apply ``nab`` to it:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import EulerAlgorithm + sage: algorithm = EulerAlgorithm() + sage: algorithm.get(nab) + [2-form on the 2-sphere S^2 of radius 1 smoothly embedded in the + Euclidean space E^3] + sage: algorithm.get(nab)[0].display() + 2/(pi + pi*x^4 + pi*y^4 + 2*pi*x^2 + 2*(pi + pi*x^2)*y^2) dx∧dy + + REFERENCES: + + - [Che1944]_ + - [Baer2020]_ """ if not isinstance(nab, LeviCivitaConnection): raise TypeError('Euler forms are currently only supported for ' @@ -1436,13 +1577,35 @@ def get_local(self, cmat): .. NOTE:: - The result is the local Euler form if ``cmat`` is given w.r.t. an - orthonormal oriented frame. + In general, the output does *not* represent the local + characteristic Euler form. The result is only guaranteed to be the + local Euler form if ``cmat`` is given w.r.t. an orthonormal + oriented frame. See :meth:`get` for details. ALGORITHM:: The algorithm is based on the Bär-Faddeev-LeVerrier algorithm for the Pfaffian. + + EXAMPLES: + + Consider the 2-sphere:: + + sage: M.<ϑ,ϕ> = manifolds.Sphere(2) # use spherical coordinates + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: e = M.frames()[0] # select a frame + sage: cmat = [[nab.curvature_form(i, j, e) for j in TM.irange()] + ....: for i in TM.irange()] + + Import the algorithm and apply ``cmat`` to it:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import EulerAlgorithm + sage: algorithm = EulerAlgorithm() + sage: [euler] = algorithm.get_local(cmat) + sage: euler.display() + """ from sage.symbolic.constants import pi From e8d8393314a45980c25d110734a57ac4bda64191 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sat, 11 Sep 2021 22:47:10 +0200 Subject: [PATCH 41/50] Trac #29581: correct doc for EulerAlgorithm --- .../characteristic_cohomology_class.py | 53 +++++-------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index ee158b8d3bf..c04c73baff2 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1427,7 +1427,7 @@ class EulerAlgorithm(Singleton, Algorithm_generic): Consider the 2-dimensional Euclidean space:: - sage: M. = manifolds.Sphere(2, coordinates='stereographic') + sage: M = manifolds.EuclideanSpace(2) sage: TM = M.tangent_bundle() sage: g = M.metric() sage: nab = g.connection() @@ -1438,41 +1438,9 @@ class EulerAlgorithm(Singleton, Algorithm_generic): sage: from sage.manifolds.differentiable.characteristic_cohomology_class import EulerAlgorithm sage: algorithm = EulerAlgorithm() sage: algorithm.get(nab) - [2-form on the 2-sphere S^2 of radius 1 smoothly embedded in the - Euclidean space E^3] + [2-form on the Euclidean plane E^2] sage: algorithm.get(nab)[0].display() - 2/(pi + pi*x^4 + pi*y^4 + 2*pi*x^2 + 2*(pi + pi*x^2)*y^2) dx∧dy - - Get the local (normalized) Pfaffian w.r.t. spherical coordinates:: - - sage: spher.<ϑ,ϕ> = M.spherical_coordinates() - sage: cmat = [[nab.curvature_form(i, j, spher.frame()) - ....: for j in TM.irange()] - ....: for i in TM.irange()] - sage: [euler_spher] = algorithm.get_local(cmat) - sage: euler_spher.display() - - - Get the local (normalized) Pfaffian w.r.t. stereographic coordinates:: - - sage: stereoN = M.stereographic_coordinates() - sage: cmat = [[nab.curvature_form(i, j, stereoN.frame()) - ....: for j in TM.irange()] - ....: for i in TM.irange()] - sage: [euler_stereo] = algorithm.get_local(cmat) - - sage: euler_stereo.display() - 2/(pi + pi*x^4 + pi*y^4 + 2*pi*x^2 + 2*(pi + pi*x^2)*y^2) dx∧dy - - In general, the result of :meth:`get_local` does not yield the Euler form:: - - sage: W = spher.domain().intersection(stereoN.domain()) - sage: euler_stereo.restrict(W) == euler_spher.restrict(W) # should be False - False - - :: - - + 0 """ @cached_method def get(self, nab): @@ -1595,17 +1563,24 @@ def get_local(self, cmat): sage: TM = M.tangent_bundle() sage: g = M.metric() sage: nab = g.connection() - sage: e = M.frames()[0] # select a frame + sage: e = M.frames()[0] # select a frame (opposite orientation!) sage: cmat = [[nab.curvature_form(i, j, e) for j in TM.irange()] ....: for i in TM.irange()] - Import the algorithm and apply ``cmat`` to it:: + We need some preprocessing because the frame is not orthonormal:: + + sage: gcmat = [[sum(g[[e, i, j]] * nab.curvature_form(j, k, e) + ....: for j in TM.irange()) + ....: for k in TM.irange()] for i in TM.irange()] + + Now, ``gcmat`` is guaranteed to be skew-symmetric and can be applied + to :meth:`get_local`. Then, the result must be normalized:: sage: from sage.manifolds.differentiable.characteristic_cohomology_class import EulerAlgorithm sage: algorithm = EulerAlgorithm() - sage: [euler] = algorithm.get_local(cmat) + sage: euler = -algorithm.get_local(gcmat)[0] / sqrt(g.det(frame=e)) sage: euler.display() - + 1/2*sin(ϑ)/pi dϑ∧dϕ """ from sage.symbolic.constants import pi From 974c3cc866d51d884de7b666335a8c5d36517666 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 12 Sep 2021 12:23:06 +0200 Subject: [PATCH 42/50] Trac #29851: finish doc so far --- .../characteristic_cohomology_class.py | 192 +++++++++++++++++- 1 file changed, 187 insertions(+), 5 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index c04c73baff2..c2245e50dcf 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1202,6 +1202,19 @@ class Algorithm_generic(SageObject): r""" Abstract algorithm class to compute the characteristic forms of the generators. + + EXAMPLES:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import Algorithm_generic + sage: algorithm = Algorithm_generic() + sage: algorithm.get + Cached version of + sage: algorithm.get_local + Traceback (most recent call last): + ... + NotImplementedError: + sage: algorithm.get_gen_pow + Cached version of """ @cached_method def get(self, nab): @@ -1217,9 +1230,22 @@ def get(self, nab): instances of :class:`sage.manifolds.differentiable.diff_form.DiffForm` - EXAMPLES: + EXAMPLES:: + + sage: M = manifolds.EuclideanSpace(4) + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() - sage: + :: + + sage: p = TM.characteristic_cohomology_class('Pontryagin') + sage: p_form = p.get_form(nab); p_form + Mixed differential form p(TE^4, nabla_g) on the 4-dimensional + Euclidean space E^4 + sage: p_form.display_expansion() + p(TE^4, nabla_g) = 1 """ if isinstance(nab, AffineConnection): vbundle = nab._domain.tangent_bundle() @@ -1255,6 +1281,26 @@ def get_local(self, cmat): ALGORITHM: The inherited class determines the algorithm. + + EXAMPLES:: + + 4-dimensional Euclidean space:: + + sage: M = manifolds.EuclideanSpace(4) + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: e = M.frames()[0] # select standard frame + sage: cmat = [[nab.curvature_form(i, j, e) for j in TM.irange()] + ....: for i in TM.irange()] + + Import the algorithm:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginAlgorithm + sage: algorithm = PontryaginAlgorithm() + sage: [p1] = algorithm.get_local(cmat) + sage: p1.display() + 0 """ pass @@ -1265,6 +1311,23 @@ def get_gen_pow(self, nab, i, n): w.r.t ``nab``. The result is cached. + + EXAMPLES:: + + sage: M = manifolds.EuclideanSpace(8) + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + + :: + + sage: A = TM.characteristic_cohomology_class('AHat') + sage: A_form = A.get_form(nab); A_form + Mixed differential form A^(TE^8, nabla_g) on the 8-dimensional + Euclidean space E^8 + sage: A_form.display_expansion() + A^(TE^8, nabla_g) = 1 """ if n == 0: return nab._domain._one_scalar_field # no computation necessary @@ -1380,6 +1443,23 @@ def get_local(self, cmat): class PontryaginAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Pontryagin forms. + + EXAMPLES: + + 5-dimensional Euclidean space:: + + sage: M = manifolds.EuclideanSpace(5) + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + + Import the algorithm:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginAlgorithm + sage: algorithm = PontryaginAlgorithm() + sage: [p1] = algorithm.get(nab) + sage: p1.display() + 0 """ def get_local(self, cmat): r""" @@ -1393,6 +1473,26 @@ def get_local(self, cmat): The algorithm is based on the Faddeev-LeVerrier algorithm for the characteristic polynomial. + + EXAMPLES: + + 5-dimensional Euclidean space:: + + sage: M = manifolds.EuclideanSpace(5) + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: e = M.frames()[0] # select standard frame + sage: cmat = [[nab.curvature_form(i, j, e) for j in TM.irange()] + ....: for i in TM.irange()] + + Import the algorithm:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginAlgorithm + sage: algorithm = PontryaginAlgorithm() + sage: [p1] = algorithm.get_local(cmat) + sage: p1.display() + 0 """ from sage.symbolic.constants import pi @@ -1428,7 +1528,6 @@ class EulerAlgorithm(Singleton, Algorithm_generic): Consider the 2-dimensional Euclidean space:: sage: M = manifolds.EuclideanSpace(2) - sage: TM = M.tangent_bundle() sage: g = M.metric() sage: nab = g.connection() sage: nab.set_immutable() @@ -1482,7 +1581,6 @@ def get(self, nab): Consider the 2-sphere:: sage: M. = manifolds.Sphere(2, coordinates='stereographic') - sage: TM = M.tangent_bundle() sage: g = M.metric() sage: nab = g.connection() sage: nab.set_immutable() @@ -1563,7 +1661,7 @@ def get_local(self, cmat): sage: TM = M.tangent_bundle() sage: g = M.metric() sage: nab = g.connection() - sage: e = M.frames()[0] # select a frame (opposite orientation!) + sage: e = M.frames()[0] # select frame (opposite orientation!) sage: cmat = [[nab.curvature_form(i, j, e) for j in TM.irange()] ....: for i in TM.irange()] @@ -1605,6 +1703,25 @@ def get_local(self, cmat): class PontryaginEulerAlgorithm(Singleton, Algorithm_generic): r""" Algorithm class to generate Euler and Pontryagin forms. + + EXAMPLES: + + 6-dimensional Euclidean space:: + + sage: M = manifolds.EuclideanSpace(6) + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + + Import the algorithm:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginEulerAlgorithm + sage: algorithm = PontryaginEulerAlgorithm() + sage: e, p1 = algorithm.get(nab) + sage: e.display() + 0 + sage: p1.display() + 0 """ @cached_method @@ -1618,6 +1735,22 @@ def get(self, nab): - a list containing the global Euler form in the first entry, and the global Pontryagin forms in the remaining entries. + EXAMPLES: + + 4-dimensional Euclidean space:: + + sage: M = manifolds.EuclideanSpace(4) + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + + Import the algorithm:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginEulerAlgorithm + sage: algorithm = PontryaginEulerAlgorithm() + sage: algorithm.get(nab) + [4-form on the 4-dimensional Euclidean space E^4, + 4-form on the 4-dimensional Euclidean space E^4] """ return EulerAlgorithm().get(nab) + PontryaginAlgorithm().get(nab) @@ -1626,11 +1759,39 @@ def get_local(self, cmat): Return the local Euler and Pontryagin forms w.r.t. a given curvature form matrix. + .. NOTE:: + + Similar as for :class:`EulerAlgorithm`, the first entry only + represents the Euler form if the curvature form matrix is chosen + w.r.t. an orthonormal oriented frame. See + :meth:`EulerAlgorithm.get_local` for details. + OUTPUT: - a list containing the local Euler form in the first entry, and the local Pontryagin forms in the remaining entries. + EXAMPLES: + + 6-dimensional Euclidean space:: + + sage: M = manifolds.EuclideanSpace(6) + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: e = M.frames()[0] # select the standard frame + sage: cmat = [[nab.curvature_form(i, j, e) for j in TM.irange()] + ....: for i in TM.irange()] + + Import the algorithm:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginEulerAlgorithm + sage: algorithm = PontryaginEulerAlgorithm() + sage: e, p1 = algorithm.get_local(cmat) + sage: e.display() + 0 + sage: p1.display() + 0 """ res = EulerAlgorithm().get_local(cmat) # first entry is Euler class res += PontryaginAlgorithm().get_local(cmat) # rest Pontryagin @@ -1640,6 +1801,27 @@ def get_local(self, cmat): def get_gen_pow(self, nab, i, n): r""" Return the `n`-th power of the `i`-th generator w.r.t ``nab``. + + The result is cached. + + EXAMPLES: + + 4-dimensional Euclidean space:: + + sage: M = manifolds.EuclideanSpace(8) + sage: TM = M.tangent_bundle() + sage: g = M.metric() + sage: nab = g.connection() + sage: nab.set_immutable() + + Import the algorithm:: + + sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginEulerAlgorithm + sage: algorithm = PontryaginEulerAlgorithm() + sage: e = algorithm.get_gen_pow(nab, 0, 1) # Euler + sage: p1_pow2 = algorithm.get_gen_pow(nab, 1, 2) # 1st Pontryagin + sage: p1_pow2 == e + True """ if n == 0: return nab._domain._one_scalar_field # no computation necessary From 5830550c072e7ba16215dfe80e3985cee9e7d320 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 12 Sep 2021 21:59:51 +0200 Subject: [PATCH 43/50] Trac #29581: fix :: mistake and add Singleton example --- .../differentiable/characteristic_cohomology_class.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index c2245e50dcf..a3382c791c8 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1282,7 +1282,7 @@ def get_local(self, cmat): The inherited class determines the algorithm. - EXAMPLES:: + EXAMPLES: 4-dimensional Euclidean space:: @@ -1301,6 +1301,12 @@ def get_local(self, cmat): sage: [p1] = algorithm.get_local(cmat) sage: p1.display() 0 + + A concrete implementation is given by a + :class:`sage.misc.fast_methods.Singleton`:: + + sage: algorithm is PontryaginAlgorithm() + True """ pass From 9a73cad1f5c1504b62c1890b28cc12c753cf9b23 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Mon, 13 Sep 2021 14:26:48 +0200 Subject: [PATCH 44/50] Trac #29581: correct ideal in doc + add long time tags for 30s or more --- .../characteristic_cohomology_class.py | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index a3382c791c8..cfc4fa55f9c 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -635,7 +635,7 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): H^*(BG; R) \cong \begin{cases} R[c_1, \ldots c_k] & \text{if } G = U(k), \\ R[p_1, \ldots p_{\lfloor \frac{k}{2}\rfloor}] & \text{if } G = O(k), \\ - R[p_1, \ldots p_k, e] \big/ (p_k^2-e) & \text{if } G = SO(2k), \\ + R[p_1, \ldots p_k, e] \big/ (p_k-e^2) & \text{if } G = SO(2k), \\ R[p_1, \ldots p_k, e] & \text{if } G = SO(2k+1). \\ \end{cases} @@ -666,6 +666,13 @@ class CharacteristicCohomologyClassRing(FiniteGCAlgebra): the Chern-Weil homomorphism. This implementation attempts to represent this subring. + .. NOTE:: + + Some generators might have torsion in `H^*(BG; R)` giving a zero + element in the de Rham cohomology. Those generators are still + considered in the implementation. Generators whose degree exceed the + dimension of the base space, however, are ignored. + INPUT: - ``base`` -- base ring @@ -1329,10 +1336,10 @@ def get_gen_pow(self, nab, i, n): :: sage: A = TM.characteristic_cohomology_class('AHat') - sage: A_form = A.get_form(nab); A_form + sage: A_form = A.get_form(nab); A_form # long time Mixed differential form A^(TE^8, nabla_g) on the 8-dimensional Euclidean space E^8 - sage: A_form.display_expansion() + sage: A_form.display_expansion() # long time A^(TE^8, nabla_g) = 1 """ if n == 0: @@ -1723,10 +1730,10 @@ class PontryaginEulerAlgorithm(Singleton, Algorithm_generic): sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginEulerAlgorithm sage: algorithm = PontryaginEulerAlgorithm() - sage: e, p1 = algorithm.get(nab) - sage: e.display() + sage: e_form, p1_form = algorithm.get(nab) # long time + sage: e_form.display() # long time 0 - sage: p1.display() + sage: p1_form.display() # long time 0 """ @@ -1787,16 +1794,16 @@ def get_local(self, cmat): sage: nab = g.connection() sage: e = M.frames()[0] # select the standard frame sage: cmat = [[nab.curvature_form(i, j, e) for j in TM.irange()] - ....: for i in TM.irange()] + ....: for i in TM.irange()] # long time Import the algorithm:: sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginEulerAlgorithm sage: algorithm = PontryaginEulerAlgorithm() - sage: e, p1 = algorithm.get_local(cmat) - sage: e.display() + sage: e, p1 = algorithm.get_local(cmat) # long time + sage: e.display() # long time 0 - sage: p1.display() + sage: p1.display() # long time 0 """ res = EulerAlgorithm().get_local(cmat) # first entry is Euler class @@ -1814,7 +1821,7 @@ def get_gen_pow(self, nab, i, n): 4-dimensional Euclidean space:: - sage: M = manifolds.EuclideanSpace(8) + sage: M = manifolds.EuclideanSpace(4) sage: TM = M.tangent_bundle() sage: g = M.metric() sage: nab = g.connection() @@ -1825,9 +1832,11 @@ def get_gen_pow(self, nab, i, n): sage: from sage.manifolds.differentiable.characteristic_cohomology_class import PontryaginEulerAlgorithm sage: algorithm = PontryaginEulerAlgorithm() sage: e = algorithm.get_gen_pow(nab, 0, 1) # Euler - sage: p1_pow2 = algorithm.get_gen_pow(nab, 1, 2) # 1st Pontryagin - sage: p1_pow2 == e - True + sage: e.display() + 0 + sage: p1_pow2 = algorithm.get_gen_pow(nab, 1, 2) # 1st Pontryagin squared + sage: p1_pow2 + 8-form zero on the 4-dimensional Euclidean space E^4 """ if n == 0: return nab._domain._one_scalar_field # no computation necessary From 4c1bc44977bbf506a415c9635bf884ae0519e229 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Mon, 13 Sep 2021 19:14:13 +0200 Subject: [PATCH 45/50] Trac #29581: remove f-string without placeholder --- .../manifolds/differentiable/characteristic_cohomology_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index cfc4fa55f9c..0459b64732a 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -767,7 +767,7 @@ def __init__(self, base, vbundle): f'field type {vbundle._field_type}') if not names or not degrees: - raise ValueError(f'cannot find any generators') + raise ValueError('cannot find any generators') names = tuple(names) # hashable degrees = tuple(degrees) # hashable From ec282634db5fbb61c7731ba8998c356814887f70 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 16 Sep 2021 14:26:07 +0200 Subject: [PATCH 46/50] Trac #29581: tie construuctor to ring --- .../characteristic_cohomology_class.py | 435 +++++++++--------- .../manifolds/differentiable/vector_bundle.py | 6 +- 2 files changed, 222 insertions(+), 219 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 0459b64732a..4b9d99c7e15 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -282,12 +282,14 @@ from sage.combinat.free_module import IndexedFreeModuleElement from sage.misc.fast_methods import Singleton from sage.structure.sage_object import SageObject -from sage.misc.cachefunc import cached_method, cached_function +from sage.misc.cachefunc import cached_method from sage.misc.abstract_method import abstract_method from .affine_connection import AffineConnection from .bundle_connection import BundleConnection from .levi_civita_connection import LeviCivitaConnection -from sage.rings.rational_field import QQ +from sage.symbolic.expression import Expression +from sage.rings.polynomial.polynomial_element import is_Polynomial +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing class CharacteristicCohomologyClassRingElement(IndexedFreeModuleElement): @@ -775,7 +777,7 @@ def __init__(self, base, vbundle): max_degree=dim, mul_symbol='⌣', mul_latex_symbol=r'\smile') - def _element_constructor_(self, x, name=None, latex_name=None): + def _element_constructor_(self, x, **kwargs): r""" Convert ``x`` into ``self``. @@ -784,11 +786,16 @@ def _element_constructor_(self, x, name=None, latex_name=None): sage: M = Manifold(8, 'M') sage: TM = M.tangent_bundle() sage: CR = TM.characteristic_cohomology_class_ring() - sage: p = TM.characteristic_cohomology_class('Pontryagin') + sage: p = CR('Pontryagin'); p + Characteristic cohomology class p(TM) of the Tangent bundle TM over + the 8-dimensional differentiable manifold M sage: CR(p, name='pontr') Characteristic cohomology class pontr(TM) of the Tangent bundle TM over the 8-dimensional differentiable manifold M """ + if isinstance(x, (str, Expression)) or is_Polynomial(x): + return self._build_element(x, **kwargs) + R = self.base_ring() if x in R: @@ -808,9 +815,216 @@ def _element_constructor_(self, x, name=None, latex_name=None): else: raise TypeError(f"do not know how to make x (= {x}) " f"an element of self (={self})") - + name, latex_name = kwargs.get('name'), kwargs.get('latex_name') return self.element_class(self, d, name=name, latex_name=latex_name) + @cached_method + def _build_element(self, *args, **kwargs): + r""" + Construct a characteristic cohomology class. + + The result is cached. + + INPUT: + + - ``val`` -- the input data corresponding to the characteristic class + using the Chern-Weil homomorphism; this argument can be either a + symbolic expression, a polynomial or one of the following predefined + classes: + + - ``'Chern'`` -- total Chern class, + - ``'ChernChar'`` -- Chern character, + - ``'Todd'`` -- Todd class, + - ``'Pontryagin'`` -- total Pontryagin class, + - ``'Hirzebruch'`` -- Hirzebruch class, + - ``'AHat'`` -- `\hat{A}` class, + - ``'Euler'`` -- Euler class. + + - ``name`` -- (default: ``None``) string representation given to the + characteristic class; if ``None`` the default algebra representation or + predefined name is used + - ``latex_name`` -- (default: ``None``) LaTeX name given to the + characteristic class; if ``None`` the value of ``name`` is used + - ``class_type`` -- (default: ``None``) class type of the characteristic + cohomology class; the following options are possible: + + - ``'multiplicative'`` -- returns a class of multiplicative type + - ``'additive'`` -- returns a class of additive type + - ``'Pfaffian'`` -- returns a class of Pfaffian type + + This argument must be stated if ``val`` is a polynomial or symbolic + expression. + + EXAMPLES: + + Total Pontryagin class of an 8-dimensional manifold:: + + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: p = TM.characteristic_cohomology_class('Pontryagin'); p + Characteristic cohomology class p(TM) of the Tangent bundle TM over the + 8-dimensional differentiable manifold M + + Define a multiplicative class (see :func:`multiplicative_sequence`):: + + sage: P. = PolynomialRing(QQ) + sage: f = 1 + x - x^2 + sage: f_class = TM.characteristic_cohomology_class(f, class_type='multiplicative'); f_class + Characteristic cohomology class (1 + p_1 - p_1^2 + 3*p_2)(TM) of the + Tangent bundle TM over the 8-dimensional differentiable manifold M + + Pass a symbolic expression, whose Taylor expansion at zero will be used:: + + sage: M = Manifold(8, 'M') + sage: TM = M.tangent_bundle() + sage: x = var('x') + sage: f = cos(x) + sage: f_class = TM.characteristic_cohomology_class(f, class_type='multiplicative'); f_class + Characteristic cohomology class (1 - 1/2*p_1^2 + p_2)(TM) of the Tangent + bundle TM over the 8-dimensional differentiable manifold M + """ + name, latex_name = kwargs.get('name'), kwargs.get('latex_name') + base_ring = self.base_ring() + class_type = kwargs.get('class_type') + vbundle = self._vbundle + val = args[0] + dim = vbundle._base_space._dim + + # predefined classes accessible via class names + if isinstance(val, str): + from sage.arith.misc import factorial, bernoulli + + P = PolynomialRing(base_ring, 'x') + x = P.gen() + if val == 'Chern': + if vbundle._field_type != 'complex': + raise ValueError( + f'total Chern class not defined on {vbundle}') + if name is None: + name = 'c' + class_type = 'multiplicative' + val = 1 + x + if val == 'Pontryagin': + if vbundle._field_type != 'real': + raise ValueError( + f'total Pontryagin class not defined on {vbundle}') + if name is None: + name = 'p' + class_type = 'multiplicative' + val = 1 + x + elif val == 'ChernChar': + if vbundle._field_type != 'complex': + raise ValueError( + f'Chern character not defined on {vbundle}') + if name is None: + name = 'ch' + if latex_name is None: + latex_name = r'\mathrm{ch}' + class_type = 'additive' + coeff = [1 / factorial(k) for k in + range(dim // 2 + 1)] # exp(x) + val = P(coeff) + elif val == 'Todd': + if vbundle._field_type != 'complex': + raise ValueError(f'Todd class not defined on {vbundle}') + if name is None: + name = 'Td' + if latex_name is None: + latex_name = r'\mathrm{Td}' + class_type = 'multiplicative' + val = 1 + x / 2 + for k in range(1, dim // 2 + 1): + val += (-1) ** (k + 1) / factorial(2 * k) * bernoulli( + 2 * k) * x ** (2 * k) + elif val == 'Hirzebruch': + if vbundle._field_type != 'real': + raise ValueError( + f'Hirzebruch class not defined on {vbundle}') + if name is None: + name = 'L' + if latex_name is None: + latex_name = 'L' + class_type = 'multiplicative' + coeff = [2 ** (2 * k) * bernoulli(2 * k) / factorial(2 * k) + for k in range(dim // 4 + 1)] + val = P(coeff) + elif val == 'AHat': + if vbundle._field_type != 'real': + raise ValueError(f'AHat class not defined on {vbundle}') + if name is None: + name = 'A^' + if latex_name is None: + latex_name = r'\hat{A}' + class_type = 'multiplicative' + coeff = [- (2 ** (2 * k) - 2) / 2 ** (2 * k) * bernoulli( + 2 * k) / factorial(2 * k) + for k in range(dim // 4 + 1)] + val = P(coeff) + elif val == 'Euler': + if vbundle._field_type != 'real' or not vbundle.has_orientation(): + raise ValueError(f'Euler class not defined on {vbundle}') + if name is None: + name = 'e' + class_type = 'Pfaffian' + val = x + else: + ValueError(f'predefined class "{val}" unknown') + + # turn symbolic expression into a polynomial via Taylor expansion + if isinstance(val, Expression): + x = val.default_variable() + P = PolynomialRing(base_ring, x) + + if vbundle._field_type == 'real': + pow_range = dim // 4 + elif vbundle._field_type == 'complex': + pow_range = dim // 2 + else: + ValueError(f'field type of {vbundle} must be real or complex') + + val = P(val.taylor(x, 0, pow_range)) + + # turn polynomial into a characteristic cohomology class via sequences + if is_Polynomial(val): + if class_type is None: + raise TypeError(f'class_type must be stated if {val} ' + f'is a polynomial') + n = self.ngens() + s = 0 # shift; important in case of Euler class generator + if self._algorithm is PontryaginEulerAlgorithm(): + s = 1 # skip Euler class + n -= 1 # ignore Euler class + + if class_type == 'additive': + sym = additive_sequence(val, vbundle._rank, n) + elif class_type == 'multiplicative': + sym = multiplicative_sequence(val, n) + elif class_type == 'Pfaffian': + P = val.parent() + x = P.gen() + val = (val(x) - val(-x)) / 2 # project to odd functions + val = P([(-1) ** k * val[2 * k + 1] for k in range(n + 1)]) + sym = multiplicative_sequence(val, n) + else: + AttributeError('unkown class type') + + d = {} + w_vec = self._weighted_vectors + for p, c in sym: + vec = [0] * self.ngens() + if class_type == 'Pfaffian': + vec[0] = 1 # always multiply with e + for i in p: + vec[i - 1 + s] += 1 + key = w_vec(vec) + d[key] = c + res = self._from_dict(d) + res.set_name(name=name, latex_name=latex_name) + return res + + # Nothing worked? Then something went wrong! + raise ValueError(f'cannot convert {val} into an element of {self}') + def _repr_(self): r""" String representation of the object. @@ -947,217 +1161,6 @@ def additive_sequence(q, k, n=None): mon_pol = m._from_dict(m_dict) return Sym.e()(mon_pol) -@cached_function -def CharacteristicCohomologyClass(*args, **kwargs): - r""" - Construct a characteristic cohomology class. - - The result is cached. - - INPUT: - - - ``vbundle`` -- the vector bundle over which the characteristic - cohomology class shall be defined; this argument is skipped when invoked - directly from the vector bundle (see - :meth:`sage.manifolds.differentiable.vector_bundle.characteristic_cohomology_class`) - - ``val`` -- the input data corresponding to the characteristic class - using the Chern-Weil homomorphism; this argument can be either a - symbolic expression, a polynomial or one of the following predefined - classes: - - - ``'Chern'`` -- total Chern class, - - ``'ChernChar'`` -- Chern character, - - ``'Todd'`` -- Todd class, - - ``'Pontryagin'`` -- total Pontryagin class, - - ``'Hirzebruch'`` -- Hirzebruch class, - - ``'AHat'`` -- `\hat{A}` class, - - ``'Euler'`` -- Euler class. - - - ``base_ring`` -- (default: ``QQ``) base ring over which the - characteristic cohomology class ring shall be defined - - ``name`` -- (default: ``None``) string representation given to the - characteristic class; if ``None`` the default algebra representation or - predefined name is used - - ``latex_name`` -- (default: ``None``) LaTeX name given to the - characteristic class; if ``None`` the value of ``name`` is used - - ``class_type`` -- (default: ``None``) class type of the characteristic - cohomology class; the following options are possible: - - - ``'multiplicative'`` -- returns a class of multiplicative type - - ``'additive'`` -- returns a class of additive type - - ``'Pfaffian'`` -- returns a class of Pfaffian type - - This argument must be stated if ``val`` is a polynomial or symbolic - expression. - - EXAMPLES: - - Total Pontryagin class of an 8-dimensional manifold:: - - sage: M = Manifold(8, 'M') - sage: TM = M.tangent_bundle() - sage: p = TM.characteristic_cohomology_class('Pontryagin'); p - Characteristic cohomology class p(TM) of the Tangent bundle TM over the - 8-dimensional differentiable manifold M - - Define a multiplicative class (see :func:`multiplicative_sequence`):: - - sage: P. = PolynomialRing(QQ) - sage: f = 1 + x - x^2 - sage: f_class = TM.characteristic_cohomology_class(f, class_type='multiplicative'); f_class - Characteristic cohomology class (1 + p_1 - p_1^2 + 3*p_2)(TM) of the - Tangent bundle TM over the 8-dimensional differentiable manifold M - - Pass a symbolic expression, whose Taylor expansion at zero will be used:: - - sage: M = Manifold(8, 'M') - sage: TM = M.tangent_bundle() - sage: x = var('x') - sage: f = cos(x) - sage: f_class = TM.characteristic_cohomology_class(f, class_type='multiplicative'); f_class - Characteristic cohomology class (1 - 1/2*p_1^2 + p_2)(TM) of the Tangent - bundle TM over the 8-dimensional differentiable manifold M - """ - from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - from sage.symbolic.expression import Expression - - name, latex_name = kwargs.get('name'), kwargs.get('latex_name') - base_ring = kwargs.get('base_ring', QQ) - class_type = kwargs.get('class_type') - vbundle = args[0] - val = args[1] # input value - R = CharacteristicCohomologyClassRing(base_ring, vbundle) - dim = vbundle._base_space._dim - - # predefined classes accessible via class names - if isinstance(val, str): - from sage.arith.misc import factorial, bernoulli - - P = PolynomialRing(base_ring, 'x') - x = P.gen() - if val == 'Chern': - if vbundle._field_type != 'complex': - raise ValueError(f'total Chern class not defined on {vbundle}') - if name is None: - name = 'c' - class_type = 'multiplicative' - val = 1 + x - if val == 'Pontryagin': - if vbundle._field_type != 'real': - raise ValueError(f'total Pontryagin class not defined on {vbundle}') - if name is None: - name = 'p' - class_type = 'multiplicative' - val = 1 + x - elif val == 'ChernChar': - if vbundle._field_type != 'complex': - raise ValueError(f'Chern character not defined on {vbundle}') - if name is None: - name = 'ch' - if latex_name is None: - latex_name = r'\mathrm{ch}' - class_type = 'additive' - coeff = [1 / factorial(k) for k in range(dim // 2 + 1)] # exp(x) - val = P(coeff) - elif val == 'Todd': - if vbundle._field_type != 'complex': - raise ValueError(f'Todd class not defined on {vbundle}') - if name is None: - name = 'Td' - if latex_name is None: - latex_name = r'\mathrm{Td}' - class_type = 'multiplicative' - val = 1 + x / 2 - for k in range(1, dim // 2 + 1): - val += (-1)**(k+1) / factorial(2*k) * bernoulli(2*k) * x**(2*k) - elif val == 'Hirzebruch': - if vbundle._field_type != 'real': - raise ValueError(f'Hirzebruch class not defined on {vbundle}') - if name is None: - name = 'L' - if latex_name is None: - latex_name = 'L' - class_type = 'multiplicative' - coeff = [2**(2*k) * bernoulli(2*k) / factorial(2*k) - for k in range(dim // 4 + 1)] - val = P(coeff) - elif val == 'AHat': - if vbundle._field_type != 'real': - raise ValueError(f'AHat class not defined on {vbundle}') - if name is None: - name = 'A^' - if latex_name is None: - latex_name = r'\hat{A}' - class_type = 'multiplicative' - coeff = [- (2**(2*k) - 2) / 2**(2*k) * bernoulli(2*k) / factorial(2*k) - for k in range(dim // 4 + 1)] - val = P(coeff) - elif val == 'Euler': - if vbundle._field_type != 'real' or not vbundle.has_orientation(): - raise ValueError(f'Euler class not defined on {vbundle}') - if name is None: - name = 'e' - class_type = 'Pfaffian' - val = x - else: - ValueError(f'predefined class "{val}" unknown') - - # turn symbolic expression into a polynomial via Taylor expansion - if isinstance(val, Expression): - x = val.default_variable() - P = PolynomialRing(base_ring, x) - - if vbundle._field_type == 'real': - pow_range = dim // 4 - elif vbundle._field_type == 'complex': - pow_range = dim // 2 - else: - ValueError(f'field type of {vbundle} must be real or complex') - - val = P(val.taylor(x, 0, pow_range)) - - # turn polynomial into a characteristic cohomology class via sequences - if is_PolynomialRing(val.parent()): - if class_type is None: - raise TypeError(f'class_type must be stated if {val} ' - f'is a polynomial') - n = R.ngens() - s = 0 # shift; important in case of Euler class generator - if R._algorithm is PontryaginEulerAlgorithm(): - s = 1 # skip Euler class - n -= 1 # ignore Euler class - - if class_type == 'additive': - sym = additive_sequence(val, vbundle._rank, n) - elif class_type == 'multiplicative': - sym = multiplicative_sequence(val, n) - elif class_type == 'Pfaffian': - P = val.parent() - x = P.gen() - val = (val(x) - val(-x)) / 2 # project to odd functions - val = P([(-1)**k * val[2*k+1] for k in range(n + 1)]) - sym = multiplicative_sequence(val, n) - else: - AttributeError('unkown class type') - - d = {} - w_vec = R._weighted_vectors - for p, c in sym: - vec = [0] * R.ngens() - if class_type == 'Pfaffian': - vec[0] = 1 # always multiply with e - for i in p: - vec[i - 1 + s] += 1 - key = w_vec(vec) - d[key] = c - res = R._from_dict(d) - res.set_name(name=name, latex_name=latex_name) - return res - - # last resort: try coercion - return R(val, name=name, latex_name=latex_name) - def fast_wedge_power(form, n): r""" diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py index e0a2f4ee440..b23207a6d3d 100644 --- a/src/sage/manifolds/differentiable/vector_bundle.py +++ b/src/sage/manifolds/differentiable/vector_bundle.py @@ -274,9 +274,9 @@ def characteristic_cohomology_class(self, *args, **kwargs): :class:`~sage.manifolds.differentiable.characteristic_class.CharacteristicClass`. """ - from .characteristic_cohomology_class import CharacteristicCohomologyClass - - return CharacteristicCohomologyClass(self, *args, **kwargs) + base_ring = kwargs.get('base_ring', QQ) + R = self.characteristic_cohomology_class_ring(base_ring) + return R(*args, **kwargs) characteristic_class = deprecated_function_alias(29581, characteristic_cohomology_class) From c5370ec74eb85fd267c558ea8bad0e74512402b1 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 16 Sep 2021 23:35:10 +0200 Subject: [PATCH 47/50] Trac #29581: remove line breaks due to hard wrap in editor --- .../characteristic_cohomology_class.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 4b9d99c7e15..2bf2a2b8d77 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -898,24 +898,21 @@ def _build_element(self, *args, **kwargs): x = P.gen() if val == 'Chern': if vbundle._field_type != 'complex': - raise ValueError( - f'total Chern class not defined on {vbundle}') + raise ValueError(f'total Chern class not defined on {vbundle}') if name is None: name = 'c' class_type = 'multiplicative' val = 1 + x if val == 'Pontryagin': if vbundle._field_type != 'real': - raise ValueError( - f'total Pontryagin class not defined on {vbundle}') + raise ValueError(f'total Pontryagin class not defined on {vbundle}') if name is None: name = 'p' class_type = 'multiplicative' val = 1 + x elif val == 'ChernChar': if vbundle._field_type != 'complex': - raise ValueError( - f'Chern character not defined on {vbundle}') + raise ValueError(f'Chern character not defined on {vbundle}') if name is None: name = 'ch' if latex_name is None: @@ -938,8 +935,7 @@ def _build_element(self, *args, **kwargs): 2 * k) * x ** (2 * k) elif val == 'Hirzebruch': if vbundle._field_type != 'real': - raise ValueError( - f'Hirzebruch class not defined on {vbundle}') + raise ValueError(f'Hirzebruch class not defined on {vbundle}') if name is None: name = 'L' if latex_name is None: From 4e2e8b85ee02476a51574d1e04e87b0e6c0eaa00 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 29 Sep 2021 11:20:42 +0200 Subject: [PATCH 48/50] Trac #29581: new location of imaginary unit --- .../manifolds/differentiable/characteristic_cohomology_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 2bf2a2b8d77..9b0ffe0bca5 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1429,7 +1429,7 @@ def get_local(self, cmat): [2-form on the 2-dimensional Lorentzian manifold M] """ from sage.symbolic.constants import pi - from sage.libs.pynac.pynac import I + from sage.rings.imaginary_unit import I dom = cmat[0][0]._domain rk = len(cmat) From 44f8cfa8d960c1ec2db3a097f95728efcc86fcf4 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 29 Sep 2021 15:53:37 +0200 Subject: [PATCH 49/50] Trac #29581: import I from symbolic.constants --- .../differentiable/characteristic_cohomology_class.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 9b0ffe0bca5..8ac01c82d8b 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1428,8 +1428,7 @@ def get_local(self, cmat): sage: algorithm.get_local(cmat) [2-form on the 2-dimensional Lorentzian manifold M] """ - from sage.symbolic.constants import pi - from sage.rings.imaginary_unit import I + from sage.symbolic.constants import pi, I dom = cmat[0][0]._domain rk = len(cmat) From ac5cbbf0072380022c32466aab95fdfb31c4ecee Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Mon, 4 Oct 2021 00:08:28 +0200 Subject: [PATCH 50/50] Trac #29581: remove unicode character --- .../differentiable/characteristic_cohomology_class.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 8ac01c82d8b..e5c80062844 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -1668,7 +1668,7 @@ def get_local(self, cmat): Consider the 2-sphere:: - sage: M.<ϑ,ϕ> = manifolds.Sphere(2) # use spherical coordinates + sage: M. = manifolds.Sphere(2) # use spherical coordinates sage: TM = M.tangent_bundle() sage: g = M.metric() sage: nab = g.connection() @@ -1689,7 +1689,7 @@ def get_local(self, cmat): sage: algorithm = EulerAlgorithm() sage: euler = -algorithm.get_local(gcmat)[0] / sqrt(g.det(frame=e)) sage: euler.display() - 1/2*sin(ϑ)/pi dϑ∧dϕ + 1/2*sin(th)/pi dth∧dphi """ from sage.symbolic.constants import pi