]>
gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/symbol_sequence.py
5 An iterable object which acts like a sequence of symbolic
6 expressions (variables).
10 - ``name`` -- The sequence name. For example, if you name the
11 sequence `x`, the variables will be called `x0`, `x1`,...
13 - ``latex_name`` -- An optional latex expression (string) to
14 use instead of `name` when converting the symbols to latex.
16 - ``domain`` -- A string representing the domain of the symbol,
17 either 'real', 'complex', or 'positive'.
21 An iterable object containing symbolic expressions.
25 The simplest use case::
27 sage: a = SymbolSequence('a')
33 Create polynomials with symbolic coefficients of arbitrary
36 sage: a = SymbolSequence('a')
37 sage: p = sum([ a[i]*x^i for i in range(0,5)])
39 a4*x^4 + a3*x^3 + a2*x^2 + a1*x + a0
41 Using a different latex name since 'lambda' is reserved::
43 sage: l = SymbolSequence('l', '\lambda')
49 Using multiple indices::
51 sage: a = SymbolSequence('a')
56 sage: [ a[i,j] for i in range(0,2) for j in range(0,2) ]
59 You can pass slices instead of integers to obtain a list of
62 sage: a = SymbolSequence('a')
66 This even works for the second, third, etc. indices::
68 sage: a = SymbolSequence('a')
74 We shouldn't overwrite variables in the global namespace::
76 sage: a = SymbolSequence('a')
83 The symbol at a given index should always be the same, even when
84 the symbols themselves are unnamed. We store the string
85 representation and compare because the output is unpredictable::
87 sage: a = SymbolSequence()
88 sage: a0str = str(a[0])
89 sage: str(a[0]) == a0str
92 Slices and single indices work when combined::
94 sage: a = SymbolSequence('a')
102 def __init__(self
, name
=None, latex_name
=None, domain
=None):
103 # We store a dict of already-created symbols so that we don't
104 # recreate a symbol which already exists. This is especially
105 # helpful when using unnamed variables, if you want e.g. a[0]
106 # to return the same variable each time.
110 self
._latex
_name
= latex_name
111 self
._domain
= domain
114 def _create_symbol_(self
, subscript
):
116 Return a symbol with the given subscript. Creates the
117 appropriate name and latex_name before delegating to
120 # Allow creating unnamed symbols, for consistency with
123 if self
._name
is not None:
124 name
= '%s%d' % (self
._name
, subscript
)
127 if self
._latex
_name
is not None:
128 latex_name
= r
'%s_{%d}' % (self
._latex
_name
, subscript
)
130 return SR
.symbol(name
, latex_name
, self
._domain
)
133 def _flatten_list_(self
, l
):
135 Recursively flatten the given list, allowing for some elements
136 to be non-iterable. This is slow, but also works, which is
137 more than can be said about some of the snappier solutions of
143 if isinstance(item
, list):
144 result
+= self
._flatten
_list
_(item
)
151 def __getitem__(self
, key
):
153 This handles individual integer arguments, slices, and
154 tuples. It just hands off the real work to
155 self._subscript_foo_().
157 if isinstance(key
, tuple):
158 return self
._subscript
_tuple
_(key
)
160 if isinstance(key
, slice):
161 return self
._subscript
_slice
_(key
)
163 # This is the most common case so it would make sense to have
164 # this test first. But there are too many different "integer"
165 # classes that you have to check for.
166 return self
._subscript
_integer
_(key
)
169 def _subscript_integer_(self
, n
):
171 The subscript is a single integer, or something that acts like
175 # Cowardly refuse to create a variable named "a-1".
176 raise IndexError('Indices must be nonnegative')
179 return self
._symbols
[n
]
181 self
._symbols
[n
] = self
._create
_symbol
_(n
)
182 return self
._symbols
[n
]
185 def _subscript_slice_(self
, s
):
187 We were given a slice. Clean up some of its properties
188 first. The start/step are default for lists. We make
189 copies of these because they're read-only.
191 (start
, step
) = (s
.start
, s
.step
)
195 # Would otherwise loop forever since our "length" is
197 raise ValueError('You must supply an terminal index')
201 # If the user asks for a slice, we'll be returning a list
203 return [ self
._subscript
_integer
_(idx
)
204 for idx
in range(start
, s
.stop
, step
) ]
208 def _subscript_tuple_(self
, args
):
210 When we have more than one level of subscripts, we pick off
211 the first one and generate the rest recursively.
214 # We never call this method without an argument.
216 args
= args
[1:] # Peel off the first arg, which we've called 'key'
218 # We don't know the type of 'key', but __getitem__ will figure
219 # it out and dispatch properly.
222 # There was only one element left in the tuple.
225 # At this point, we know we were given at least a two-tuple.
226 # The symbols corresponding to the first entry are already
227 # computed, in 'v'. Here we recursively compute the symbols
228 # corresponding to the second coordinate, with the first
229 # coordinate(s) fixed.
230 if isinstance(key
, slice):
231 ss
= [ SymbolSequence(w
._repr
_(), w
._latex
_(), self
._domain
)
234 # This might be nested...
235 maybe_nested_list
= [ s
._subscript
_tuple
_(args
) for s
in ss
]
236 return self
._flatten
_list
_(maybe_nested_list
)
239 # If it's not a slice, it's an integer.
240 ss
= SymbolSequence(v
._repr
_(), v
._latex
_(), self
._domain
)
241 return ss
._subscript
_tuple
_(args
)