Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
WIP Chart as a Map
Browse files Browse the repository at this point in the history
  • Loading branch information
mkoeppe committed Jun 20, 2021
1 parent e6070fd commit bd199dc
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 92 deletions.
200 changes: 114 additions & 86 deletions src/sage/manifolds/chart.py
Expand Up @@ -38,7 +38,9 @@
# ****************************************************************************

from sage.structure.sage_object import SageObject
from sage.structure.unique_representation import UniqueRepresentation
from sage.structure.element import Element
from sage.misc.classcall_metaclass import ClasscallMetaclass
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
from sage.categories.sets_cat import Sets
from sage.categories.topological_spaces import TopologicalSpaces
from sage.categories.homset import Hom
Expand All @@ -54,7 +56,7 @@
from sage.ext.fast_callable import fast_callable


class Chart(Map): #, UniqueRepresentation):
class Chart(Map, metaclass=InheritComparisonClasscallMetaclass):
r"""
Chart on a topological manifold.
Expand Down Expand Up @@ -266,7 +268,31 @@ class Chart(Map): #, UniqueRepresentation):
manifolds over `\RR`.
"""
def __init__(self, domain, coordinates='', names=None, calc_method=None):

def __classcall__(cls, domain, coordinates='',
calc_method=None, coordinate_options=None,
names=None):
"""
Implement UniqueRepresentation behavior
"""
if isinstance(coordinates, str):
if coordinates == '':
for x in names:
coordinates += x + ' '
coordinates = coordinates[:-1]
coordinates, coordinate_options = cls._parse_coordinates(domain, coordinates)

coord_string = ' '.join(str(x) for x in coordinates)

try:
return domain._charts_by_coord[coord_string]
except KeyError:
self = type.__call__(cls, domain, coordinates,
calc_method, coordinate_options)
domain._charts_by_coord[coord_string] = self
return self

def __init__(self, domain, coordinates, calc_method, periods=None):
r"""
Construct a chart.
Expand All @@ -278,7 +304,7 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None):
Chart (M, (x, y))
sage: type(X)
<class 'sage.manifolds.chart.Chart'>
sage: assumptions() # no assumptions on x,y set by X._init_coordinates
sage: assumptions() # no assumptions on x,y set
[]
sage: TestSuite(X).run()
Expand All @@ -287,46 +313,37 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None):
if not isinstance(domain, TopologicalManifold):
raise TypeError("the first argument must be an open subset of " +
"a topological manifold")
if coordinates == '':
for x in names:
coordinates += x + ' '
coordinates = coordinates[:-1]
self._manifold = domain.manifold()
self._domain = domain
self._sindex = self._manifold.start_index()
# Handling of calculus methods available on this chart:
self._calc_method = CalculusMethod(current=calc_method,
base_field_type=self.manifold().base_field_type())
self.simplify = self._calc_method.simplify

# Treatment of the coordinates:
self._periods = {} # dict. of periods (if any); key = coord. index
if ' ' in coordinates:
coord_list = coordinates.split()
if periods is None:
self._periods = {}
else:
coord_list = [coordinates]
if len(coord_list) != self._manifold.dim():
self._periods = periods # dict. of periods (if any); key = coord. index

if len(coordinates) != self._manifold.dim():
raise ValueError("the list of coordinates must contain " +
"{} elements".format(self._manifold.dim()))
# The treatment of coordinates is performed by a separate method,
# _init_coordinates, which sets self._xx and
# which may be redefined for subclasses (for instance RealChart).
self._init_coordinates(coord_list)
coord_string = ' '.join(str(x) for x in self._xx)
if coord_string in self._domain._charts_by_coord:
raise ValueError("the chart with coordinates " + coord_string +
" has already been declared on " +
"the {}".format(self._domain))
self._domain._charts_by_coord[coord_string] = self
self._xx = coordinates

if self._periods:
# If any coordinate is periodic, we would need to make the codomain a torus
# in order to declare that the chart is a continuous map. But Sage does not
# have a suitable class for this.
cat = Sets()
else:
cat = TopologicalSpaces()
Map.__init__(self, Hom(self._manifold, self._domain.base_field(), cat))
# We would like to say TopologicalSpaces() here but "Vector space of
# dimension 2 over Complex Field with 53 bits of precision is not
# in Category of topological spaces".
#cat = TopologicalSpaces()
cat = Sets()
codomain = VectorSpace(domain.base_field(), len(coordinates))
Map.__init__(self, Hom(domain, codomain, cat))

#
# Additional restrictions on the coordinates
Expand Down Expand Up @@ -365,34 +382,40 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None):
dom._zero_scalar_field._express[self] = self.function_ring().zero()
dom._one_scalar_field._express[self] = self.function_ring().one()

def _init_coordinates(self, coord_list):
@classmethod
def _parse_coordinates(cls, domain, coordinates):
r"""
Initialization of the coordinates as symbolic variables.
This method must be redefined by derived classes in order to take
into account specificities (e.g. enforcing real coordinates).
INPUT:
- ``coord_list`` -- list of coordinate fields, which items in each
field separated by ":"; there are at most 2 items per field:
the coordinate name and the coordinate LaTeX symbol
- ``coord_list`` -- list (or space-separated concatenation) of
coordinate fields. Each field is a string of at most 3 items,
separated by ":". These items are: the coordinate symbol, the
(optional) indicator of the periodic character of the
coordinate, and the (optional) coordinate LaTeX symbol
OUTPUT:
- a tuple of variables (as elements of ``SR``)
- a dictionary with possible keys:
- `"periods": a dictionary with keys = coordinate indices
and values = periods
TESTS::
sage: from sage.manifolds.chart import Chart
sage: M = Manifold(2, 'M', field='complex', structure='topological')
sage: X.<z1, z2> = M.chart()
sage: X._init_coordinates(['z1', 'z2'])
sage: X
Chart (M, (z1, z2))
sage: X._init_coordinates([r'z1:\zeta_1', r'z2:\zeta_2'])
sage: X
Chart (M, (z1, z2))
sage: latex(X)
\left(M,({\zeta_1}, {\zeta_2})\right)
sage: Chart._parse_coordinates(M, ['z1', 'z2'])
sage: Chart._parse_coordinates(M, 'z1 z2')
sage: Chart._parse_coordinates(M, ['z1:\zeta_1', r'z2:\zeta_2'])
"""
if isinstance(coordinates, str):
coord_list = coordinates.split()
else:
coord_list = coordinates
xx_list = [] # will contain the coordinates as Sage symbolic variables
periods = {}
for coord_index, coord_field in enumerate(coord_list):
coord_properties = coord_field.split(':')
coord_symb = coord_properties[0].strip() # the coordinate symbol
Expand All @@ -405,14 +428,18 @@ def _init_coordinates(self, coord_list):
period = SR(prop1[7:])
else:
period = domain.base_field()(prop1[7:])
self._periods[coord_index + self._sindex] = period
periods[coord_index + domain._sindex] = period
else:
# prop1 is the coordinate's LaTeX symbol
coord_latex = prop1
# Construction of the coordinate as a Sage symbolic variable:
coord_var = SR.var(coord_symb, latex_name=coord_latex)
xx_list.append(coord_var)
self._xx = tuple(xx_list)
return tuple(xx_list), dict(periods=periods)

def __reduce__(self):
return (self.__class__, (self.domain(), self._xx, self._calc_method,
dict(periods=self._periods)))

def _repr_(self):
r"""
Expand Down Expand Up @@ -542,23 +569,23 @@ def __call__(self, point):
"""
return point.coord(self)

def domain(self):
r"""
Return the open subset on which the chart is defined.
## def domain(self):
## r"""
## Return the open subset on which the chart is defined.

EXAMPLES::
## EXAMPLES::

sage: M = Manifold(2, 'M', structure='topological')
sage: X.<x,y> = M.chart()
sage: X.domain()
2-dimensional topological manifold M
sage: U = M.open_subset('U')
sage: Y.<u,v> = U.chart()
sage: Y.domain()
Open subset U of the 2-dimensional topological manifold M
## sage: M = Manifold(2, 'M', structure='topological')
## sage: X.<x,y> = M.chart()
## sage: X.domain()
## 2-dimensional topological manifold M
## sage: U = M.open_subset('U')
## sage: Y.<u,v> = U.chart()
## sage: Y.domain()
## Open subset U of the 2-dimensional topological manifold M

"""
return self._domain
## """
## return self._domain

def manifold(self):
r"""
Expand Down Expand Up @@ -1595,7 +1622,7 @@ class RealChart(Chart):
:meth:`plot`.
"""
def __init__(self, domain, coordinates='', names=None, calc_method=None):
def __init__(self, domain, coordinates, calc_method=None, bounds=None, periods=None):
r"""
Construct a chart on a real topological manifold.
Expand All @@ -1613,11 +1640,13 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None):
sage: TestSuite(X).run()
"""
Chart.__init__(self, domain, coordinates=coordinates, names=names,
calc_method=calc_method)
super().__init__(domain, coordinates, calc_method=calc_method)
self._bounds = bounds
self._periods = periods
self._fast_valid_coordinates = None

def _init_coordinates(self, coord_list):
@classmethod
def _parse_coordinates(cls, domain, coordinates):
r"""
Initialization of the coordinates as symbolic variables.
Expand All @@ -1626,34 +1655,28 @@ def _init_coordinates(self, coord_list):
INPUT:
- ``coord_list`` -- list of coordinate fields, which items in each
field separated by ":"; there are at most 3 items per field:
the coordinate name, the coordinate LaTeX symbol and the
coordinate range
- ``coord_list`` -- list (or space-separated concatenation) of
coordinate fields. Each field is a string of at most 3 items,
separated by ":". These items are: the coordinate symbol, the
(optional) coordinate range or indicator of the periodic
character of the coordinate, and the (optional) coordinate
LaTeX symbol
TESTS::
sage: from sage.manifolds.chart import RealChart
sage: M = Manifold(2, 'M', structure='topological')
sage: X.<x,y> = M.chart()
sage: X._init_coordinates(['x', 'y'])
sage: X
Chart (M, (x, y))
sage: latex(X)
\left(M,(x, y)\right)
sage: X.coord_range()
x: (-oo, +oo); y: (-oo, +oo)
sage: X._init_coordinates([r'x1:\xi:(0,1)', r'y1:\eta'])
sage: X
Chart (M, (x1, y1))
sage: latex(X)
\left(M,({\xi}, {\eta})\right)
sage: X.coord_range()
x1: (0, 1); y1: (-oo, +oo)
sage: RealChart._parse_coordinates(M, ['x', 'y'])
sage: RealChart._parse_coordinates(M, [r'x1:\xi:(0,1)', r'y1:\eta'])
"""
from sage.symbolic.assumptions import assume
if isinstance(coordinates, str):
coord_list = coordinates.split()
else:
coord_list = coordinates
xx_list = [] # will contain the coordinates as Sage symbolic variables
bounds_list = [] # will contain the coordinate bounds
periods = {}
for coord_index, coord_field in enumerate(coord_list):
coord_properties = coord_field.split(':')
coord_symb = coord_properties[0].strip() # the coordinate symbol
Expand Down Expand Up @@ -1695,7 +1718,7 @@ def _init_coordinates(self, coord_list):
latex_name=coord_latex)
assume(coord_var, 'real')
if is_periodic:
self._periods[coord_index + self._sindex] = xmax - xmin
periods[coord_index + domain._sindex] = xmax - xmin
xmin_included = 'periodic'
xmax_included = 'periodic'
else:
Expand All @@ -1711,8 +1734,13 @@ def _init_coordinates(self, coord_list):
assume(coord_var < xmax)
xx_list.append(coord_var)
bounds_list.append(((xmin, xmin_included), (xmax, xmax_included)))
self._xx = tuple(xx_list)
self._bounds = tuple(bounds_list)
return tuple(xx_list), dict(bounds=tuple(bounds_list),
periods=periods)

def __reduce__(self):
return (self.__class__, (self.domain(), self._xx, self._calc_method,
dict(bounds=self._bounds,
periods=self._periods)))

def coord_bounds(self, i=None):
r"""
Expand Down
12 changes: 6 additions & 6 deletions src/sage/manifolds/differentiable/chart.py
Expand Up @@ -259,7 +259,7 @@ class DiffChart(Chart):
on differentiable manifolds over `\RR`.
"""
def __init__(self, domain, coordinates='', names=None, calc_method=None):
def __init__(self, domain, coordinates, calc_method=None, periods=None):
r"""
Construct a chart.
Expand All @@ -276,8 +276,8 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None):
sage: TestSuite(X).run()
"""
Chart.__init__(self, domain, coordinates=coordinates, names=names,
calc_method=calc_method)
super().__init__(domain, coordinates,
calc_method=calc_method, periods=periods)
# Construction of the coordinate frame associated to the chart:
self._frame = CoordFrame(self)
self._coframe = self._frame._coframe
Expand Down Expand Up @@ -946,7 +946,7 @@ class RealDiffChart(DiffChart, RealChart):
:meth:`~sage.manifolds.chart.RealChart.plot`.
"""
def __init__(self, domain, coordinates='', names=None, calc_method=None):
def __init__(self, domain, coordinates, calc_method=None, bounds=None, periods=None):
r"""
Construct a chart on a real differentiable manifold.
Expand All @@ -964,8 +964,8 @@ def __init__(self, domain, coordinates='', names=None, calc_method=None):
sage: TestSuite(X).run()
"""
RealChart.__init__(self, domain, coordinates=coordinates, names=names,
calc_method = calc_method)
RealChart.__init__(self, domain, coordinates,
calc_method=calc_method, bounds=bounds, periods=periods)
# Construction of the coordinate frame associated to the chart:
self._frame = CoordFrame(self)
self._coframe = self._frame._coframe
Expand Down

0 comments on commit bd199dc

Please sign in to comment.