|
55 | 55 |
|
56 | 56 | from sage.rings.integer import Integer |
57 | 57 | from sage.rings.integer_ring import ZZ |
58 | | -from sage.structure.element import ModuleElement |
| 58 | +from sage.structure.element import ModuleElementWithMutability |
| 59 | +from sage.misc.cachefunc import cached_method |
59 | 60 | from sage.tensor.modules.free_module_tensor import FreeModuleTensor |
60 | 61 | from sage.tensor.modules.tensor_with_indices import TensorWithIndices |
61 | 62 |
|
62 | | -class TensorField(ModuleElement): |
| 63 | +class TensorField(ModuleElementWithMutability): |
63 | 64 | r""" |
64 | 65 | Tensor field along a differentiable manifold. |
65 | 66 |
|
@@ -361,6 +362,35 @@ class TensorField(ModuleElement): |
361 | 362 | sage: s.restrict(U) == f.restrict(U) * t.restrict(U) |
362 | 363 | True |
363 | 364 |
|
| 365 | + Notice that the zero tensor field is immutable, and therefore its |
| 366 | + components cannot be changed:: |
| 367 | +
|
| 368 | + sage: zer = M.tensor_field_module((1, 1)).zero() |
| 369 | + sage: zer.is_immutable() |
| 370 | + True |
| 371 | + sage: zer.set_comp() |
| 372 | + Traceback (most recent call last): |
| 373 | + ... |
| 374 | + AssertionError: the components of an immutable element cannot be |
| 375 | + changed |
| 376 | +
|
| 377 | + Other tensor fields can be declared immutable, too:: |
| 378 | +
|
| 379 | + sage: t.is_immutable() |
| 380 | + False |
| 381 | + sage: t.set_immutable() |
| 382 | + sage: t.is_immutable() |
| 383 | + True |
| 384 | + sage: t.set_comp() |
| 385 | + Traceback (most recent call last): |
| 386 | + ... |
| 387 | + AssertionError: the components of an immutable element cannot be |
| 388 | + changed |
| 389 | + sage: t.set_name('b') |
| 390 | + Traceback (most recent call last): |
| 391 | + ... |
| 392 | + AssertionError: the name of an immutable element cannot be changed |
| 393 | +
|
364 | 394 | """ |
365 | 395 | def __init__(self, vector_field_module, tensor_type, name=None, |
366 | 396 | latex_name=None, sym=None, antisym=None, parent=None): |
@@ -409,7 +439,7 @@ def __init__(self, vector_field_module, tensor_type, name=None, |
409 | 439 | """ |
410 | 440 | if parent is None: |
411 | 441 | parent = vector_field_module.tensor_module(*tensor_type) |
412 | | - ModuleElement.__init__(self, parent) |
| 442 | + ModuleElementWithMutability.__init__(self, parent) |
413 | 443 | self._vmodule = vector_field_module |
414 | 444 | self._tensor_type = tuple(tensor_type) |
415 | 445 | self._tensor_rank = self._tensor_type[0] + self._tensor_type[1] |
@@ -601,6 +631,9 @@ def set_name(self, name=None, latex_name=None): |
601 | 631 | a |
602 | 632 |
|
603 | 633 | """ |
| 634 | + if self.is_immutable(): |
| 635 | + raise AssertionError("the name of an immutable element " |
| 636 | + "cannot be changed") |
604 | 637 | if name is not None: |
605 | 638 | self._name = name |
606 | 639 | if latex_name is None: |
@@ -907,6 +940,26 @@ def symmetries(self): |
907 | 940 |
|
908 | 941 | #### End of simple accessors ##### |
909 | 942 |
|
| 943 | + def set_immutable(self): |
| 944 | + r""" |
| 945 | + Set ``self`` and all restrictions of ``self`` immutable. |
| 946 | +
|
| 947 | + EXAMPLES:: |
| 948 | +
|
| 949 | + sage: M = Manifold(2, 'M') |
| 950 | + sage: X.<x,y> = M.chart() |
| 951 | + sage: U = M.open_subset('U', coord_def={X: x^2+y^2<1}) |
| 952 | + sage: a = M.tensor_field(1, 1, [[1+y,x], [0,x+y]], name='a') |
| 953 | + sage: aU = a.restrict(U) |
| 954 | + sage: a.set_immutable() |
| 955 | + sage: aU.is_immutable() |
| 956 | + True |
| 957 | +
|
| 958 | + """ |
| 959 | + for rst in self._restrictions.values(): |
| 960 | + rst.set_immutable() |
| 961 | + super().set_immutable() |
| 962 | + |
910 | 963 | def set_restriction(self, rst): |
911 | 964 | r""" |
912 | 965 | Define a restriction of ``self`` to some subdomain. |
@@ -943,6 +996,9 @@ def set_restriction(self, rst): |
943 | 996 | True |
944 | 997 |
|
945 | 998 | """ |
| 999 | + if self.is_immutable(): |
| 1000 | + raise AssertionError("the restrictions of an immutable element " |
| 1001 | + "cannot be changed") |
946 | 1002 | if not isinstance(rst, TensorField): |
947 | 1003 | raise TypeError("the argument must be a tensor field") |
948 | 1004 | if not rst._domain.is_subset(self._domain): |
@@ -1116,7 +1172,8 @@ def restrict(self, subdomain, dest_map=None): |
1116 | 1172 | res._restrictions.update(rst._restrictions) |
1117 | 1173 | res._restrictions_graph.update(rst._restrictions_graph) |
1118 | 1174 | rst._extensions_graph.update(res._extensions_graph) |
1119 | | - |
| 1175 | + if self.is_immutable(): |
| 1176 | + res.set_immutable() # restrictions must be immutable, too |
1120 | 1177 | self._restrictions[subdomain] = res |
1121 | 1178 | self._restrictions_graph[subdomain] = res |
1122 | 1179 | res._extensions_graph.update(self._extensions_graph) |
@@ -1242,17 +1299,18 @@ def set_comp(self, basis=None): |
1242 | 1299 | ValueError: no basis could be found for computing the components |
1243 | 1300 | in the Coordinate frame (V, (d/du,d/dv)) |
1244 | 1301 |
|
1245 | | - Since zero is a special element, its components cannot be changed:: |
| 1302 | + Since zero is an immutable, its components cannot be changed:: |
1246 | 1303 |
|
1247 | 1304 | sage: z = M.tensor_field_module((1, 1)).zero() |
1248 | 1305 | sage: z.set_comp(e)[0,1] = u*v |
1249 | 1306 | Traceback (most recent call last): |
1250 | 1307 | ... |
1251 | | - AssertionError: the components of the zero element cannot be changed |
| 1308 | + AssertionError: the components of an immutable element cannot be |
| 1309 | + changed |
1252 | 1310 |
|
1253 | 1311 | """ |
1254 | | - if self is self.parent().zero(): |
1255 | | - raise AssertionError("the components of the zero element " |
| 1312 | + if self.is_immutable(): |
| 1313 | + raise AssertionError("the components of an immutable element " |
1256 | 1314 | "cannot be changed") |
1257 | 1315 | self._is_zero = False # a priori |
1258 | 1316 | if basis is None: |
@@ -1374,14 +1432,15 @@ def add_comp(self, basis=None): |
1374 | 1432 | Since zero is a special element, its components cannot be changed:: |
1375 | 1433 |
|
1376 | 1434 | sage: z = M.tensor_field_module((1, 1)).zero() |
1377 | | - sage: z.add_comp(e)[0,1] = u*v |
| 1435 | + sage: z.add_comp(e_uv)[1, 1] = u^2 |
1378 | 1436 | Traceback (most recent call last): |
1379 | 1437 | ... |
1380 | | - AssertionError: the components of the zero element cannot be changed |
| 1438 | + AssertionError: the components of an immutable element cannot be |
| 1439 | + changed |
1381 | 1440 |
|
1382 | 1441 | """ |
1383 | | - if self is self.parent().zero(): |
1384 | | - raise AssertionError("the components of the zero element " |
| 1442 | + if self.is_immutable(): |
| 1443 | + raise AssertionError("the components of an immutable element " |
1385 | 1444 | "cannot be changed") |
1386 | 1445 | self._is_zero = False # a priori |
1387 | 1446 | if basis is None: |
@@ -1455,6 +1514,9 @@ def add_comp_by_continuation(self, frame, subdomain, chart=None): |
1455 | 1514 | and `a` is defined on the entire manifold `S^2`. |
1456 | 1515 |
|
1457 | 1516 | """ |
| 1517 | + if self.is_immutable(): |
| 1518 | + raise AssertionError("the components of an immutable element " |
| 1519 | + "cannot be changed") |
1458 | 1520 | dom = frame._domain |
1459 | 1521 | if not dom.is_subset(self._domain): |
1460 | 1522 | raise ValueError("the vector frame is not defined on a subset " + |
@@ -1552,14 +1614,17 @@ def add_expr_from_subdomain(self, frame, subdomain): |
1552 | 1614 | on V: (xp, yp) |--> 1/(xp^2 + yp^2) |
1553 | 1615 |
|
1554 | 1616 | """ |
| 1617 | + if self.is_immutable(): |
| 1618 | + raise AssertionError("the expressions of an immutable element " |
| 1619 | + "cannot be changed") |
1555 | 1620 | dom = frame._domain |
1556 | 1621 | if not dom.is_subset(self._domain): |
1557 | 1622 | raise ValueError("the vector frame is not defined on a subset " + |
1558 | 1623 | "of the tensor field domain") |
1559 | 1624 | if frame not in self.restrict(frame.domain())._components: |
1560 | 1625 | raise ValueError("the tensor doesn't have an expression in " |
1561 | 1626 | "the frame"+frame._repr_()) |
1562 | | - comp = self._add_comp_unsafe(frame) |
| 1627 | + comp = self._add_comp_unsafe(frame) # the components stay the same |
1563 | 1628 | scomp = self.restrict(subdomain).comp(frame.restrict(subdomain)) |
1564 | 1629 | for ind in comp.non_redundant_index_generator(): |
1565 | 1630 | comp[[ind]]._express.update(scomp[[ind]]._express) |
@@ -1879,7 +1944,6 @@ def display_comp(self, frame=None, chart=None, coordinate_labels=True, |
1879 | 1944 | only_nonzero=only_nonzero, |
1880 | 1945 | only_nonredundant=only_nonredundant) |
1881 | 1946 |
|
1882 | | - |
1883 | 1947 | def __getitem__(self, args): |
1884 | 1948 | r""" |
1885 | 1949 | Return a component with respect to some frame. |
@@ -2041,6 +2105,9 @@ def copy_from(self, other): |
2041 | 2105 | False |
2042 | 2106 |
|
2043 | 2107 | """ |
| 2108 | + if self.is_immutable(): |
| 2109 | + raise AssertionError("the components of an immutable element " |
| 2110 | + "cannot be changed") |
2044 | 2111 | if other not in self.parent(): |
2045 | 2112 | raise TypeError("the original must be an element " |
2046 | 2113 | + "of {}".format(self.parent())) |
|
0 commit comments