| 520 | |
| 521 | def symbols(self, name=None, latex_name=None, domain=None): |
| 522 | """ |
| 523 | Construct an iterable object which acts like a sequence of |
| 524 | symbolic expressions (variables). |
| 525 | |
| 526 | INPUT: |
| 527 | |
| 528 | - ``name`` -- The sequence name. For example, if you name the |
| 529 | sequence `x`, the variables will be called `x0`, `x1`,... |
| 530 | |
| 531 | - ``latex_name`` -- An optional latex expression (string) to |
| 532 | use instead of `name` when converting the symbols to latex. |
| 533 | |
| 534 | - ``domain`` -- A string representing the domain of the symbol, |
| 535 | either 'real', 'complex', or 'positive'. |
| 536 | |
| 537 | OUTPUT: |
| 538 | |
| 539 | An iterable object containing symbolic expressions. |
| 540 | |
| 541 | EXAMPLES: |
| 542 | |
| 543 | The simplest use case:: |
| 544 | |
| 545 | sage: a = SR.symbols('a') |
| 546 | sage: a[0] |
| 547 | a0 |
| 548 | sage: a[1] |
| 549 | a1 |
| 550 | |
| 551 | Create polynomials with symbolic coefficients of arbitrary |
| 552 | degree:: |
| 553 | |
| 554 | sage: a = SR.symbols('a') |
| 555 | sage: p = sum([ a[i]*x^i for i in range(0,5)]) |
| 556 | sage: p |
| 557 | a4*x^4 + a3*x^3 + a2*x^2 + a1*x + a0 |
| 558 | |
| 559 | Using a different latex name since 'lambda' is reserved:: |
| 560 | |
| 561 | sage: l = SR.symbols('l', '\lambda') |
| 562 | sage: l[0] |
| 563 | l0 |
| 564 | sage: latex(l[0]) |
| 565 | \lambda_{0} |
| 566 | |
| 567 | Using multiple indices:: |
| 568 | |
| 569 | sage: a = SR.symbols('a') |
| 570 | sage: a[0,1,2] |
| 571 | a012 |
| 572 | sage: latex(a[0,1,2]) |
| 573 | a_{0}_{1}_{2} |
| 574 | sage: [ a[i,j] for i in range(0,2) for j in range(0,2) ] |
| 575 | [a00, a01, a10, a11] |
| 576 | |
| 577 | You can pass slices instead of integers to obtain a list of |
| 578 | symbols:: |
| 579 | |
| 580 | sage: a = SR.symbols('a') |
| 581 | sage: a[5:7] |
| 582 | [a5, a6] |
| 583 | |
| 584 | This even works for the second, third, etc. indices:: |
| 585 | |
| 586 | sage: a = SR.symbols('a') |
| 587 | sage: a[0:2, 0:2] |
| 588 | [a00, a01, a10, a11] |
| 589 | |
| 590 | TESTS: |
| 591 | |
| 592 | We shouldn't overwrite variables in the global namespace:: |
| 593 | |
| 594 | sage: a = SR.symbols('a') |
| 595 | sage: a0 = 4 |
| 596 | sage: a[0] |
| 597 | a0 |
| 598 | sage: a0 |
| 599 | 4 |
| 600 | |
| 601 | The symbol at a given index should always be the same, even |
| 602 | when the symbols themselves are unnamed. We store the string |
| 603 | representation and compare because the output is unpredictable:: |
| 604 | |
| 605 | sage: a = SR.symbols() |
| 606 | sage: a0str = str(a[0]) |
| 607 | sage: str(a[0]) == a0str |
| 608 | True |
| 609 | |
| 610 | Slices and single indices work when combined:: |
| 611 | |
| 612 | sage: a = SR.symbols('a') |
| 613 | sage: a[3, 0:2] |
| 614 | [a30, a31] |
| 615 | sage: a[0:2, 3] |
| 616 | [a03, a13] |
| 617 | |
| 618 | """ |
| 619 | return SymbolSequence(name, latex_name, domain) |
| 620 | |
| 621 | |
| 999 | |
| 1000 | |
| 1001 | class SymbolSequence: |
| 1002 | """ |
| 1003 | An iterable object which acts like a sequence of symbolic |
| 1004 | expressions (variables). The full documentation and test suite can |
| 1005 | be found under SymbolicRing.symbols(). |
| 1006 | """ |
| 1007 | |
| 1008 | def __init__(self, name=None, latex_name=None, domain=None): |
| 1009 | # We store a dict of already-created symbols so that we don't |
| 1010 | # recreate a symbol which already exists. This is especially |
| 1011 | # helpful when using unnamed variables, if you want e.g. a[0] |
| 1012 | # to return the same variable each time. |
| 1013 | self._symbols = {} |
| 1014 | |
| 1015 | self._name = name |
| 1016 | self._latex_name = latex_name |
| 1017 | self._domain = domain |
| 1018 | |
| 1019 | |
| 1020 | def _create_symbol_(self, subscript): |
| 1021 | """ |
| 1022 | Return a symbol with the given subscript. Creates the |
| 1023 | appropriate name and latex_name before delegating to |
| 1024 | SR.symbol(). |
| 1025 | |
| 1026 | EXAMPLES:: |
| 1027 | |
| 1028 | sage: from sage.symbolic.ring import SymbolSequence |
| 1029 | sage: a = SymbolSequence('a', 'alpha', 'real') |
| 1030 | sage: a1 = a._create_symbol_(1) |
| 1031 | sage: a1 |
| 1032 | a1 |
| 1033 | sage: latex(a1) |
| 1034 | alpha_{1} |
| 1035 | |
| 1036 | """ |
| 1037 | # Allow creating unnamed symbols, for consistency with |
| 1038 | # SR.symbol(). |
| 1039 | name = None |
| 1040 | if self._name is not None: |
| 1041 | name = '%s%d' % (self._name, subscript) |
| 1042 | |
| 1043 | latex_name = None |
| 1044 | if self._latex_name is not None: |
| 1045 | latex_name = r'%s_{%d}' % (self._latex_name, subscript) |
| 1046 | |
| 1047 | return SR.symbol(name, latex_name, self._domain) |
| 1048 | |
| 1049 | |
| 1050 | def _flatten_list_(self, l): |
| 1051 | """ |
| 1052 | Recursively flatten the given list, allowing for some elements |
| 1053 | to be non-iterable. This is slow, but also works, which is |
| 1054 | more than can be said about some of the snappier solutions of |
| 1055 | lore. |
| 1056 | |
| 1057 | EXAMPLES:: |
| 1058 | |
| 1059 | sage: from sage.symbolic.ring import SymbolSequence |
| 1060 | sage: a = SymbolSequence('a') |
| 1061 | sage: a._flatten_list_([1,2,3]) |
| 1062 | [1, 2, 3] |
| 1063 | sage: a._flatten_list_([1,[2,3]]) |
| 1064 | [1, 2, 3] |
| 1065 | sage: a._flatten_list_([1,[2,[3]]]) |
| 1066 | [1, 2, 3] |
| 1067 | sage: a._flatten_list_([[[[[1,[2,[3]]]]]]]) |
| 1068 | [1, 2, 3] |
| 1069 | |
| 1070 | """ |
| 1071 | result = [] |
| 1072 | |
| 1073 | for item in l: |
| 1074 | if isinstance(item, list): |
| 1075 | result += self._flatten_list_(item) |
| 1076 | else: |
| 1077 | result += [item] |
| 1078 | |
| 1079 | return result |
| 1080 | |
| 1081 | |
| 1082 | def __getitem__(self, key): |
| 1083 | """ |
| 1084 | This handles individual integer arguments, slices, and |
| 1085 | tuples. It just hands off the real work to |
| 1086 | self._subscript_foo_(). |
| 1087 | |
| 1088 | EXAMPLES: |
| 1089 | |
| 1090 | An integer argument:: |
| 1091 | |
| 1092 | sage: from sage.symbolic.ring import SymbolSequence |
| 1093 | sage: a = SymbolSequence('a') |
| 1094 | sage: a.__getitem__(1) |
| 1095 | a1 |
| 1096 | |
| 1097 | A tuple argument:: |
| 1098 | |
| 1099 | sage: from sage.symbolic.ring import SymbolSequence |
| 1100 | sage: a = SymbolSequence('a') |
| 1101 | sage: a.__getitem__((1,2)) |
| 1102 | a12 |
| 1103 | |
| 1104 | A slice argument:: |
| 1105 | |
| 1106 | sage: from sage.symbolic.ring import SymbolSequence |
| 1107 | sage: a = SymbolSequence('a') |
| 1108 | sage: a.__getitem__(slice(1,4)) |
| 1109 | [a1, a2, a3] |
| 1110 | |
| 1111 | """ |
| 1112 | if isinstance(key, tuple): |
| 1113 | return self._subscript_tuple_(key) |
| 1114 | |
| 1115 | if isinstance(key, slice): |
| 1116 | return self._subscript_slice_(key) |
| 1117 | |
| 1118 | # This is the most common case so it would make sense to have |
| 1119 | # this test first. But there are too many different "integer" |
| 1120 | # classes that we'd have to check for. |
| 1121 | return self._subscript_integer_(key) |
| 1122 | |
| 1123 | |
| 1124 | def _subscript_integer_(self, n): |
| 1125 | """ |
| 1126 | The subscript is a single integer, or something that acts like |
| 1127 | one. |
| 1128 | |
| 1129 | EXAMPLES:: |
| 1130 | |
| 1131 | sage: from sage.symbolic.ring import SymbolSequence |
| 1132 | sage: a = SymbolSequence('a') |
| 1133 | sage: a._subscript_integer_(123) |
| 1134 | a123 |
| 1135 | |
| 1136 | """ |
| 1137 | if n < 0: |
| 1138 | # Cowardly refuse to create a variable named "a-1". |
| 1139 | raise IndexError('Indices must be nonnegative') |
| 1140 | |
| 1141 | try: |
| 1142 | return self._symbols[n] |
| 1143 | except KeyError: |
| 1144 | self._symbols[n] = self._create_symbol_(n) |
| 1145 | return self._symbols[n] |
| 1146 | |
| 1147 | |
| 1148 | def _subscript_slice_(self, s): |
| 1149 | """ |
| 1150 | We were given a slice. Clean up some of its properties |
| 1151 | first. The start/step are default for lists. We make |
| 1152 | copies of these because they're read-only. |
| 1153 | |
| 1154 | EXAMPLES:: |
| 1155 | |
| 1156 | sage: from sage.symbolic.ring import SymbolSequence |
| 1157 | sage: a = SymbolSequence('a') |
| 1158 | sage: a._subscript_slice_(slice(1,3)) |
| 1159 | [a1, a2] |
| 1160 | |
| 1161 | """ |
| 1162 | (start, step) = (s.start, s.step) |
| 1163 | if start is None: |
| 1164 | start = 0 |
| 1165 | if s.stop is None: |
| 1166 | # Would otherwise loop forever since our "length" is |
| 1167 | # undefined. |
| 1168 | raise ValueError('You must supply an terminal index') |
| 1169 | if step is None: |
| 1170 | step = 1 |
| 1171 | |
| 1172 | # If the user asks for a slice, we'll be returning a list |
| 1173 | # of symbols. |
| 1174 | return [ self._subscript_integer_(idx) |
| 1175 | for idx in range(start, s.stop, step) ] |
| 1176 | |
| 1177 | |
| 1178 | |
| 1179 | def _subscript_tuple_(self, args): |
| 1180 | """ |
| 1181 | When we have more than one level of subscripts, we pick off |
| 1182 | the first one and generate the rest recursively. |
| 1183 | |
| 1184 | EXAMPLES: |
| 1185 | |
| 1186 | A simple two-tuple:: |
| 1187 | |
| 1188 | sage: from sage.symbolic.ring import SymbolSequence |
| 1189 | sage: a = SymbolSequence('a') |
| 1190 | sage: a._subscript_tuple_((1,8)) |
| 1191 | a18 |
| 1192 | |
| 1193 | Nested tuples:: |
| 1194 | |
| 1195 | sage: a._subscript_tuple_(( (1,2), (3,(4,5,6)) )) |
| 1196 | a123456 |
| 1197 | |
| 1198 | """ |
| 1199 | |
| 1200 | # We never call this method without an argument. |
| 1201 | key = args[0] |
| 1202 | args = args[1:] # Peel off the first arg, which we've called 'key' |
| 1203 | |
| 1204 | # We don't know the type of 'key', but __getitem__ will figure |
| 1205 | # it out and dispatch properly. |
| 1206 | v = self[key] |
| 1207 | if len(args) == 0: |
| 1208 | # There was only one element left in the tuple. |
| 1209 | return v |
| 1210 | |
| 1211 | # At this point, we know we were given at least a two-tuple. |
| 1212 | # The symbols corresponding to the first entry are already |
| 1213 | # computed, in 'v'. Here we recursively compute the symbols |
| 1214 | # corresponding to the second coordinate, with the first |
| 1215 | # coordinate(s) fixed. |
| 1216 | if isinstance(key, slice): |
| 1217 | ss = [ SymbolSequence(w._repr_(), w._latex_(), self._domain) |
| 1218 | for w in v ] |
| 1219 | |
| 1220 | # This might be nested... |
| 1221 | maybe_nested_list = [ s._subscript_tuple_(args) for s in ss ] |
| 1222 | return self._flatten_list_(maybe_nested_list) |
| 1223 | |
| 1224 | else: |
| 1225 | # If it's not a slice, it's an integer. |
| 1226 | ss = SymbolSequence(v._repr_(), v._latex_(), self._domain) |
| 1227 | return ss._subscript_tuple_(args) |