1 """
2 General utility functions and classes.
3
4 $Id: utils.py 8026 2008-02-19 14:30:05Z ceball $
5 """
6 __version__='$Revision: 8026 $'
7
8 import __main__
9 import math
10 import re
11 import string
12 import random
13 import numpy
14 import cProfile
15 import pstats
16
17
19 """
20 Converts a tuple (X1,X2,...,Xn) to a string 'X1xX2x...xXn'
21 """
22 return 'x'.join([`N` for N in tuple])
23
24
25
26 enum = enumerate
27
28
30 """
31 A simple structure class, taking keyword args and assigning them to attributes.
32
33 For instance:
34
35 s = Struct(foo='a',bar=1)
36 >>> s.foo
37 'a'
38 >>> s.bar
39 1
40
41
42 From http://www.norvig.com/python-iaq.html
43 """
45
47
48 args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
49 return 'Struct(%s)' % ', '.join(args)
50
51
52
54 """
55 Returns a list of the indices needed to address or loop over all
56 the elements of a 1D or 2D matrix with the given shape. E.g.:
57
58 flat_indices((3,)) == [0,1,2]
59 """
60 if len(shape) == 1:
61 return [(x,) for x in range(shape[0])]
62 else:
63 rows,cols = shape
64 return [(r,c) for r in range(rows) for c in range(cols)]
65
66
67
69 """
70 Flattens a list.
71
72 Written by Bearophile as published on the www.python.org newsgroups.
73 Pulled into Topographica 3/5/2005.
74 """
75 if type(l) != list:
76 return l
77 else:
78 result = []
79 stack = []
80 stack.append((l,0))
81 while len(stack) != 0:
82 sequence, j = stack.pop(-1)
83 while j < len(sequence):
84 if type(sequence[j]) != list:
85 k, j, lens = j, j+1, len(sequence)
86 while j < len(sequence) and \
87 (type(sequence[j]) != list):
88 j += 1
89 result.extend(sequence[k:j])
90 else:
91 stack.append((sequence, j+1))
92 sequence, j = sequence[j], 0
93 return result
94
95
96
98 """
99 Evaluates the given string in __main__, and converts it to a float.
100
101 The string can contain any expression that will evaluate to a
102 number. The expression can use any variables or functions that
103 are defined in the main namespace.
104
105 See string.atof() for more details about the conversion from the
106 evaluated string into a float.
107 """
108 return string.atof(eval(str,__main__.__dict__))
109
110
111 """
112 Return the cross-product of a variable number of lists (e.g. of a list of lists).
113
114 Use to obtain permutations, e.g.
115 l1=[a,b]
116 l2=[c,d]
117 cross_product([l1,l2]) =
118 [[a,c], [a,d], [b,c], [b,d]]
119
120
121 From:
122 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/159975
123 """
124
125 cross_product=lambda ss,row=[],level=0: len(ss)>1 \
126 and reduce(lambda x,y:x+y,[cross_product(ss[1:],row+[i],level+1) for i in ss[0]]) \
127 or [row+[i] for i in ss[0]]
128
129
130
131
132 -def frange(start, end=None, inc=1.0, inclusive=False):
133 """
134 A range function that accepts float increments.
135
136 Otherwise, works just as the inbuilt range() function. If
137 inclusive is False, as in the default, the range is exclusive (not
138 including the end value), as in the inbuilt range(). If inclusive
139 is true, the range may include the end value.
140
141 'All theoretic restrictions apply, but in practice this is
142 more useful than in theory.'
143
144 From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66472
145 """
146 if end == None:
147 end = start + 0.0
148 start = 0.0
149
150
151
152 assert ((inc>0 and start<=end) or (inc<0 and start>=end))
153
154 L = []
155 while 1:
156 next = start + len(L) * inc
157 if inclusive:
158 if inc > 0 and next > end: break
159 elif inc < 0 and next < end: break
160 else:
161 if inc > 0 and next >= end: break
162 elif inc < 0 and next <= end: break
163 L.append(next)
164
165 return L
166
167
168
170 """
171 Return the values of dictionary d sorted by key.
172 """
173
174
175 keys = d.keys()
176 keys.sort()
177 return map(d.get, keys)
178
179
181 """
182 Return the keys of dictionary d sorted by value.
183 """
184
185
186 items=d.items()
187 backitems=[ [v[1],v[0]] for v in items]
188 backitems.sort()
189 return [ backitems[i][1] for i in range(0,len(backitems))]
190
191
193 """
194 Return the inverse of dictionary dict_.
195
196 (I.e. return a dictionary with keys that are the values of dict_,
197 and values that are the corresponding keys from dict_.)
198
199 The values of dict_ must be unique.
200 """
201 idict = dict([(value,key) for key,value in dict_.iteritems()])
202 if len(idict)!=len(dict_):
203 raise ValueError("Dictionary has no inverse (values not unique).")
204 return idict
205
206
208 """
209 Returns the class name of x as a string with the leading package information removed.
210
211 E.g. if x is of type "<class 'topo.base.sheet.Sheet'>", returns
212 "Sheet"
213 """
214 return re.sub("'>","",re.sub(".*[.]","",repr(type(x))))
215
216
217
218 -def profile(command,n=50,sorting=('cumulative','time'),strip_dirs=False):
219 """
220 Profile the given command (supplied as a string), printing
221 statistics about the top n functions when ordered according to
222 sorting.
223
224 sorting defaults to ordering by cumulative time and then internal
225 time; see http://docs.python.org/lib/profile-stats.html for other
226 sorting options.
227
228 By default, the complete paths of files are not shown. If there
229 are multiple files with the same name, you might wish to set
230 strip_dirs=False to make it easier to follow the output.
231
232
233 Examples:
234
235 - profile loading a simulation:
236 profile('execfile("examples/hierarchical.ty")')
237
238 - profile running an already loaded simulation:
239 profile('topo.sim.run(10)')
240
241 - profile running a whole simulation:
242 profile('execfile("examples/lissom_oo_or.ty");topo.sim.run(20000)')
243
244 - profile running a simulation, but from the commandline:
245 ./topographica examples/hierarchical.ty -c "from topo.misc.utils import profile; profile('topo.sim.run(10)')"
246 """
247
248
249
250
251
252 prof = cProfile.run(command,'filename')
253 prof_stats = pstats.Stats('filename')
254
255 if strip_dirs:prof_stats.strip_dirs()
256
257 prof_stats.sort_stats(*sorting).print_callees(n)
258
259
260
261
262 prof_stats.sort_stats(*sorting).print_stats(n)
263
264
265
266
268 """
269 Select randomly from the given sequence.
270
271 The weights, if given, should be a sequence the same length as
272 seq, as would be passed to weighted_sample_idx().
273 """
274 if not weights:
275 return seq[random.randrange(len(seq))]
276 else:
277 assert len(weights) == len(seq)
278 return seq[weighted_sample_idx(weights)]
279
280
282 """
283 Return an integer generated by sampling the discrete distribution
284 represented by the sequence of weights.
285
286 The integer will be in the range [0,len(weights)). The weights
287 need not sum to unity, but can contain any non-negative values
288 (e.g., [1 1 1 100] is a valid set of weights).
289
290 To use weights from a 2D numpy array w, specify w.ravel() (not the
291 w.flat iterator).
292 """
293 total = sum(weights)
294 if total == 0:
295 return random.randrange(len(weights))
296 index = random.random() * total
297 accum = 0
298 for i,x in enumerate(weights):
299 accum += x
300 if index < accum:
301 return i
302
303
305 """
306 Given a flat matrix index and a 2D matrix shape, return the (row,col)
307 coordinates of the index.
308 """
309 assert len(shape) == 2
310 rows,cols = shape
311
312 return idx/cols,idx%cols
313
314
315
317 """
318 Given a row, column, and matrix shape, return the corresponding index
319 into the flattened (raveled) matrix.
320 """
321 assert len(shape) == 2
322 rows,cols = shape
323
324 return r * cols + c
325
326
327
329 """
330 Return the centroid of a weighted set of points as an array.
331
332 The pts argument should be an array of points, one per row,
333 and weights should be a vector of weights.
334 """
335
336 return numpy.dot(numpy.transpose(pts),weights)/sum(weights)
337
339 """
340 Split x into its sign and absolute value.
341
342 Returns a tuple (sign(x),abs(x)). Note: sign(0) = 1, unlike
343 numpy.sign.
344 """
345
346 if x < 0:
347 sgn = -1
348 else:
349 sgn = 1
350
351 return sgn,abs(x)
352
353
354
355
356
357
358
359
360
362 """
363 The singleton pattern.
364
365 To create a singleton class, you subclass from Singleton; each
366 subclass will have a single instance, no matter how many times its
367 constructor is called. To further initialize the subclass
368 instance, subclasses should override 'init' instead of __init__ -
369 the __init__ method is called each time the constructor is called.
370
371 From http://www.python.org/2.2.3/descrintro.html#__new__
372 """
374 it = cls.__dict__.get("__it__")
375 if it is not None:
376 return it
377 cls.__it__ = it = object.__new__(cls)
378 it.init(*args, **kwds)
379 return it
380
381 - def init(self, *args, **kwds):
382 """Method to be overridden if the subclass needs initialization."""
383 pass
384
385
387 """
388 Causes __new__ to be called on unpickling; in turn, __new__
389 ensures there is only one instance.
390 """
391 return (type(self),tuple())
392
393
394
395
397 """
398 Provides a simple means to include arbitrary attributes from
399 modules when pickling.
400
401 To include a variable z of module x.y (i.e. x.y.z) in a snapshot (and have
402 it put back as x.y.z on loading), do:
403 ExtraPickler().add( ('x.y','z') )
404 """
405 extras = []
406
409
411 states = {}
412 for extra in self.extras:
413 d = {}
414 exec "from %s import %s"%(extra[0],extra[1]) in d
415 states[extra] = d[extra[1]]
416 return (type(self),tuple(),{'extras':self.extras,'states':states})
417
419 import __main__
420
421 self.extras = state['extras']
422 states = state['states']
423
424 for extra in set(self.extras):
425 item = states[extra]
426 module = extra[0]
427 attribute = extra[1]
428
429
430
431
432 __main__.__dict__['val']=item
433
434 exec "import %s"%module in __main__.__dict__
435
436
437
438
439
440 update_main=False
441 if attribute in __main__.__dict__:
442 full_path_item = eval("%s.%s"%(module,attribute),__main__.__dict__)
443 if __main__.__dict__[attribute] is full_path_item:
444 update_main=True
445
446 exec "%s.%s=val"%(module,attribute) in __main__.__dict__
447
448 if update_main:
449 exec "%s=val"%(attribute) in __main__.__dict__
450