From f3c1fd5e5e1954c2dca42df4cebdffb543f66d2f Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 26 Mar 2023 20:10:41 +0800 Subject: [PATCH 01/13] Add a few operators from linear symplectic geometry --- .../manifolds/differentiable/diff_form.py | 8 +-- src/sage/manifolds/differentiable/manifold.py | 5 +- .../differentiable/symplectic_form.py | 53 ++++++++++++++++ .../differentiable/symplectic_form_test.py | 60 +++++++++++++++++++ .../manifolds/differentiable/tensorfield.py | 27 ++++++++- .../differentiable/tensorfield_paral.py | 5 +- .../differentiable/vectorfield_module.py | 11 +++- src/sage/tensor/modules/free_module_tensor.py | 45 +++++++------- 8 files changed, 182 insertions(+), 32 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index ec6a8d353f3..fd07ac9f6f8 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -630,8 +630,8 @@ def hodge_dual( .. MATH:: - *A_{i_1\ldots i_{n-p}} = \frac{1}{p!} A_{k_1\ldots k_p} - \epsilon^{k_1\ldots k_p}_{\qquad\ i_1\ldots i_{n-p}} + *A_{i_1\ldots i_{n-p}} = \frac{1}{p!} A^{k_1\ldots k_p} + \epsilon_{k_1\ldots k_p\, i_1\ldots i_{n-p}} where `n` is the manifold's dimension, `\epsilon` is the volume `n`-form associated with `g` (see @@ -772,12 +772,12 @@ def hodge_dual( nondegenerate_tensor = self._vmodule._ambient_domain.metric() p = self.tensor_type()[1] - eps = nondegenerate_tensor.volume_form(p) + eps = nondegenerate_tensor.volume_form() if p == 0: common_domain = nondegenerate_tensor.domain().intersection(self.domain()) result = self.restrict(common_domain) * eps.restrict(common_domain) else: - result = self.contract(*range(p), eps, *range(p)) + result = self.up(nondegenerate_tensor).contract(*range(p), eps, *range(p)) if p > 1: result = result / factorial(p) result.set_name( diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index e674ad8db05..3a469e64f0c 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -454,6 +454,7 @@ if TYPE_CHECKING: from sage.manifolds.differentiable.diff_map import DiffMap + from sage.manifolds.differentiable.diff_form import DiffForm from sage.manifolds.differentiable.metric import PseudoRiemannianMetric from sage.manifolds.differentiable.vectorfield_module import ( VectorFieldFreeModule, @@ -2177,7 +2178,7 @@ def multivector_field(self, *args, **kwargs): resu._init_components(args[1], **kwargs) return resu - def diff_form(self, *args, **kwargs): + def diff_form(self, *args, **kwargs) -> DiffForm: r""" Define a differential form on ``self``. @@ -2281,7 +2282,7 @@ def diff_form(self, *args, **kwargs): resu._init_components(args[1], **kwargs) return resu - def one_form(self, *comp, **kwargs): + def one_form(self, *comp, **kwargs) -> DiffForm: r""" Define a 1-form on the manifold. diff --git a/src/sage/manifolds/differentiable/symplectic_form.py b/src/sage/manifolds/differentiable/symplectic_form.py index c72f50b70fa..983ce479b4c 100644 --- a/src/sage/manifolds/differentiable/symplectic_form.py +++ b/src/sage/manifolds/differentiable/symplectic_form.py @@ -588,6 +588,59 @@ def hodge_star(self, pform: DiffForm) -> DiffForm: """ return pform.hodge_dual(self) + def on_forms(self, first: DiffForm, second: DiffForm) -> DiffScalarField: + r""" + Return the contraction of the two forms with respect to the symplectic form. + + The symplectic form `\omega` gives rise to a bilinear form, + also denoted by `\omega` on the space of `1`-forms by + + .. MATH:: + \omega(\alpha, \beta) = \omega(\alpha^\sharp, \beta^\sharp), + + where `\alpha^\sharp` is the dual of `\alpha` with respect to `\omega`, see + :meth:`~sage.manifolds.differentiable.tensor_field.TensorField.up`. + This bilinear form induces a bilinear form on the space of all forms determined + by its value on decomposable elements as: + + .. MATH:: + \omega(\alpha_1 \wedge \ldots \wedge\alpha_p, \beta_1 \wedge \ldots \wedge\beta_p) + = det(\omega(\alpha_i, \beta_j)). + + INPUT: + + - ``first`` -- a `p`-form `\alpha` + - ``second`` -- a `p`-form `\beta` + + OUTPUT: + + - the scalar field `\omega(\alpha, \beta)` + + EXAMPLES: + + Contraction of two forms on the symplectic vector space `R^2`:: + + sage: M = manifolds.StandardSymplecticSpace(2) + sage: omega = M.symplectic_form() + sage: a = M.one_form(1, 0, name='a') + sage: b = M.one_form(0, 1, name='b') + sage: omega.on_forms(a, b).display() + omega(a, b): R2 → ℝ + (q, p) ↦ -1 + """ + from sage.arith.misc import factorial + + if first.degree() != second.degree(): + raise ValueError("the two forms must have the same degree") + + second_all_up = second + all_positions = range(first.degree()) + for k in all_positions: + second_all_up = second_all_up.up(self, k) + return first.contract( + *all_positions, second_all_up, *all_positions + ) / factorial(first.degree()) + class SymplecticFormParal(SymplecticForm, DiffFormParal): r""" diff --git a/src/sage/manifolds/differentiable/symplectic_form_test.py b/src/sage/manifolds/differentiable/symplectic_form_test.py index f33f59d5adb..79a0921b73a 100644 --- a/src/sage/manifolds/differentiable/symplectic_form_test.py +++ b/src/sage/manifolds/differentiable/symplectic_form_test.py @@ -1,3 +1,4 @@ +# pylint: disable=missing-function-docstring from _pytest.fixtures import FixtureRequest import pytest @@ -125,6 +126,46 @@ def test_poisson_bracket_as_commutator_hamiltonian_vector_fields( omega.hamiltonian_vector_field(g) ) == omega.hamiltonian_vector_field(omega.poisson_bracket(f, g)) + def test_hodge_star_of_one_is_volume( + self, M: DifferentiableManifold, omega: SymplecticForm + ): + assert M.one_scalar_field().hodge_dual(omega) == omega.volume_form() + + def test_hodge_star_of_volume_is_one( + self, M: DifferentiableManifold, omega: SymplecticForm + ): + assert omega.volume_form().hodge_dual(omega) == M.one_scalar_field() + ) + + def test_hodge_star_is_given_using_omega_on_forms( + self, M: DifferentiableManifold, omega: SymplecticForm + ): + a = M.one_form(1,2) + b = M.one_form(3,4) + assert a.wedge(b.hodge_dual(omega)) == omega.on_forms(a, b) * omega.volume_form() + + def test_trace_of_form_is_given_using_contraction_with_omega( + self, M: DifferentiableManifold, omega: SymplecticForm + ): + a = M.one_form(1,2) + assert a.trace(using=omega) == a.up(omega, 1).trace() + + def test_omega_on_forms_is_determinant_for_decomposables( + self, M: DifferentiableManifold, omega: SymplecticForm + ): + a = M.one_form(1,2) + b = M.one_form(3,4) + c = M.one_form(5,6) + d = M.one_form(7,8) + + assert omega.on_forms(a.wedge(b), c.wedge(d)) == omega.on_forms(a,c) * omega.on_forms(b, d) - omega.on_forms(a,d) * omega.on_forms(b,c) + + def test_omega_on_one_forms_is_omega_on_dual_vectors( + self, M: DifferentiableManifold, omega: SymplecticForm + ): + a = M.one_form(1,2) + b = M.one_form(3,4) + assert omega.on_forms(a, b) == omega(a.up(omega), b.up(omega)) def generic_scalar_field(M: DifferentiableManifold, name: str) -> DiffScalarField: chart_functions = {chart: function(name)(*chart[:]) for chart in M.atlas()} @@ -158,3 +199,22 @@ def test_flat(self, M: StandardSymplecticSpace, omega: SymplecticForm): X = M.vector_field(1, 2, name="X") assert str(X.display()) == r"X = e_q + 2 e_p" assert str(omega.flat(X).display()) == r"X_flat = 2 dq - dp" + + def test_hodge_star(self, M: StandardSymplecticSpace, omega: SymplecticForm): + # Standard basis + e = M.one_form(0,1, name='e') + f = M.one_form(1,0, name='f') + assert e.wedge(f) == omega + + assert M.one_scalar_field().hodge_dual(omega) == omega + assert e.hodge_dual(omega) == e + assert f.hodge_dual(omega) == f + assert omega.hodge_dual(omega) == M.one_scalar_field() + + def test_omega_on_one_forms(self, M: StandardSymplecticSpace, omega: SymplecticForm): + # Standard basis + e = M.one_form(0,1, name='e') + f = M.one_form(1,0, name='f') + assert e.wedge(f) == omega + + assert omega.on_forms(e, f) == 1 diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index db4a2a05aa1..93d2c499bdf 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -3030,10 +3030,20 @@ def __call__(self, *args): resu._latex_name = res_latex return resu - def trace(self, pos1=0, pos2=1): + def trace( + self, + pos1=0, + pos2=1, + using: Optional[ + Union[PseudoRiemannianMetric, SymplecticForm, PoissonTensorField] + ] = None, + ): r""" Trace (contraction) on two slots of the tensor field. + If a non-degenerate form is provided, the trace of a `(0,2)` tensor field + is computed by first raising the last index. + INPUT: - ``pos1`` -- (default: 0) position of the first index for the @@ -3041,6 +3051,7 @@ def trace(self, pos1=0, pos2=1): - ``pos2`` -- (default: 1) position of the second index for the contraction, with the same convention as for ``pos1``. The variance type of ``pos2`` must be opposite to that of ``pos1`` + - ``using`` -- (default: ``None``) a non-degenerate form OUTPUT: @@ -3073,6 +3084,13 @@ def trace(self, pos1=0, pos2=1): sage: s == a.trace(0,1) # explicit mention of the positions True + The trace of a type-`(0,2)` tensor field using a metric:: + + sage: M = Manifold(2, 'M', start_index=1) + sage: g = M.metric('g') + sage: g[1,1], g[1,2], g[2,2] = 1, 0, 1 + sage: g.trace(using=g).display() + Instead of the explicit call to the method :meth:`trace`, one may use the index notation with Einstein convention (summation over repeated indices); it suffices to pass the indices as a string inside @@ -3139,6 +3157,13 @@ def trace(self, pos1=0, pos2=1): True """ + if using is not None: + if self.tensor_type() != (0, 2): + raise ValueError( + "trace with respect to a non-degenerate form is only defined for type-(0,2) tensor fields" + ) + return self.up(using, 1).trace() + # The indices at pos1 and pos2 must be of different types: k_con = self._tensor_type[0] l_cov = self._tensor_type[1] diff --git a/src/sage/manifolds/differentiable/tensorfield_paral.py b/src/sage/manifolds/differentiable/tensorfield_paral.py index eb517832382..ba14af62118 100644 --- a/src/sage/manifolds/differentiable/tensorfield_paral.py +++ b/src/sage/manifolds/differentiable/tensorfield_paral.py @@ -1590,8 +1590,9 @@ def restrict(self, subdomain: DifferentiableManifold, dest_map: Optional[DiffMap return self if subdomain not in self._restrictions: if not subdomain.is_subset(self._domain): - raise ValueError("the provided domain is not a subset of " + - "the field's domain") + raise ValueError( + f"the provided domain {subdomain} is not a subset of the field's domain {self._domain}" + ) if dest_map is None: dest_map = self._fmodule._dest_map.restrict(subdomain) elif not dest_map._codomain.is_subset(self._ambient_domain): diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 12060175278..10e766e4f30 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -45,7 +45,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Literal, Optional, overload from sage.categories.modules import Modules from sage.manifolds.differentiable.vectorfield import VectorField, VectorFieldParal @@ -57,6 +57,8 @@ from sage.tensor.modules.reflexive_module import ReflexiveModule_base if TYPE_CHECKING: + from sage.manifolds.differentiable.diff_form import DiffForm + from sage.manifolds.scalarfield import ScalarField from sage.manifolds.differentiable.diff_map import DiffMap from sage.manifolds.differentiable.manifold import DifferentiableManifold @@ -950,8 +952,13 @@ def alternating_contravariant_tensor(self, degree, name=None, latex_name=latex_name) return self.exterior_power(degree).element_class(self, degree, name=name, latex_name=latex_name) + @overload + def alternating_form( + self, degree: Literal[0], name=None, latex_name=None + ) -> ScalarField: + pass - def alternating_form(self, degree, name=None, latex_name=None): + def alternating_form(self, degree: int, name=None, latex_name=None) -> DiffForm: r""" Construct an alternating form on the vector field module ``self``. diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index a7865299688..2441d57b6e7 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -194,7 +194,7 @@ class being: # ***************************************************************************** from __future__ import annotations -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING, Dict, Optional, Union from sage.rings.integer import Integer from sage.structure.element import ModuleElementWithMutability @@ -207,6 +207,9 @@ class being: if TYPE_CHECKING: from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule from sage.tensor.modules.free_module_basis import FreeModuleBasis + from sage.manifolds.differentiable.metric import PseudoRiemannianMetric + from sage.manifolds.differentiable.poisson_tensor import PoissonTensorField + from sage.manifolds.differentiable.symplectic_form import SymplecticForm class FreeModuleTensor(ModuleElementWithMutability): @@ -2442,10 +2445,20 @@ def __call__(self, *args): res._latex_name = res_latex return res - def trace(self, pos1=0, pos2=1): + def trace( + self, + pos1: int = 0, + pos2: int = 1, + using: Optional[ + Union[PseudoRiemannianMetric, SymplecticForm, PoissonTensorField] + ] = None, + ): r""" Trace (contraction) on two slots of the tensor. + If a non-degenerate form is provided, the trace of a `(0,2)` tensor field + is computed by first raising the last index. + INPUT: - ``pos1`` -- (default: 0) position of the first index for the @@ -2455,6 +2468,8 @@ def trace(self, pos1=0, pos2=1): contraction, with the same convention as for ``pos1``; the variance type of ``pos2`` must be opposite to that of ``pos1`` + - ``using`` -- (default: ``None``) a non-degenerate form + OUTPUT: - tensor or scalar resulting from the ``(pos1, pos2)`` contraction @@ -2565,6 +2580,13 @@ def trace(self, pos1=0, pos2=1): True """ + if using is not None: + if self.tensor_type() != (0, 2): + raise ValueError( + "trace with respect to a non-degenerate form is only defined for type-(0,2) tensor fields" + ) + return self.up(using, 1).trace() + # The indices at pos1 and pos2 must be of different types: k_con = self._tensor_type[0] l_cov = self._tensor_type[1] @@ -2822,25 +2844,6 @@ def contract(self, *args): if self._tensor_rank + other._tensor_rank - 2*ncontr == 0: # Case of scalar output: return cmp_res - # - # Reordering of the indices to have all contravariant indices first: - # - nb_cov_s = 0 # Number of covariant indices of self not involved in the - # contraction - for pos in range(k1,k1+l1): - if pos not in pos1: - nb_cov_s += 1 - nb_con_o = 0 # Number of contravariant indices of other not involved - # in the contraction - for pos in range(0,k2): - if pos not in pos2: - nb_con_o += 1 - if nb_cov_s != 0 and nb_con_o != 0: - # some reordering is necessary: - p2 = k1 + l1 - ncontr - p1 = p2 - nb_cov_s - p3 = p2 + nb_con_o - cmp_res = cmp_res.swap_adjacent_indices(p1, p2, p3) type_res = (k1+k2-ncontr, l1+l2-ncontr) return self._fmodule.tensor_from_comp(type_res, cmp_res) From 40a9f45c6461d68b2502f98c54d0bf13d6200d8b Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 26 Mar 2023 20:30:41 +0800 Subject: [PATCH 02/13] fix tests --- src/sage/manifolds/differentiable/symplectic_form.py | 10 ++++------ src/sage/manifolds/differentiable/tensorfield.py | 6 ++++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/manifolds/differentiable/symplectic_form.py b/src/sage/manifolds/differentiable/symplectic_form.py index 983ce479b4c..cb3fb615a80 100644 --- a/src/sage/manifolds/differentiable/symplectic_form.py +++ b/src/sage/manifolds/differentiable/symplectic_form.py @@ -575,10 +575,10 @@ def hodge_star(self, pform: DiffForm) -> DiffForm: sage: omega = M.symplectic_form() sage: a = M.one_form(1, 0, name='a') sage: omega.hodge_star(a).display() - *a = -dq + *a = dq sage: b = M.one_form(0, 1, name='b') sage: omega.hodge_star(b).display() - *b = -dp + *b = dp sage: f = M.scalar_field(1, name='f') sage: omega.hodge_star(f).display() *f = -dq∧dp @@ -618,15 +618,13 @@ def on_forms(self, first: DiffForm, second: DiffForm) -> DiffScalarField: EXAMPLES: - Contraction of two forms on the symplectic vector space `R^2`:: - sage: M = manifolds.StandardSymplecticSpace(2) sage: omega = M.symplectic_form() sage: a = M.one_form(1, 0, name='a') sage: b = M.one_form(0, 1, name='b') sage: omega.on_forms(a, b).display() - omega(a, b): R2 → ℝ - (q, p) ↦ -1 + R2 → ℝ + (q, p) ↦ -1 """ from sage.arith.misc import factorial diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 93d2c499bdf..5bee8261d2e 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -3086,10 +3086,12 @@ def trace( The trace of a type-`(0,2)` tensor field using a metric:: - sage: M = Manifold(2, 'M', start_index=1) sage: g = M.metric('g') - sage: g[1,1], g[1,2], g[2,2] = 1, 0, 1 + sage: g[0,0], g[0,1], g[1,1] = 1, 0, 1 sage: g.trace(using=g).display() + M → ℝ + on U: (x, y) ↦ 2 + on W: (u, v) ↦ 2 Instead of the explicit call to the method :meth:`trace`, one may use the index notation with Einstein convention (summation over From fe7ac4e7b0d6037761f448a49d5fa6adc761018f Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 26 Mar 2023 21:04:07 +0800 Subject: [PATCH 03/13] fix pytests --- .../differentiable/symplectic_form.py | 5 +---- .../differentiable/symplectic_form_test.py | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/sage/manifolds/differentiable/symplectic_form.py b/src/sage/manifolds/differentiable/symplectic_form.py index cb3fb615a80..2807416e5a7 100644 --- a/src/sage/manifolds/differentiable/symplectic_form.py +++ b/src/sage/manifolds/differentiable/symplectic_form.py @@ -631,12 +631,9 @@ def on_forms(self, first: DiffForm, second: DiffForm) -> DiffScalarField: if first.degree() != second.degree(): raise ValueError("the two forms must have the same degree") - second_all_up = second all_positions = range(first.degree()) - for k in all_positions: - second_all_up = second_all_up.up(self, k) return first.contract( - *all_positions, second_all_up, *all_positions + *all_positions, second.up(self), *all_positions ) / factorial(first.degree()) diff --git a/src/sage/manifolds/differentiable/symplectic_form_test.py b/src/sage/manifolds/differentiable/symplectic_form_test.py index 79a0921b73a..110d96d8ea1 100644 --- a/src/sage/manifolds/differentiable/symplectic_form_test.py +++ b/src/sage/manifolds/differentiable/symplectic_form_test.py @@ -135,19 +135,12 @@ def test_hodge_star_of_volume_is_one( self, M: DifferentiableManifold, omega: SymplecticForm ): assert omega.volume_form().hodge_dual(omega) == M.one_scalar_field() - ) - def test_hodge_star_is_given_using_omega_on_forms( - self, M: DifferentiableManifold, omega: SymplecticForm - ): - a = M.one_form(1,2) - b = M.one_form(3,4) - assert a.wedge(b.hodge_dual(omega)) == omega.on_forms(a, b) * omega.volume_form() - - def test_trace_of_form_is_given_using_contraction_with_omega( + def test_trace_of_two_form_is_given_using_contraction_with_omega( self, M: DifferentiableManifold, omega: SymplecticForm ): - a = M.one_form(1,2) + a = M.diff_form(2) + a[1,2] = 3 assert a.trace(using=omega) == a.up(omega, 1).trace() def test_omega_on_forms_is_determinant_for_decomposables( @@ -218,3 +211,11 @@ def test_omega_on_one_forms(self, M: StandardSymplecticSpace, omega: SymplecticF assert e.wedge(f) == omega assert omega.on_forms(e, f) == 1 + + def test_hodge_star_is_given_using_omega_on_forms( + self, M: StandardSymplecticSpace, omega: SymplecticForm + ): + a = M.one_form(1,2) + b = M.one_form(3,4) + assert a.wedge(b.hodge_dual(omega)) == omega.on_forms(a, b) * omega.volume_form() + From 55dce0f91c27c16490f094c4527251a26fad4930 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 26 Mar 2023 21:26:49 +0800 Subject: [PATCH 04/13] Fix computation of hodge for pseudo-riemannian --- src/sage/manifolds/differentiable/diff_form.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index fd07ac9f6f8..a86d6f9b6c0 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -637,6 +637,9 @@ def hodge_dual( `n`-form associated with `g` (see :meth:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric.volume_form`) and the indices `k_1,\ldots, k_p` are raised with `g`. + If `g` is a pseudo-Riemannian metric, an additional multiplicative factor of + `(-1)^s` is introduced on the right-hand side, + where `s` is the number of negative eigenvalues of `g`. INPUT: @@ -780,6 +783,10 @@ def hodge_dual( result = self.up(nondegenerate_tensor).contract(*range(p), eps, *range(p)) if p > 1: result = result / factorial(p) + from sage.manifolds.differentiable.metric import PseudoRiemannianMetric + if isinstance(nondegenerate_tensor, PseudoRiemannianMetric): + result = result * nondegenerate_tensor._indic_signat + result.set_name( name=format_unop_txt("*", self._name), latex_name=format_unop_latex(r"\star ", self._latex_name), From 6d11b39dde7aef7d77e537c3b55f1248ea620ac7 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 26 Mar 2023 21:29:13 +0800 Subject: [PATCH 05/13] readd accidentially deleted code --- src/sage/tensor/modules/free_module_tensor.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index 2441d57b6e7..7826416e232 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -2844,6 +2844,25 @@ def contract(self, *args): if self._tensor_rank + other._tensor_rank - 2*ncontr == 0: # Case of scalar output: return cmp_res + # + # Reordering of the indices to have all contravariant indices first: + # + nb_cov_s = 0 # Number of covariant indices of self not involved in the + # contraction + for pos in range(k1,k1+l1): + if pos not in pos1: + nb_cov_s += 1 + nb_con_o = 0 # Number of contravariant indices of other not involved + # in the contraction + for pos in range(0,k2): + if pos not in pos2: + nb_con_o += 1 + if nb_cov_s != 0 and nb_con_o != 0: + # some reordering is necessary: + p2 = k1 + l1 - ncontr + p1 = p2 - nb_cov_s + p3 = p2 + nb_con_o + cmp_res = cmp_res.swap_adjacent_indices(p1, p2, p3) type_res = (k1+k2-ncontr, l1+l2-ncontr) return self._fmodule.tensor_from_comp(type_res, cmp_res) From 8f2bdf89a1dfeca32bd7eb71fe724d2d566f987e Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 1 Apr 2023 13:56:54 +0800 Subject: [PATCH 06/13] hide (-1)^s convention behind additional argument --- src/sage/manifolds/differentiable/diff_form.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index a86d6f9b6c0..6210c4e7170 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -619,6 +619,7 @@ def hodge_dual( nondegenerate_tensor: Union[ PseudoRiemannianMetric, SymplecticForm, None ] = None, + minus_eigenvalues_convention: bool = False, ) -> DiffForm: r""" Compute the Hodge dual of the differential form with respect to some non-degenerate @@ -637,8 +638,8 @@ def hodge_dual( `n`-form associated with `g` (see :meth:`~sage.manifolds.differentiable.metric.PseudoRiemannianMetric.volume_form`) and the indices `k_1,\ldots, k_p` are raised with `g`. - If `g` is a pseudo-Riemannian metric, an additional multiplicative factor of - `(-1)^s` is introduced on the right-hand side, + If `g` is a pseudo-Riemannian metric, sometimes an additional multiplicative + factor of `(-1)^s` is introduced on the right-hand side, where `s` is the number of negative eigenvalues of `g`. INPUT: @@ -649,6 +650,9 @@ def hodge_dual( :class:`~sage.manifolds.differentiable.symplectic_form.SymplecticForm`. If none is provided, the ambient domain of ``self`` is supposed to be endowed with a default metric and this metric is then used. + - ``minus_eigenvalues_convention`` -- if `true`, a factor of `(-1)^s` is + introduced with `s` being the number of negative eigenvalues of the + ``nondegenerate_tensor``. OUTPUT: @@ -783,9 +787,10 @@ def hodge_dual( result = self.up(nondegenerate_tensor).contract(*range(p), eps, *range(p)) if p > 1: result = result / factorial(p) - from sage.manifolds.differentiable.metric import PseudoRiemannianMetric - if isinstance(nondegenerate_tensor, PseudoRiemannianMetric): - result = result * nondegenerate_tensor._indic_signat + if minus_eigenvalues_convention: + from sage.manifolds.differentiable.metric import PseudoRiemannianMetric + if isinstance(nondegenerate_tensor, PseudoRiemannianMetric): + result = result * nondegenerate_tensor._indic_signat result.set_name( name=format_unop_txt("*", self._name), From 63236193644f6950eb711417bf10a58b1fca1828 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 1 Apr 2023 13:58:52 +0800 Subject: [PATCH 07/13] add more docs about minus one convention --- src/sage/manifolds/differentiable/diff_form.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index 6210c4e7170..a19ab90c10c 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -641,6 +641,8 @@ def hodge_dual( If `g` is a pseudo-Riemannian metric, sometimes an additional multiplicative factor of `(-1)^s` is introduced on the right-hand side, where `s` is the number of negative eigenvalues of `g`. + This convention can be enforced by setting the option + ``minus_eigenvalues_convention``. INPUT: From 11b1a86be0355f5f70e2024d9ce8bce973377e22 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 1 Apr 2023 20:15:43 +0800 Subject: [PATCH 08/13] fix linter --- src/sage/manifolds/differentiable/symplectic_form.py | 2 +- src/sage/manifolds/differentiable/symplectic_form_test.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/manifolds/differentiable/symplectic_form.py b/src/sage/manifolds/differentiable/symplectic_form.py index 2807416e5a7..a5f54eaa993 100644 --- a/src/sage/manifolds/differentiable/symplectic_form.py +++ b/src/sage/manifolds/differentiable/symplectic_form.py @@ -592,7 +592,7 @@ def on_forms(self, first: DiffForm, second: DiffForm) -> DiffScalarField: r""" Return the contraction of the two forms with respect to the symplectic form. - The symplectic form `\omega` gives rise to a bilinear form, + The symplectic form `\omega` gives rise to a bilinear form, also denoted by `\omega` on the space of `1`-forms by .. MATH:: diff --git a/src/sage/manifolds/differentiable/symplectic_form_test.py b/src/sage/manifolds/differentiable/symplectic_form_test.py index 110d96d8ea1..78c4fcaffd0 100644 --- a/src/sage/manifolds/differentiable/symplectic_form_test.py +++ b/src/sage/manifolds/differentiable/symplectic_form_test.py @@ -2,8 +2,6 @@ from _pytest.fixtures import FixtureRequest import pytest -# TODO: Remove sage.all import as soon as it's no longer necessary to load everything upfront -import sage.all from sage.manifolds.manifold import Manifold from sage.manifolds.differentiable.manifold import DifferentiableManifold from sage.manifolds.differentiable.examples.sphere import Sphere @@ -218,4 +216,3 @@ def test_hodge_star_is_given_using_omega_on_forms( a = M.one_form(1,2) b = M.one_form(3,4) assert a.wedge(b.hodge_dual(omega)) == omega.on_forms(a, b) * omega.volume_form() - From ab89d6639949f9aad194b5c4fdf4ca6be1241475 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Fri, 14 Apr 2023 19:32:49 +0800 Subject: [PATCH 09/13] improve performance in hodge dual --- src/sage/manifolds/differentiable/diff_form.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index a19ab90c10c..d229c3a545a 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -781,7 +781,10 @@ def hodge_dual( nondegenerate_tensor = self._vmodule._ambient_domain.metric() p = self.tensor_type()[1] - eps = nondegenerate_tensor.volume_form() + # For performance reasons, we raise the indicies of the volume form + # and not of the differential form; in the symplectic case this is wrong by + # a factor of (-1)^p, which will be corrected below + eps = nondegenerate_tensor.volume_form(p) if p == 0: common_domain = nondegenerate_tensor.domain().intersection(self.domain()) result = self.restrict(common_domain) * eps.restrict(common_domain) @@ -793,6 +796,10 @@ def hodge_dual( from sage.manifolds.differentiable.metric import PseudoRiemannianMetric if isinstance(nondegenerate_tensor, PseudoRiemannianMetric): result = result * nondegenerate_tensor._indic_signat + from sage.manifolds.differentiable.symplectic_form import SymplecticForm + if isinstance(nondegenerate_tensor, SymplecticForm): + # correction because we lifted the indicies of the volume (see above) + result = result * (-1)**p result.set_name( name=format_unop_txt("*", self._name), From a9da1aa3632947b2fffcc63a903c7ab2f40987fb Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Fri, 14 Apr 2023 19:38:32 +0800 Subject: [PATCH 10/13] fix tests --- src/sage/manifolds/differentiable/diff_form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index 1d6200a27ac..8375bb61716 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -789,7 +789,7 @@ def hodge_dual( common_domain = nondegenerate_tensor.domain().intersection(self.domain()) result = self.restrict(common_domain) * eps.restrict(common_domain) else: - result = self.up(nondegenerate_tensor).contract(*range(p), eps, *range(p)) + result = self.contract(*range(p), eps, *range(p)) if p > 1: result = result / factorial(p) if minus_eigenvalues_convention: From 56ca3d70e8e5c15720ae689f09546df2ac3cc437 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 19 Apr 2023 16:35:01 +0200 Subject: [PATCH 11/13] Update src/sage/tensor/modules/free_module_tensor.py Co-authored-by: Eric Gourgoulhon --- src/sage/tensor/modules/free_module_tensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index c9655a0aef8..87927937c3f 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -2459,7 +2459,7 @@ def trace( r""" Trace (contraction) on two slots of the tensor. - If a non-degenerate form is provided, the trace of a `(0,2)` tensor field + If a non-degenerate form is provided, the trace of a type-`(0,2)` tensor is computed by first raising the last index. INPUT: From 1820d7115fabb65a99159db8fa0e55ba794acf0c Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 19 Apr 2023 16:35:15 +0200 Subject: [PATCH 12/13] Update src/sage/tensor/modules/free_module_tensor.py Co-authored-by: Eric Gourgoulhon --- src/sage/tensor/modules/free_module_tensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index 87927937c3f..b3b39a7e362 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -2586,7 +2586,7 @@ def trace( if using is not None: if self.tensor_type() != (0, 2): raise ValueError( - "trace with respect to a non-degenerate form is only defined for type-(0,2) tensor fields" + "trace with respect to a non-degenerate form is only defined for type-(0,2) tensor" ) return self.up(using, 1).trace() From b439ea140b929774f86761e0423b7729b7c18439 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 30 Apr 2023 21:02:42 +0800 Subject: [PATCH 13/13] add test for trace in free_module_tensor --- .../differentiable/tensorfield_paral_test.py | 15 +++++++++++++++ src/sage/tensor/modules/free_module_tensor.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/sage/manifolds/differentiable/tensorfield_paral_test.py diff --git a/src/sage/manifolds/differentiable/tensorfield_paral_test.py b/src/sage/manifolds/differentiable/tensorfield_paral_test.py new file mode 100644 index 00000000000..7eda28c5c49 --- /dev/null +++ b/src/sage/manifolds/differentiable/tensorfield_paral_test.py @@ -0,0 +1,15 @@ +# pylint: disable=missing-function-docstring,missing-class-docstring +import pytest + +from sage.manifolds.differentiable.examples.euclidean import EuclideanSpace +from sage.manifolds.differentiable.manifold import DifferentiableManifold + + +class TestR3VectorSpace: + @pytest.fixture + def manifold(self): + return EuclideanSpace(3) + + def test_trace_using_metric_works(self, manifold: DifferentiableManifold): + metric = manifold.metric('g') + assert metric.trace(using=metric) == manifold.scalar_field(3) diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index b3b39a7e362..7a74aa8098e 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -2515,7 +2515,7 @@ def trace( The contraction on two slots having the same tensor type cannot occur:: - sage: b = M.tensor((2,0), name='b') ; b + sage: b = M.tensor((2,0), name='b') ; b Type-(2,0) tensor b on the Rank-3 free module M over the Integer Ring sage: b[:] = [[1,2,3], [4,5,6], [7,8,9]] sage: b.trace(0,1)