1 """
2 A collection of classes that, when called, generate numbers
3 according to different distributions (e.g. random numbers).
4
5 $Id: basic.py 10672 2009-10-28 01:00:50Z ceball $
6 """
7 __version__='$Revision: 8985 $'
8
9 import random
10 import operator
11
12 from math import e,pi
13
14 import param
15
16
17
19 """
20 Abstract base class for any object that when called produces a number.
21
22 Primarily provides support for using NumberGenerators in simple
23 arithmetic expressions, such as abs((x+y)/z), where x,y,z are
24 NumberGenerators or numbers.
25 """
26
28 raise NotImplementedError
29
30
39
48
52
53
54
56 """Applies any binary operator to NumberGenerators or numbers to yield a NumberGenerator."""
57
58 - def __init__(self,lhs,rhs,operator,reverse=False,**args):
59 """
60 Accepts two NumberGenerator operands, an operator, and
61 optional arguments to be provided to the operator when calling
62 it on the two operands.
63 """
64
65
66
67 super(BinaryOperator,self).__init__()
68
69 if reverse:
70 self.lhs=rhs
71 self.rhs=lhs
72 else:
73 self.lhs=lhs
74 self.rhs=rhs
75 self.operator=operator
76 self.args=args
77
79 return self.operator(self.lhs() if callable(self.lhs) else self.lhs,
80 self.rhs() if callable(self.rhs) else self.rhs, **self.args)
81
82
83
85 """Applies any unary operator to a NumberGenerator to yield another NumberGenerator."""
86
87 - def __init__(self,operand,operator,**args):
88 """
89 Accepts a NumberGenerator operand, an operator, and
90 optional arguments to be provided to the operator when calling
91 it on the operand.
92 """
93
94
95
96 super(UnaryOperator,self).__init__()
97
98 self.operand=operand
99 self.operator=operator
100 self.args=args
101
104
105
106
108 """
109 Python's random module provides the Random class, which can be
110 instantiated to give an object that can be asked to generate
111 numbers from any of several different random distributions
112 (e.g. uniform, Gaussian).
113
114 To make it easier to use these, Topographica provides here a
115 hierarchy of classes, each tied to a particular random
116 distribution. This allows setting parameters on creation rather
117 than passing them each call, and allows pickling to work properly.
118
119 The underlying random.Random() instance and all its methods can be
120 accessed from the 'random_generator' attribute.
121 """
122 __abstract = True
123
125 """
126 Initialize a new Random() instance and store the supplied
127 positional and keyword arguments.
128
129 If seed=X is specified, sets the Random() instance's seed.
130 Otherwise, calls the instance's jumpahead() method to get a
131 state very likely to be different from any just used.
132 """
133 self.random_generator = random.Random()
134
135 if 'seed' in params:
136 self.random_generator.seed(params['seed'])
137 del params['seed']
138 else:
139 self.random_generator.jumpahead(10)
140
141 super(RandomDistribution,self).__init__(**params)
142
144 raise NotImplementedError
145
146
159
160
174
175
176 -class Choice(RandomDistribution):
177 """
178 Return a random element from the specified list of choices.
179
180 Accepts items of any type, though they are typically numbers.
181 See the choice() function in the random module for further details.
182 """
183 choices = param.List(default=[0,1],
184 doc="List of items from which to select.")
185
188
189
191 """
192 Normally distributed (Gaussian) random number.
193
194 Specified with mean mu and standard deviation sigma.
195 See the random module for further details.
196 """
197 mu = param.Number(default=0.0,doc="Mean value.")
198 sigma = param.Number(default=1.0,doc="Standard deviation.")
199
202
203
205 """
206 Circularly normal distributed random number.
207
208 If kappa is zero, this distribution reduces to a uniform random
209 angle over the range 0 to 2*pi. Otherwise, it is concentrated to
210 a greater or lesser degree (determined by kappa) around the mean
211 mu. For large kappa (narrow peaks), this distribution approaches
212 the Gaussian (normal) distribution with variance 1/kappa. See the
213 random module for further details.
214 """
215
216 mu = param.Number(default=0.0,softbounds=(0.0,2*pi),doc="""
217 Mean value, in the range 0 to 2*pi.""")
218
219 kappa = param.Number(default=1.0,softbounds=(0.0,50.0),doc="""
220 Concentration (inverse variance).""")
221
224
225
226 import topo
228 """
229 Function object that provides a value that decays according to an
230 exponential function, based on topo.sim.time().
231
232 Returns starting_value*base^(-time/time_constant).
233
234 See http://en.wikipedia.org/wiki/Exponential_decay.
235 """
236 starting_value = param.Number(1.0, doc="Value used for time zero.")
237 ending_value = param.Number(0.0, doc="Value used for time infinity.")
238
239 time_constant = param.Number(10000,doc="""
240 Time scale for the exponential; large values give slow decay.""")
241
242 base = param.Number(e, doc="""
243 Base of the exponent; the default yields starting_value*exp(-t/time_constant).
244 Another popular choice of base is 2, which allows the
245 time_constant to be interpreted as a half-life.""")
246
247
248
249 time_fn = param.Callable(default=topo.sim.time,doc="""
250 Function to generate the time used for the decay.""")
251
257
258
260 """
261 Function object that silently enforces numeric bounds on values
262 returned by a callable object.
263 """
264 generator = param.Callable(None, doc="Object to call to generate values.")
265
266 bounds = param.Parameter((None,None), doc="""
267 Legal range for the value returned, as a pair.
268
269 The default bounds are (None,None), meaning there are actually
270 no bounds. One or both bounds can be set by specifying a
271 value. For instance, bounds=(None,10) means there is no lower
272 bound, and an upper bound of 10.""")
273
275 val = self.generator()
276 min_, max_ = self.bounds
277 if min_ != None and val < min_: return min_
278 elif max_ != None and val > max_: return max_
279 else: return val
280
281
282
283
284 __all__ = list(set([k for k,v in locals().items() if isinstance(v,type) and issubclass(v,NumberGenerator)]))
285