Package topo :: Package misc :: Module utils
[hide private]
[frames] | no frames]

Source Code for Module topo.misc.utils

  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   
18 -def NxN(tuple):
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
29 -class Struct:
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 """
44 - def __init__(self, **entries): self.__dict__.update(entries)
45
46 - def __repr__(self):
47 # 48 args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()] 49 return 'Struct(%s)' % ', '.join(args)
50 51 52
53 -def flat_indices(shape):
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
68 -def flatten(l):
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 # CEBNOTE: use this for tkparameterizedobject or delete it
97 -def eval_atof(str):
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 # Need to re-write so someone other than Python knows what might happen when this runs 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 # JABALERT: Should frange be replaced with numpy.arange or numpy.linspace?
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 # Increments of zero would lead to an infinite loop, which can happen if 151 # this is mistakenly called with a integer-based rational expression like 1/2. 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 # CB: this function isn't used, as of 2006/06/22
169 -def values_sorted_by_key(d):
170 """ 171 Return the values of dictionary d sorted by key. 172 """ 173 # By Alex Martelli, 2001/04/08 174 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52306 175 keys = d.keys() 176 keys.sort() 177 return map(d.get, keys)
178 179
180 -def keys_sorted_by_value(d):
181 """ 182 Return the keys of dictionary d sorted by value. 183 """ 184 # By Daniel Schult, 2004/01/23 185 # http://aspn.activestate.com/ASPN/Python/Cookbook/Recipe/52306 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
192 -def inverse(dict_):
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
207 -def shortclassname(x):
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 # This function simply wraps some functions from the cProfile 248 # module, making profiling easier. 249 250 # CB: leaves around "filename": should give this a proper name and maybe 251 # put in /tmp/ and maybe allow someone to choose where to save it 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 ### the above lets us see which times are due to which calls 259 ### unambiguously, while the version below only reports total time 260 ### spent in each object, not the time due to that particular 261 ### call. 262 prof_stats.sort_stats(*sorting).print_stats(n)
263 264 265 266
267 -def weighted_sample(seq,weights=[]):
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
281 -def weighted_sample_idx(weights):
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
304 -def idx2rowcol(idx,shape):
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
316 -def rowcol2idx(r,c,shape):
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
328 -def centroid(pts,weights):
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 # CEBALERT: use numpy.sum? Worthwhile if weights is a numpy.array. 336 return numpy.dot(numpy.transpose(pts),weights)/sum(weights)
337
338 -def signabs(x):
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 # CEBALERT: would it be simpler just to have a dictionary/list 356 # somewhere of extra things to pickle, and pickle that in 357 # save_snapshot()? What about supporting things other than 358 # module-level attributes? 359 360
361 -class Singleton(object):
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 """
373 - def __new__(cls, *args, **kwds):
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 # CB: our addition
386 - def __reduce_ex__(self,p):
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
396 -class ExtraPickler(Singleton):
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
407 - def add(self,item):
408 self.extras.append(item)
409
410 - def __reduce_ex__(self,p):
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
418 - def __setstate__(self,state):
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 # CEBALERT: same alert about setting val in main as for 430 # the parameter pickler. surely it's not required? at least 431 # prefix __ or something. 432 __main__.__dict__['val']=item 433 434 exec "import %s"%module in __main__.__dict__ 435 436 # complication: we're about to set x.y.z=item, but 437 # the same thing might also be __main__.__dict__['z'] 438 # (i.e. main's z might BE x.y.z; when we write over x.y.z, 439 # we then also need to write over main's z) 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: # can't just check name! 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