root/branches/0.3/random.py

Revision 26, 5.3 kB (checked in by dejan, 2 years ago)

Another update so the new changes in common and random are compatible with pyNN.pcsim

  • Property svn:eol-style set to native
  • Property svn:keywords set to Revision Id
Line 
1 """
2 Provides wrappers for several random number generators, giving them all a
3 common interface so that they can be used interchangeably in PyNN.
4
5 Note however that we have so far made no effort to implement parameter translation,
6 and parameter names/order may be different for the different RNGs.
7
8 $Id$
9 """
10
11 import sys
12 import numpy.random
13 try:
14     import pygsl.rng
15 except ImportError:
16     print "Warning: GSL random number generators not available"
17 import time
18
19 # The following two functions taken from
20 # http://www.nedbatchelder.com/text/pythonic-interfaces.html
21 def _functionId(obj, nFramesUp):
22     """ Create a string naming the function n frames up on the stack. """
23     fr = sys._getframe(nFramesUp+1)
24     co = fr.f_code
25     return "%s.%s" % (obj.__class__, co.co_name)
26  
27 def abstractMethod(obj=None):
28     """ Use this instead of 'pass' for the body of abstract methods. """
29     raise Exception("Unimplemented abstract method: %s" % _functionId(obj, 1))
30  
31  
32 class AbstractRNG:
33     """Abstract class for wrapping random number generators. The idea is to be able
34     to use either simulator-native rngs, which may be more efficient, or a
35     standard python rng, e.g. a numpy.random.RandomState object, which would
36     allow the same random numbers to be used across different simulators, or
37     simply to read externally-generated numbers from files."""
38    
39     def __init__(self,seed=None):
40         if seed:
41             assert isinstance(seed,int)
42         self.seed = seed
43         # define some aliases
44         self.random = self.next
45         self.sample = self.next
46    
47     def next(self,n=1,distribution='uniform',parameters=[]):
48         """Return n random numbers from the distribution.
49         
50         If n is 1, return a float, if n > 1, return a numpy array,
51         if n <= 0, raise an Exception."""
52         abstractMethod(self)
53
54    
55 class NumpyRNG(AbstractRNG):
56     """Wrapper for the numpy.random.RandomState class (Mersenne Twister PRNG)."""
57    
58     def __init__(self,seed=None):
59         AbstractRNG.__init__(self,seed)
60         self.rng = numpy.random.RandomState()
61         if self.seed  :
62             self.rng.seed(self.seed)
63         else:
64             self.rng.seed()
65            
66     def __getattr__(self, name):
67         """This is to give NumpyRNG the same methods as numpy.random.RandomState."""
68         return getattr(self.rng,name)
69    
70     def next(self,n=1,distribution='uniform',parameters=[]):
71         """Return n random numbers from the distribution.
72         
73         If n is 1, return a float, if n > 1, return a numpy array,
74         if n <= 0, raise an Exception."""
75         if n > 1:
76            return getattr(self.rng,distribution)(size=n,*parameters)
77         elif n == 1:
78             return getattr(self.rng,distribution)(size=1,*parameters)[0]
79         else:
80             raise ValueError, "The sample number must be positive"
81
82
83 class GSLRNG(AbstractRNG):
84     """Wrapper for the GSL random number generators."""
85        
86     def __init__(self,seed=None,type='mt19937'):
87         AbstractRNG.__init__(self,seed)
88         self.rng = getattr(pygsl.rng,type)()
89         if self.seed  :
90             self.rng.set(self.seed)
91         else:
92             self.seed = int(time.time())
93             self.rng.set(self.seed)
94    
95     def __getattr__(self, name):
96         """This is to give GSLRNG the same methods as the GSL RNGs."""
97         return getattr(self.rng,name)
98    
99     def next(self,n=1,distribution='uniform',parameters=[]):
100         """Return n random numbers from the distribution.
101         
102         If n is 1, return a float, if n > 1, return a numpy array,
103         if n <= 0, raise an Exception."""
104         p = parameters + [n]
105         return getattr(self.rng,distribution)(*p)
106
107    
108 class NativeRNG(AbstractRNG):
109     """Signals that the simulator's own native RNG should be used.
110     Each simulator module should implement a class of the same name which
111     inherits from this and which sets the seed appropriately."""
112     pass
113
114
115 class RandomDistribution:
116     """Class which defines a next(n) method which returns an array of n random
117        numbers from a given distribution."""
118        
119     def __init__(self,rng=None,distribution='uniform',parameters=[]):
120         """
121         If present, rng should be a NumpyRNG or GSLRNG object.
122         distribution should be the name of a method supported by the underlying
123             random number generator object.
124         parameters should be a list or tuple containing the arguments expected
125             by the underlying method in the correct order. named arguments are
126             not yet supported.
127         Note that NumpyRNG and GSLRNG distributions may not have the same names,
128             e.g., 'normal' for NumpyRNG and 'gaussian' for GSLRNG, and the
129             arguments may also differ.
130         """
131         self.name = distribution
132         assert isinstance(parameters,(list,tuple,dict)), "The parameters argument must be a list or tuple or dict"
133         self.parameters = parameters
134         if rng:
135             assert isinstance(rng,AbstractRNG), "rng must be a pyNN.random RNG object"
136             self.rng = rng
137         else: # use numpy.random.RandomState() by default
138             self.rng = NumpyRNG()
139        
140     def next(self,n=1):
141         """Return n random numbers from the distribution."""
142         return self.rng.next(n=n,distribution=self.name,parameters=self.parameters)
143        
Note: See TracBrowser for help on using the browser.