Skip to content

Commit

Permalink
Trac #30300: sage.tensor.modules.free_module_basis: Make Basis_abstra…
Browse files Browse the repository at this point in the history
…ct a subclass of AbstractFamily

... in particular provide an implementation of the `keys()` method.

We also add a generic method `AbstractFamily.items()` to complement the
methods `keys()` and `values()`, mimicking  the `Mapping` protocol.
(Split out from #34340.)

URL: https://trac.sagemath.org/30300
Reported by: mkoeppe
Ticket author(s): Matthias Koeppe
Reviewer(s): Eric Gourgoulhon
  • Loading branch information
Release Manager committed Aug 28, 2022
2 parents 3f624d5 + 66874a9 commit 3091ae9
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 11 deletions.
4 changes: 2 additions & 2 deletions src/sage/manifolds/differentiable/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def frame(self):
sage: c_xy.frame()
Coordinate frame (M, (∂/∂x,∂/∂y))
sage: type(c_xy.frame())
<class 'sage.manifolds.differentiable.vectorframe.CoordFrame'>
<class 'sage.manifolds.differentiable.vectorframe.CoordFrame_with_category'>
Check that ``c_xy.frame()`` is indeed the coordinate frame associated
with the coordinates `(x,y)`::
Expand Down Expand Up @@ -487,7 +487,7 @@ def coframe(self):
sage: c_xy.coframe()
Coordinate coframe (M, (dx,dy))
sage: type(c_xy.coframe())
<class 'sage.manifolds.differentiable.vectorframe.CoordCoFrame'>
<class 'sage.manifolds.differentiable.vectorframe.CoordCoFrame_with_category'>
Check that ``c_xy.coframe()`` is indeed the coordinate coframe
associated with the coordinates `(x, y)`::
Expand Down
4 changes: 2 additions & 2 deletions src/sage/manifolds/differentiable/vectorframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ def at(self, point):
Dual basis (dx,dy) on the Tangent space at Point p on the
2-dimensional differentiable manifold M
sage: type(fp)
<class 'sage.tensor.modules.free_module_basis.FreeModuleCoBasis'>
<class 'sage.tensor.modules.free_module_basis.FreeModuleCoBasis_with_category'>
sage: fp[0]
Linear form dx on the Tangent space at Point p on the 2-dimensional
differentiable manifold M
Expand Down Expand Up @@ -1362,7 +1362,7 @@ def at(self, point):
Basis (∂/∂x,∂/∂y) on the Tangent space at Point p on the
2-dimensional differentiable manifold M
sage: type(ep)
<class 'sage.tensor.modules.free_module_basis.FreeModuleBasis'>
<class 'sage.tensor.modules.free_module_basis.FreeModuleBasis_with_category'>
sage: ep[0]
Tangent vector ∂/∂x at Point p on the 2-dimensional differentiable
manifold M
Expand Down
4 changes: 2 additions & 2 deletions src/sage/manifolds/local_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ def at(self, point):
Dual basis (e^1,e^2) on the Fiber of E at Point p on the
2-dimensional topological manifold M
sage: type(e_dual_p)
<class 'sage.tensor.modules.free_module_basis.FreeModuleCoBasis'>
<class 'sage.tensor.modules.free_module_basis.FreeModuleCoBasis_with_category'>
sage: e_dual_p[1]
Linear form e^1 on the Fiber of E at Point p on the 2-dimensional
topological manifold M
Expand Down Expand Up @@ -1045,7 +1045,7 @@ def at(self, point):
Basis (e_0,e_1) on the Fiber of E at Point p on the 2-dimensional
topological manifold M
sage: type(ep)
<class 'sage.tensor.modules.free_module_basis.FreeModuleBasis'>
<class 'sage.tensor.modules.free_module_basis.FreeModuleBasis_with_category'>
sage: ep[0]
Vector e_0 in the fiber of E at Point p on the 2-dimensional
topological manifold M
Expand Down
40 changes: 40 additions & 0 deletions src/sage/sets/family.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from pprint import pformat, saferepr
from collections.abc import Iterable

from sage.misc.abstract_method import abstract_method
from sage.misc.cachefunc import cached_method
from sage.structure.parent import Parent
from sage.categories.enumerated_sets import EnumeratedSets
Expand Down Expand Up @@ -431,6 +432,45 @@ def hidden_keys(self):
"""
return []

@abstract_method
def keys(self):
"""
Return the keys of the family.
EXAMPLES::
sage: f = Family({3: 'a', 4: 'b', 7: 'd'})
sage: sorted(f.keys())
[3, 4, 7]
"""

@abstract_method(optional=True)
def values(self):
"""
Return the elements (values) of this family.
EXAMPLES::
sage: f = Family(["c", "a", "b"], lambda x: x + x)
sage: sorted(f.values())
['aa', 'bb', 'cc']
"""

def items(self):
"""
Return an iterator for key-value pairs.
A key can only appear once, but if the function is not injective, values may
appear multiple times.
EXAMPLES::
sage: f = Family([-2, -1, 0, 1, 2], abs)
sage: list(f.items())
[(-2, 2), (-1, 1), (0, 0), (1, 1), (2, 2)]
"""
return zip(self.keys(), self.values())

def zip(self, f, other, name=None):
r"""
Given two families with same index set `I` (and same hidden
Expand Down
103 changes: 98 additions & 5 deletions src/sage/tensor/modules/free_module_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,44 @@
# http://www.gnu.org/licenses/
#******************************************************************************

from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
from sage.rings.integer_ring import ZZ
from sage.sets.family import AbstractFamily
from sage.structure.unique_representation import UniqueRepresentation
from sage.structure.sage_object import SageObject

class Basis_abstract(UniqueRepresentation, SageObject):
class Basis_abstract(UniqueRepresentation, AbstractFamily):
"""
Abstract base class for (dual) bases of free modules.
A basis is an :class:`~sage.sets.family.AbstractFamily`, hence like
:class:`collections.abc.Mapping` subclasses such as :class:`dict`, it is
an associative :class:`Container`, providing methods :meth:`keys`,
:meth:`values`, and :meth:`items`. Thus, ``e[i]`` returns the element
of the basis ``e`` indexed by the key ``i``. However, in contrast to
:class:`Mapping` subclasses, not the :meth:`keys` but the
:meth:`values` are considered the elements.
EXAMPLES:
sage: M = FiniteRankFreeModule(ZZ, 3, name='M', start_index=1)
sage: e = M.basis('e'); e
Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring
sage: list(e)
[Element e_1 of the Rank-3 free module M over the Integer Ring,
Element e_2 of the Rank-3 free module M over the Integer Ring,
Element e_3 of the Rank-3 free module M over the Integer Ring]
sage: e.category()
Category of facade finite enumerated sets
sage: list(e.keys())
[1, 2, 3]
sage: list(e.values())
[Element e_1 of the Rank-3 free module M over the Integer Ring,
Element e_2 of the Rank-3 free module M over the Integer Ring,
Element e_3 of the Rank-3 free module M over the Integer Ring]
sage: list(e.items())
[(1, Element e_1 of the Rank-3 free module M over the Integer Ring),
(2, Element e_2 of the Rank-3 free module M over the Integer Ring),
(3, Element e_3 of the Rank-3 free module M over the Integer Ring)]
"""
def __init__(self, fmodule, symbol, latex_symbol, indices, latex_indices):
"""
Expand All @@ -54,10 +86,59 @@ def __init__(self, fmodule, symbol, latex_symbol, indices, latex_indices):
self._latex_symbol = latex_symbol
self._indices = indices
self._latex_indices = latex_indices
super().__init__(category=FiniteEnumeratedSets(), facade=fmodule)

def keys(self):
"""
Return the keys (indices) of the family.
EXAMPLES::
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: list(e.keys())
[0, 1, 2]
"""
return self._fmodule.irange()

def values(self):
"""
Return the basis elements of ``self``.
EXAMPLES::
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: list(e.values())
[Element e_0 of the Rank-3 free module M over the Integer Ring,
Element e_1 of the Rank-3 free module M over the Integer Ring,
Element e_2 of the Rank-3 free module M over the Integer Ring]
"""
return self._vec

def _element_constructor_(self, x):
"""
Test whether ``x`` is an element of ``self``.
EXAMPLES::
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: e(e[1])
Element e_1 of the Rank-3 free module M over the Integer Ring
sage: f = M.basis('f')
sage: e(f[1])
Traceback (most recent call last):
...
ValueError: no common basis for the comparison
"""
if x in self.values():
return x
raise ValueError(f'{x} is not in {self}')

def __iter__(self):
r"""
Return the list of basis elements of ``self``.
Return an iterator for the basis elements of ``self``.
EXAMPLES::
Expand All @@ -83,8 +164,7 @@ def __iter__(self):
Element e_2 of the Rank-3 free module M1 over the Integer Ring,
Element e_3 of the Rank-3 free module M1 over the Integer Ring]
"""
for i in self._fmodule.irange():
yield self[i]
yield from self.values()

def _test_iter_len(self, **options):
r"""
Expand Down Expand Up @@ -125,6 +205,19 @@ def __len__(self):
"""
return self._fmodule._rank

def cardinality(self):
r"""
Return the basis length, i.e. the rank of the free module.
EXAMPLES::
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: e.cardinality()
3
"""
return ZZ(self._fmodule._rank)

def __getitem__(self, index):
r"""
Return the basis element corresponding to a given index.
Expand Down

0 comments on commit 3091ae9

Please sign in to comment.