1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 import numpy as np
32 from .core.parameter_core import Parameterizable, adjust_name_for_printing
33 from .core.observable_array import ObsAr
34 from .core.pickleable import Pickleable
35 from functools import reduce
36 from collections import OrderedDict
37
38
39
40 __index_name__ = "index"
41 try:
42 __precision__ = np.get_printoptions()['precision']
43 except:
44 __precision__ = 8
45
46
47 __print_threshold__ = 5
48
49
50 -class Param(Parameterizable, ObsAr):
51 """
52 Parameter object for GPy models.
53
54 :param str name: name of the parameter to be printed
55 :param input_array: array which this parameter handles
56 :type input_array: np.ndarray
57 :param default_constraint: The default constraint for this parameter
58 :type default_constraint:
59
60 You can add/remove constraints by calling constrain on the parameter itself, e.g:
61
62 - self[:,1].constrain_positive()
63 - self[0].tie_to(other)
64 - self.untie()
65 - self[:3,:].unconstrain()
66 - self[1].fix()
67
68 Fixing parameters will fix them to the value they are right now. If you change
69 the fixed value, it will be fixed to the new value!
70
71 Important Notes:
72
73 The array given into this, will be used as the Param object. That is, the
74 memory of the numpy array given will be the memory of this object. If
75 you want to make a new Param object you need to copy the input array!
76
77 Multilevel indexing (e.g. self[:2][1:]) is not supported and might lead to unexpected behaviour.
78 Try to index in one go, using boolean indexing or the numpy builtin
79 np.index function.
80
81 See :py:class:`GPy.core.parameterized.Parameterized` for more details on constraining etc.
82
83 """
84 __array_priority__ = -1
85 _fixes_ = None
86 parameters = []
87 - def __new__(cls, name, input_array, default_constraint=None):
88 obj = np.atleast_1d(super(Param, cls).__new__(cls, input_array=input_array))
89 obj._current_slice_ = (slice(obj.shape[0]),)
90 obj._realshape_ = obj.shape
91 obj._realsize_ = obj.size
92 obj._realndim_ = obj.ndim
93 obj._original_ = obj
94 return obj
95
96 - def __init__(self, name, input_array, default_constraint=None, *a, **kw):
97 self._in_init_ = True
98 super(Param, self).__init__(name=name, default_constraint=default_constraint, *a, **kw)
99 self._in_init_ = False
100
102
103 if obj is None: return
104 super(Param, self).__array_finalize__(obj)
105 self._parent_ = getattr(obj, '_parent_', None)
106 self._parent_index_ = getattr(obj, '_parent_index_', None)
107 self._default_constraint_ = getattr(obj, '_default_constraint_', None)
108 self._current_slice_ = getattr(obj, '_current_slice_', None)
109 self._realshape_ = getattr(obj, '_realshape_', None)
110 self._realsize_ = getattr(obj, '_realsize_', None)
111 self._realndim_ = getattr(obj, '_realndim_', None)
112 self._original_ = getattr(obj, '_original_', None)
113 self._name = getattr(obj, '_name', None)
114 self._gradient_array_ = getattr(obj, '_gradient_array_', None)
115 self._update_on = getattr(obj, '_update_on', None)
116 try:
117 self._index_operations = obj._index_operations
118 except AttributeError:
119 pass
120
121
122
123
124 @property
126 """
127 As we are a leaf, this just returns self
128 """
129 return self
130
131 @property
133 """
134 Return self as numpy array view
135 """
136 return self.view(np.ndarray)
137
138 @property
140 """
141 Return a view on the gradient, which is in the same shape as this parameter is.
142 Note: this is not the real gradient array, it is just a view on it.
143
144 To work on the real gradient array use: self.full_gradient
145 """
146 if getattr(self, '_gradient_array_', None) is None:
147 self._gradient_array_ = np.empty(self._realshape_, dtype=np.float64)
148 return self._gradient_array_
149
150 @gradient.setter
153
154
155
156
158 if not isinstance(s, tuple):
159 s = (s,)
160
161
162 new_arr = super(Param, self).__getitem__(s, *args, **kwargs)
163 try:
164 new_arr._current_slice_ = s
165 new_arr._gradient_array_ = self.gradient[s]
166 new_arr._original_ = self._original_
167 except AttributeError: pass
168 return new_arr
169
171
172
173 extended_realshape = np.cumprod((1,) + self._realshape_[:0:-1])[::-1]
174 ind = self._indices(slice_index)
175 if ind.ndim < 2: ind = ind[:, None]
176 return np.asarray(np.apply_along_axis(lambda x: np.sum(extended_realshape * x), 1, ind), dtype=int)
177
180
181
182
183
185 if (not hasattr(self, "_fixes_")) or (self._fixes_ is None) or (self._fixes_.size != self._realsize_): self._fixes_ = np.ones(self._realsize_, dtype=bool)
186
187
188
189
190 @property
194
196 return self._original_
197
198
199
200
203
210
219
220
221
222
223 @property
225 if self.size <= 1:
226 return [str(self.view(np.ndarray)[0])]
227 else: return [str(self.shape)]
228 - def parameter_names(self, add_self=False, adjust_for_printing=False, recursive=True, **kw):
234 @property
237 @property
240
242 prop = self._index_operations[propname]
243 return [' '.join(map(lambda c: str(c[0]) if c[1].size == self._realsize_ else "{" + str(c[0]) + "}", prop.items()))]
244
250
251 if slice_index is None:
252 slice_index = self._current_slice_
253
254 indices = np.indices(self._realshape_, dtype=int)
255 indices = indices[(slice(None),)+slice_index]
256 indices = np.rollaxis(indices, 0, indices.ndim).reshape(-1,self._realndim_)
257
258
259
260
261
262
263
264 return indices
265
267 return reduce(lambda a, b: max(a, len(" ".join(map(str, b)))), gen, len(header))
268
271
273 return reduce(lambda a, b: max(a, len(str(b))), ind, len(__index_name__))
274
275 - def _repr_html_(self, indices=None, iops=None, lx=None, li=None, lls=None):
276 """Representation of the parameter in html for notebook display."""
277 filter_ = self._current_slice_
278 vals = self.flat
279 if indices is None: indices = self._indices(filter_)
280 if iops is None:
281 ravi = self._raveled_index(filter_)
282 iops = OrderedDict([name, iop.properties_for(ravi)] for name, iop in self._index_operations.items())
283 if lls is None: lls = [self._max_len_names(iop, name) for name, iop in iops.items()]
284
285 header_format = """
286 <tr>
287 <th><b>{i}</b></th>
288 <th><b>{x}</b></th>
289 <th><b>{iops}</b></th>
290 </tr>"""
291 header = header_format.format(x=self.hierarchy_name(), i=__index_name__, iops="</b></th><th><b>".join(list(iops.keys())))
292
293 to_print = ["""<style type="text/css">
294 .tg {padding:2px 3px;word-break:normal;border-collapse:collapse;border-spacing:0;border-color:#DCDCDC;margin:0px auto;width:100%;}
295 .tg td{font-family:"Courier New", Courier, monospace !important;font-weight:bold;color:#444;background-color:#F7FDFA;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#DCDCDC;}
296 .tg th{font-family:"Courier New", Courier, monospace !important;font-weight:normal;color:#fff;background-color:#26ADE4;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#DCDCDC;}
297 .tg .tg-left{font-family:"Courier New", Courier, monospace !important;font-weight:normal;text-align:left;}
298 .tg .tg-right{font-family:"Courier New", Courier, monospace !important;font-weight:normal;text-align:right;}
299 </style>"""]
300 to_print.append('<table class="tg">')
301 to_print.append(header)
302
303 format_spec = self._format_spec(indices, iops, lx, li, lls, False)
304 format_spec[:2] = ["<tr><td class=tg-left>{i}</td>".format(i=format_spec[0]), "<td class=tg-right>{i}</td>".format(i=format_spec[1])]
305 for i in range(2, len(format_spec)):
306 format_spec[i] = '<td class=tg-left>{c}</td>'.format(c=format_spec[i])
307 format_spec = "".join(format_spec) + '</tr>'
308
309 for i in range(self.size):
310 to_print.append(format_spec.format(index=indices[i], value="{1:.{0}f}".format(__precision__, vals[i]), **dict((name, ' '.join(map(str, iops[name][i]))) for name in iops)))
311 return '\n'.join(to_print)
312
327
328
329 - def __str__(self, indices=None, iops=None, lx=None, li=None, lls=None, only_name=False, VT100=True):
330 filter_ = self._current_slice_
331 vals = self.flat
332 if indices is None: indices = self._indices(filter_)
333 if iops is None:
334 ravi = self._raveled_index(filter_)
335 iops = OrderedDict([name, iop.properties_for(ravi)] for name, iop in self._index_operations.items())
336 if lls is None: lls = [self._max_len_names(iop, name) for name, iop in iops.items()]
337
338 format_spec = ' | '.join(self._format_spec(indices, iops, lx, li, lls, VT100))
339
340 to_print = []
341
342 if not only_name: to_print.append(format_spec.format(index=__index_name__, value=self.hierarchy_name(), **dict((name, name) for name in iops)))
343 else: to_print.append(format_spec.format(index='-'*li, value=self.hierarchy_name(), **dict((name, '-'*l) for name, l in zip(iops, lls))))
344
345 for i in range(self.size):
346 to_print.append(format_spec.format(index=indices[i], value="{1:.{0}f}".format(__precision__, vals[i]), **dict((name, ' '.join(map(str, iops[name][i]))) for name in iops)))
347 return '\n'.join(to_print)
348
350 """
351 Build a pydot representation of this model. This needs pydot installed.
352
353 Example Usage:
354
355 np.random.seed(1000)
356 X = np.random.normal(0,1,(20,2))
357 beta = np.random.uniform(0,1,(2,1))
358 Y = X.dot(beta)
359 m = RidgeRegression(X, Y)
360 G = m.build_pydot()
361 G.write_png('example_hierarchy_layout.png')
362
363 The output looks like:
364
365 .. image:: example_hierarchy_layout.png
366
367 Rectangles are parameterized objects (nodes or leafs of hierarchy).
368
369 Trapezoids are param objects, which represent the arrays for parameters.
370
371 Black arrows show parameter hierarchical dependence. The arrow points
372 from parents towards children.
373
374 Orange arrows show the observer pattern. Self references (here) are
375 the references to the call to parameters changed and references upwards
376 are the references to tell the parents they need to update.
377 """
378 import pydot
379 node = pydot.Node(id(self), shape='trapezium', label=self.name)
380 G.add_node(node)
381 for _, o, _ in self.observers:
382 label = o.name if hasattr(o, 'name') else str(o)
383 observed_node = pydot.Node(id(o), label=label)
384 if str(id(o)) not in G.obj_dict['nodes']:
385 G.add_node(observed_node)
386 edge = pydot.Edge(str(id(self)), str(id(o)), color='darkorange2', arrowhead='vee')
387 G.add_edge(edge)
388
389 return node
390
393 """
394 Parameter concatenation for convenience of printing regular expression matched arrays
395 you can index this concatenation as if it was the flattened concatenation
396 of all the parameters it contains, same for setting parameters (Broadcasting enabled).
397
398 See :py:class:`GPy.core.parameter.Param` for more details on constraining.
399 """
400
401 from .core.lists_and_dicts import ArrayList
402 self.params = ArrayList([])
403 for p in params:
404 for p in p.flattened_parameters:
405 if p not in self.params:
406 self.params.append(p)
407 self._param_sizes = [p.size for p in self.params]
408 startstops = np.cumsum([0] + self._param_sizes)
409 self._param_slices_ = [slice(start, stop) for start,stop in zip(startstops, startstops[1:])]
410
411 parents = dict()
412 for p in self.params:
413 if p.has_parent():
414 parent = p._parent_
415 level = 0
416 while parent is not None:
417 if parent in parents:
418 parents[parent] = max(level, parents[parent])
419 else:
420 parents[parent] = level
421 level += 1
422 parent = parent._parent_
423 import operator
424
425
426 self.parents = map(lambda x: x[0], sorted(parents.items(), key=operator.itemgetter(1)))
427
428
429
431 ind = np.zeros(sum(self._param_sizes), dtype=bool); ind[s] = True;
432 params = [p.param_array.flat[ind[ps]] for p,ps in zip(self.params, self._param_slices_) if np.any(p.param_array.flat[ind[ps]])]
433 if len(params)==1: return params[0]
434 return ParamConcatenation(params)
435
437 if isinstance(val, ParamConcatenation):
438 val = val.values()
439 ind = np.zeros(sum(self._param_sizes), dtype=bool); ind[s] = True;
440 vals = self.values(); vals[s] = val
441 for p, ps in zip(self.params, self._param_slices_):
442 p.flat[ind[ps]] = vals[ps]
443 self.update_all_params()
444
446 return np.hstack([p.param_array.flat for p in self.params])
447
448
449
451 for par in self.parents:
452 par.trigger_update(trigger_parent=False)
453
454 - def constrain(self, constraint, warning=True):
457 constrain.__doc__ = Param.constrain.__doc__
458
462 constrain_positive.__doc__ = Param.constrain_positive.__doc__
463
466 constrain_fixed.__doc__ = Param.constrain_fixed.__doc__
467 fix = constrain_fixed
468
472 constrain_negative.__doc__ = Param.constrain_negative.__doc__
473
477 constrain_bounded.__doc__ = Param.constrain_bounded.__doc__
478
481 unconstrain.__doc__ = Param.unconstrain.__doc__
482
485 unconstrain_negative.__doc__ = Param.unconstrain_negative.__doc__
486
489 unconstrain_positive.__doc__ = Param.unconstrain_positive.__doc__
490
493 unconstrain_fixed.__doc__ = Param.unconstrain_fixed.__doc__
494 unfix = unconstrain_fixed
495
498 unconstrain_bounded.__doc__ = Param.unconstrain_bounded.__doc__
499
500
501
502
503 - def checkgrad(self, verbose=False, step=1e-6, tolerance=1e-3):
505
506
507 __lt__ = lambda self, val: self.values() < val
508 __le__ = lambda self, val: self.values() <= val
509 __eq__ = lambda self, val: self.values() == val
510 __ne__ = lambda self, val: self.values() != val
511 __gt__ = lambda self, val: self.values() > val
512 __ge__ = lambda self, val: self.values() >= val
513
515 params = self.params
516
517 indices = [p._indices() for p in params]
518 lx = max([p._max_len_values() for p in params])
519 li = max([p._max_len_index(i) for p, i in zip(params, indices)])
520
521 lls = None
522 params_iops = []
523 for p in params:
524 filter_ = p._current_slice_
525 ravi = p._raveled_index(filter_)
526 iops = OrderedDict([name, iop.properties_for(ravi)] for name, iop in p._index_operations.items())
527 _lls = [p._max_len_names(iop, name) for name, iop in iops.items()]
528 if lls is None:
529 lls = _lls
530 else:
531 for i in range(len(lls)):
532 lls[i] = max(lls[i], _lls[i])
533 params_iops.append(iops)
534
535 strings = []
536 start = True
537
538 for i in range(len(params)):
539 strings.append(params[i].__str__(indices=indices[i], iops=params_iops[i], lx=lx, li=li, lls=lls, only_name=(not start), **kwargs))
540 start = False
541 i += 1
542
543 return "\n".join(strings)
545 return "\n".join(map(repr,self.params))
546
548 self[:] = np.ndarray.__ilshift__(self.values(), *args, **kwargs)
549
551 self[:] = np.ndarray.__irshift__(self.values(), *args, **kwargs)
552
554 self[:] = np.ndarray.__ixor__(self.values(), *args, **kwargs)
555
557 self[:] = np.ndarray.__ipow__(self.values(), *args, **kwargs)
558
560 self[:] = np.ndarray.__ifloordiv__(self.values(), *args, **kwargs)
561
563 self[:] = np.ndarray.__isub__(self.values(), *args, **kwargs)
564
565 - def __ior__(self, *args, **kwargs):
566 self[:] = np.ndarray.__ior__(self.values(), *args, **kwargs)
567
569 self[:] = np.ndarray.__itruediv__(self.values(), *args, **kwargs)
570
572 self[:] = np.ndarray.__idiv__(self.values(), *args, **kwargs)
573
575 self[:] = np.ndarray.__iand__(self.values(), *args, **kwargs)
576
578 self[:] = np.ndarray.__imod__(self.values(), *args, **kwargs)
579
581 self[:] = np.ndarray.__iadd__(self.values(), *args, **kwargs)
582
584 self[:] = np.ndarray.__imul__(self.values(), *args, **kwargs)
585