Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FiniteRankFreeModuleMorphism.display: Show matrix decorated with basis element names #37825

Merged
merged 3 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/sage/manifolds/differentiable/diff_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ class is
sage: Phi.display()
Phi: S^2 β†’ R^3
on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1),
(x^2 + y^2 - 1)/(x^2 + y^2 + 1))
(x^2 + y^2 - 1)/(x^2 + y^2 + 1))
on V: (u, v) ↦ (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1),
-(u^2 + v^2 - 1)/(u^2 + v^2 + 1))
-(u^2 + v^2 - 1)/(u^2 + v^2 + 1))

It is possible to create the map via the method
:meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.diff_map`
Expand Down Expand Up @@ -163,7 +163,7 @@ class is
sage: Phi1.display()
Phi: S^2 β†’ R^3
on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1),
(x^2 + y^2 - 1)/(x^2 + y^2 + 1))
(x^2 + y^2 - 1)/(x^2 + y^2 + 1))

The definition can be completed by means of the method
:meth:`~sage.manifolds.continuous_map.ContinuousMap.add_expr`::
Expand All @@ -173,9 +173,9 @@ class is
sage: Phi1.display()
Phi: S^2 β†’ R^3
on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1),
(x^2 + y^2 - 1)/(x^2 + y^2 + 1))
(x^2 + y^2 - 1)/(x^2 + y^2 + 1))
on V: (u, v) ↦ (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1),
-(u^2 + v^2 - 1)/(u^2 + v^2 + 1))
-(u^2 + v^2 - 1)/(u^2 + v^2 + 1))

At this stage, ``Phi1`` and ``Phi`` are fully equivalent::

Expand Down Expand Up @@ -227,6 +227,14 @@ class is
Basis (βˆ‚/βˆ‚X,βˆ‚/βˆ‚Y,βˆ‚/βˆ‚Z) on the Tangent space at Point Phi(N) on the
3-dimensional differentiable manifold R^3

A convenient way to display the matrix of the differential::

sage: Phi.differential(np).display()
βˆ‚/βˆ‚u βˆ‚/βˆ‚v
βˆ‚/βˆ‚XβŽ› 2 0⎞
βˆ‚/βˆ‚Y⎜ 0 2⎟
βˆ‚/βˆ‚Z⎝ 0 0⎠

Differentiable maps can be composed by means of the operator ``*``: let
us introduce the map `\RR^3\rightarrow \RR^2` corresponding to
the projection from the point `(X,Y,Z)=(0,0,1)` onto the equatorial plane
Expand Down
138 changes: 123 additions & 15 deletions src/sage/tensor/modules/free_module_morphism.py
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,58 @@ def is_identity(self):
# End of Morphism methods
#

def _modules_and_bases(self, basis1=None, basis2=None):
r"""
Return domain, codomain, domain basis, and codomain basis.

This method implements default argument handling for methods
:meth:`matrix` and :meth:`display`.

INPUT:

- ``basis1`` -- (default: ``None``) basis of the domain of ``self``; if
none is provided, the domain's default basis is assumed
- ``basis2`` -- (default: ``None``) basis of the codomain of ``self``;
if none is provided, ``basis2`` is set to ``basis1`` if ``self`` is
an endomorphism, otherwise, ``basis2`` is set to the codomain's
default basis.

EXAMPLES::

sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
sage: e = M.basis('e'); f = N.basis('f')
sage: phi = M.hom(N, [[-1,2,0], [5,1,2]])
sage: phi._modules_and_bases()
(Rank-3 free module M over the Integer Ring,
Rank-2 free module N over the Integer Ring,
Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring,
Basis (f_0,f_1) on the Rank-2 free module N over the Integer Ring)
sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
sage: phi._modules_and_bases(ep)
(Rank-3 free module M over the Integer Ring,
Rank-2 free module N over the Integer Ring,
Basis (ep_0,ep_1,ep_2) on the Rank-3 free module M over the Integer Ring,
Basis (f_0,f_1) on the Rank-2 free module N over the Integer Ring)
"""
fmodule1 = self.domain()
fmodule2 = self.codomain()
if basis1 is None:
basis1 = fmodule1.default_basis()
elif basis1 not in fmodule1.bases():
raise TypeError(str(basis1) + " is not a basis on the " +
str(fmodule1) + ".")
if basis2 is None:
if self.is_endomorphism():
basis2 = basis1
else:
basis2 = fmodule2.default_basis()
elif basis2 not in fmodule2.bases():
raise TypeError(str(basis2) + " is not a basis on the " +
str(fmodule2) + ".")
return fmodule1, fmodule2, basis1, basis2

def matrix(self, basis1=None, basis2=None):
r"""
Return the matrix of ``self`` w.r.t to a pair of bases.
Expand Down Expand Up @@ -1109,21 +1161,7 @@ def matrix(self, basis1=None, basis2=None):

"""
from sage.matrix.constructor import matrix
fmodule1 = self.domain()
fmodule2 = self.codomain()
if basis1 is None:
basis1 = fmodule1.default_basis()
elif basis1 not in fmodule1.bases():
raise TypeError(str(basis1) + " is not a basis on the " +
str(fmodule1) + ".")
if basis2 is None:
if self.is_endomorphism():
basis2 = basis1
else:
basis2 = fmodule2.default_basis()
elif basis2 not in fmodule2.bases():
raise TypeError(str(basis2) + " is not a basis on the " +
str(fmodule2) + ".")
fmodule1, fmodule2, basis1, basis2 = self._modules_and_bases(basis1, basis2)
if (basis1, basis2) not in self._matrices:
if self._is_identity:
# The identity endomorphism
Expand Down Expand Up @@ -1248,3 +1286,73 @@ def _common_bases(self, other):
except ValueError:
continue
return resu

def display(self, basis1=None, basis2=None):
r"""
Display ``self`` as a matrix w.r.t to a pair of bases.

If the matrix is not known already, it is computed from the matrix in
another pair of bases by means of the change-of-basis formula.

INPUT:

- ``basis1`` -- (default: ``None``) basis of the domain of ``self``; if
none is provided, the domain's default basis is assumed
- ``basis2`` -- (default: ``None``) basis of the codomain of ``self``;
if none is provided, ``basis2`` is set to ``basis1`` if ``self`` is
an endomorphism, otherwise, ``basis2`` is set to the codomain's
default basis.

EXAMPLES::

sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
sage: e = M.basis('e'); f = N.basis('f')
sage: phi = M.hom(N, [[-1,2,0], [5,1,2]])
sage: phi.display() # default bases
e_0 e_1 e_2
f_0βŽ› -1 2 0⎞
f_1⎝ 5 1 2⎠
sage: phi.display(e, f) # given bases
e_0 e_1 e_2
f_0βŽ› -1 2 0⎞
f_1⎝ 5 1 2⎠

Matrix of an endomorphism::

sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
sage: phi.display(ep)
ep_0 ep_1 ep_2
ep_0βŽ› 1 2 3⎞
ep_1⎜ 4 5 6⎟
ep_2⎝ 7 8 9⎠
sage: phi.display(ep, ep) # same as above
ep_0 ep_1 ep_2
ep_0βŽ› 1 2 3⎞
ep_1⎜ 4 5 6⎟
ep_2⎝ 7 8 9⎠
sage: phi.display() # matrix w.r.t to the module's default basis
e_0 e_1 e_2
e_0βŽ› 1 -3 1⎞
e_1⎜-18 39 -18⎟
e_2⎝-25 54 -25⎠
"""
from sage.misc.latex import latex
from .format_utilities import is_atomic, FormattedExpansion
fmodule1, fmodule2, basis1, basis2 = self._modules_and_bases(basis1, basis2)
matrix = self.matrix(basis1, basis2)
if all(element._name for element in basis1):
basis1_names = [element._name for element in basis1]
else:
basis1_names = None
if all(element._name for element in basis2):
basis2_names = [element._name for element in basis2]
else:
basis2_names = None
resu_txt = matrix.str(unicode=True,
top_border=basis1_names,
left_border=basis2_names)
resu_latex = latex(matrix)
return FormattedExpansion(resu_txt, resu_latex)