1 """
2 General utility functions and classes.
3
4 $Id: util.py 11304 2010-07-27 16:35:22Z ceball $
5 """
6 __version__='$Revision: 11304 $'
7
8 import re
9 import random
10 import numpy
11 import functools
12
13
14
16 """
17 Converts a tuple (X1,X2,...,Xn) to a string 'X1xX2x...xXn'
18 """
19 return 'x'.join([`N` for N in tuple])
20
21
22
23 enum = enumerate
24
25
27 """
28 A simple structure class, taking keyword args and assigning them to attributes.
29
30 For instance:
31
32 s = Struct(foo='a',bar=1)
33 >>> s.foo
34 'a'
35 >>> s.bar
36 1
37
38
39 From http://www.norvig.com/python-iaq.html
40 """
42
44
45 args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
46 return 'Struct(%s)' % ', '.join(args)
47
48
49
51 """
52 Returns a list of the indices needed to address or loop over all
53 the elements of a 1D or 2D matrix with the given shape. E.g.:
54
55 flat_indices((3,)) == [0,1,2]
56 """
57 if len(shape) == 1:
58 return [(x,) for x in range(shape[0])]
59 else:
60 rows,cols = shape
61 return [(r,c) for r in range(rows) for c in range(cols)]
62
63
64
66 """
67 Flattens a list.
68
69 Written by Bearophile as published on the www.python.org newsgroups.
70 Pulled into Topographica 3/5/2005.
71 """
72 if type(l) != list:
73 return l
74 else:
75 result = []
76 stack = []
77 stack.append((l,0))
78 while len(stack) != 0:
79 sequence, j = stack.pop(-1)
80 while j < len(sequence):
81 if type(sequence[j]) != list:
82 k, j, lens = j, j+1, len(sequence)
83 while j < len(sequence) and \
84 (type(sequence[j]) != list):
85 j += 1
86 result.extend(sequence[k:j])
87 else:
88 stack.append((sequence, j+1))
89 sequence, j = sequence[j], 0
90 return result
91
92
93
94 """
95 Return the cross-product of a variable number of lists (e.g. of a list of lists).
96
97 Use to obtain permutations, e.g.
98 l1=[a,b]
99 l2=[c,d]
100 cross_product([l1,l2]) =
101 [[a,c], [a,d], [b,c], [b,d]]
102
103
104 From:
105 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/159975
106 """
107
108 cross_product=lambda ss,row=[],level=0: len(ss)>1 \
109 and reduce(lambda x,y:x+y,[cross_product(ss[1:],row+[i],level+1) for i in ss[0]]) \
110 or [row+[i] for i in ss[0]]
111
112
113
114
115 -def frange(start, end=None, inc=1.0, inclusive=False):
116 """
117 A range function that accepts float increments.
118
119 Otherwise, works just as the inbuilt range() function. If
120 inclusive is False, as in the default, the range is exclusive (not
121 including the end value), as in the inbuilt range(). If inclusive
122 is true, the range may include the end value.
123
124 'All theoretic restrictions apply, but in practice this is
125 more useful than in theory.'
126
127 From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66472
128 """
129 if end == None:
130 end = start + 0.0
131 start = 0.0
132
133
134
135 assert ((inc>0 and start<=end) or (inc<0 and start>=end))
136
137 L = []
138 while 1:
139 next = start + len(L) * inc
140 if inclusive:
141 if inc > 0 and next > end: break
142 elif inc < 0 and next < end: break
143 else:
144 if inc > 0 and next >= end: break
145 elif inc < 0 and next <= end: break
146 L.append(next)
147
148 return L
149
150
151
153 """
154 Returns the class name of x as a string with the leading package information removed.
155
156 E.g. if x is of type "<class 'topo.base.sheet.Sheet'>", returns
157 "Sheet"
158 """
159 return re.sub("'>","",re.sub(".*[.]","",repr(type(x))))
160
161
162
163 -def profile(command,n=50,sorting=('cumulative','time'),strip_dirs=False):
164 """
165 Profile the given command (supplied as a string), printing
166 statistics about the top n functions when ordered according to
167 sorting.
168
169 sorting defaults to ordering by cumulative time and then internal
170 time; see http://docs.python.org/lib/profile-stats.html for other
171 sorting options.
172
173 By default, the complete paths of files are not shown. If there
174 are multiple files with the same name, you might wish to set
175 strip_dirs=False to make it easier to follow the output.
176
177
178 Examples:
179
180 - profile loading a simulation:
181 profile('execfile("examples/hierarchical.ty")')
182
183 - profile running an already loaded simulation:
184 profile('topo.sim.run(10)')
185
186 - profile running a whole simulation:
187 profile('execfile("examples/lissom_oo_or.ty");topo.sim.run(20000)')
188
189 - profile running a simulation, but from the commandline:
190 ./topographica examples/hierarchical.ty -c "from topo.misc.util import profile; profile('topo.sim.run(10)')"
191 """
192
193
194 import cProfile, pstats
195
196
197
198
199 prof = cProfile.run(command,'filename')
200 prof_stats = pstats.Stats('filename')
201
202 if strip_dirs:prof_stats.strip_dirs()
203
204 prof_stats.sort_stats(*sorting).print_callees(n)
205
206
207
208
209 prof_stats.sort_stats(*sorting).print_stats(n)
210
211
212
213
215 """
216 Select randomly from the given sequence.
217
218 The weights, if given, should be a sequence the same length as
219 seq, as would be passed to weighted_sample_idx().
220 """
221 if not weights:
222 return seq[random.randrange(len(seq))]
223 else:
224 assert len(weights) == len(seq)
225 return seq[weighted_sample_idx(weights)]
226
227
229 """
230 Return an integer generated by sampling the discrete distribution
231 represented by the sequence of weights.
232
233 The integer will be in the range [0,len(weights)). The weights
234 need not sum to unity, but can contain any non-negative values
235 (e.g., [1 1 1 100] is a valid set of weights).
236
237 To use weights from a 2D numpy array w, specify w.ravel() (not the
238 w.flat iterator).
239 """
240 total = sum(weights)
241 if total == 0:
242 return random.randrange(len(weights))
243 index = random.random() * total
244 accum = 0
245 for i,x in enumerate(weights):
246 accum += x
247 if index < accum:
248 return i
249
250
252 """
253 Given a flat matrix index and a 2D matrix shape, return the (row,col)
254 coordinates of the index.
255 """
256 assert len(shape) == 2
257 rows,cols = shape
258
259 return idx/cols,idx%cols
260
261
262
264 """
265 Given a row, column, and matrix shape, return the corresponding index
266 into the flattened (raveled) matrix.
267 """
268 assert len(shape) == 2
269 rows,cols = shape
270
271 return r * cols + c
272
273
274
276 """
277 Return the centroid of a weighted set of points as an array.
278
279 The pts argument should be an array of points, one per row,
280 and weights should be a vector of weights.
281 """
282
283 return numpy.dot(numpy.transpose(pts),weights)/sum(weights)
284
285
286
288 """
289 Split x into its sign and absolute value.
290
291 Returns a tuple (sign(x),abs(x)). Note: sign(0) = 1, unlike
292 numpy.sign.
293 """
294
295 if x < 0:
296 sgn = -1
297 else:
298 sgn = 1
299
300 return sgn,abs(x)
301
302
304 """
305 Interpolate an appropriate value from the given list of values, by number.
306
307 Assumes the table is a list of items to be returned for integer values,
308 and interpolates between those items for non-integer values.
309 """
310
311 lower_index=int(value)
312 upper_index=lower_index+1
313
314
315 if lower_index+1<len(table):
316 lookup=table[lower_index]+(value%1.0)*(table[upper_index]-table[lower_index])
317
318
319 elif lower_index+1==len(table):
320 lookup=table[len(table)-1]
321
322
323
324
325
326
327
328
329
330
331 else:
332 lookup=table[len(table)-1]
333 print "Warning -- value %f out of range; returning maximum of %f" % (value,lookup)
334
335 return lookup
336
337
338
339
340
342 """
343 For all file_like_objs passed on initialization, provides a
344 convenient way to call any of file's methods (on all of them).
345
346 E.g. The following would cause 'test' to be written into two
347 files, as well as to stdout:
348
349 import sys
350 f1 = open('file1','w')
351 f2 = open('file2','w')
352 m = MultiFile(f1,f2,sys.stdout)
353 m.write('test')
354 """
358
360
361
362
363
364 file_methods = [attr for attr in file.__dict__
365 if not attr.startswith('_')
366 and callable(file.__dict__[attr])]
367
368 for method in file_methods:
369 def looped_method(method_,*args,**kw):
370 all_out = []
371 for output in self.file_like_objs:
372 out = getattr(output,method_)(*args,**kw)
373 all_out.append(out)
374 return all_out
375
376 setattr(self,method,functools.partial(looped_method,method))
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392 import os,sys,imp
395 if name not in sys.modules:
396 module = self.create_module(name)
397 module.__file__ = self.path
398 sys.modules[name] = module
399 if '.' in name:
400 parent_name, child_name = name.rsplit('.', 1)
401 setattr(sys.modules[parent_name], child_name, module)
402 return sys.modules[name]
403
405 raise NotImplementedError
406
409 raise NotImplementedError
410
413 module = imp.new_module(name)
414
415
416 code = \
417 """
418 from __future__ import division
419 import topo.misc.fixedpoint as fixedpoint
420 import param
421 class mpq(object):
422 def __new__(self,*args,**kw):
423 n = fixedpoint.FixedPoint(eval(str(args[0])),precision=4)
424 param.Parameterized().warning("gmpy.mpq('%s') replaced by fixedpoint.FixedPoint('%s')"%(args[0],n))
425 return n
426 """
427 exec code in module.__dict__
428 return module
429
431
433 if fullname == 'gmpy' or fullname.startswith('gmpy.'):
434 import param
435 param.Parameterized().warning('Module "gmpy" is not available. gmpy.mpq is provided by using fixedpoint.FixedPoint.')
436 g = gmpyFaker()
437 g.path = path
438 return g
439 return None
440