2013-10-02 12:11:53 +01:00
'''
Created on 4 Sep 2013
@author : maxz
'''
import itertools
import numpy
2013-10-17 14:33:41 +01:00
from transformations import Logexp , NegativeLogexp , Logistic
2013-11-06 11:40:54 +00:00
from parameterized import Nameable , Pickleable , Observable
from GPy . core . parameterized import _adjust_name_for_printing
2013-10-02 12:11:53 +01:00
2013-10-07 07:40:48 +01:00
###### printing
__constraints_name__ = " Constraint "
__index_name__ = " Index "
__tie_name__ = " Tied to "
__precision__ = numpy . get_printoptions ( ) [ ' precision ' ] # numpy printing precision used, sublassing numpy ndarray after all
2013-10-18 16:20:01 +01:00
__print_threshold__ = 5
2013-10-25 15:29:04 +01:00
######
2013-10-07 07:40:48 +01:00
2013-10-27 17:04:46 +00:00
class ListArray ( numpy . ndarray ) :
"""
ndarray which can be stored in lists and checked if it is in .
"""
def __new__ ( cls , input_array ) :
obj = numpy . asanyarray ( input_array ) . view ( cls )
return obj
def __eq__ ( self , other ) :
return other is self
2013-11-06 11:40:54 +00:00
class ObservableArray ( ListArray , Observable ) :
2013-10-27 17:04:46 +00:00
"""
An ndarray which reports changed to it ' s observers.
The observers can add themselves with a callable , which
will be called every time this array changes . The callable
takes exactly one argument , which is this array itself .
"""
def __new__ ( cls , input_array ) :
obj = super ( ObservableArray , cls ) . __new__ ( cls , input_array ) . view ( cls )
obj . _observers_ = { }
return obj
def __array_finalize__ ( self , obj ) :
# see InfoArray.__array_finalize__ for comments
if obj is None : return
self . _observers_ = getattr ( obj , ' _observers_ ' , None )
2013-11-06 11:40:54 +00:00
def __setitem__ ( self , s , val , update = True ) :
2013-10-27 17:04:46 +00:00
if not numpy . all ( numpy . equal ( self [ s ] , val ) ) :
2013-11-06 11:40:54 +00:00
super ( ObservableArray , self ) . __setitem__ ( s , val )
if update :
self . _notify_observers ( )
2013-10-27 17:04:46 +00:00
def __getslice__ ( self , start , stop ) :
return self . __getitem__ ( slice ( start , stop ) )
def __setslice__ ( self , start , stop , val ) :
return self . __setitem__ ( slice ( start , stop ) , val )
class Param ( ObservableArray , Nameable , Pickleable ) :
2013-10-02 12:11:53 +01:00
"""
2013-10-15 09:01:03 +01:00
Parameter object for GPy models .
2013-10-02 12:11:53 +01:00
2013-10-16 21:08:35 +01:00
: param name : name of the parameter to be printed
: param input_array : array which this parameter handles
: param gradient : callable with one argument , which is the model of this parameter
2013-10-17 14:33:41 +01:00
: param args : additional arguments to gradient
: param kwargs : additional keyword arguments to gradient
2013-10-15 09:01:03 +01:00
You can add / remove constraints by calling the constrain on the parameter itself , e . g :
2013-10-11 16:44:34 +01:00
2013-10-15 09:01:03 +01:00
- self [ : , 1 ] . constrain_positive ( )
- self [ 0 ] . tie_to ( other )
- self . untie ( )
- self [ : 3 , : ] . unconstrain ( )
- self [ 1 ] . fix ( )
2013-10-11 16:44:34 +01:00
2013-10-15 09:01:03 +01:00
Fixing parameters will fix them to the value they are right now . If you change
the fixed value , it will be fixed to the new value !
2013-10-07 07:40:48 +01:00
2013-10-15 09:01:03 +01:00
See : py : class : ` GPy . core . parameterized . Parameterized ` for more details .
2013-10-07 07:40:48 +01:00
"""
2013-10-16 21:08:35 +01:00
__array_priority__ = - numpy . inf # Never give back Param
2013-10-25 15:29:04 +01:00
def __new__ ( cls , name , input_array , * args , * * kwargs ) :
2013-10-27 17:04:46 +00:00
obj = numpy . atleast_1d ( super ( Param , cls ) . __new__ ( cls , input_array = input_array ) )
2013-10-25 15:29:04 +01:00
obj . _direct_parent_ = None
2013-11-03 13:58:15 +00:00
#obj.name = name
2013-10-16 21:08:35 +01:00
obj . _parent_index_ = None
2013-10-25 15:29:04 +01:00
obj . _highest_parent_ = None
2013-10-17 14:33:41 +01:00
obj . _current_slice_ = ( slice ( obj . shape [ 0 ] ) , )
2013-10-16 21:08:35 +01:00
obj . _realshape_ = obj . shape
obj . _realsize_ = obj . size
obj . _realndim_ = obj . ndim
2013-10-22 13:39:58 +01:00
obj . _updated_ = False
2013-10-17 20:47:41 +01:00
from index_operations import ParamDict
obj . _tied_to_me_ = ParamDict ( set )
obj . _tied_to_ = [ ]
2013-10-17 14:33:41 +01:00
obj . _original_ = True
2013-10-27 17:04:46 +00:00
return obj
def __init__ ( self , name , input_array ) :
super ( Param , self ) . __init__ ( name = name )
2013-10-02 19:18:23 +01:00
def __array_finalize__ ( self , obj ) :
# see InfoArray.__array_finalize__ for comments
if obj is None : return
2013-11-03 13:58:15 +00:00
super ( Param , self ) . __array_finalize__ ( obj )
self . name = getattr ( obj , ' name ' , None )
2013-10-16 21:08:35 +01:00
self . _current_slice_ = getattr ( obj , ' _current_slice_ ' , None )
2013-10-25 15:29:04 +01:00
self . _direct_parent_ = getattr ( obj , ' _direct_parent_ ' , None )
2013-10-16 21:08:35 +01:00
self . _parent_index_ = getattr ( obj , ' _parent_index_ ' , None )
2013-10-25 15:29:04 +01:00
self . _highest_parent_ = getattr ( obj , ' _highest_parent_ ' , None )
2013-10-17 20:47:41 +01:00
self . _tied_to_me_ = getattr ( obj , ' _tied_to_me_ ' , None )
self . _tied_to_ = getattr ( obj , ' _tied_to_ ' , None )
2013-10-16 21:08:35 +01:00
self . _realshape_ = getattr ( obj , ' _realshape_ ' , None )
self . _realsize_ = getattr ( obj , ' _realsize_ ' , None )
self . _realndim_ = getattr ( obj , ' _realndim_ ' , None )
2013-10-22 13:39:58 +01:00
self . _updated_ = getattr ( obj , ' _updated_ ' , None )
2013-10-17 14:33:41 +01:00
self . _original_ = getattr ( obj , ' _original_ ' , None )
2013-10-22 13:39:58 +01:00
2013-10-16 21:08:35 +01:00
def __array_wrap__ ( self , out_arr , context = None ) :
return out_arr . view ( numpy . ndarray )
#===========================================================================
# Pickling operations
#===========================================================================
def __reduce__ ( self ) :
func , args , state = super ( Param , self ) . __reduce__ ( )
return func , args , ( state ,
2013-11-03 13:58:15 +00:00
( self . name ,
2013-10-25 15:29:04 +01:00
self . _direct_parent_ ,
2013-10-16 21:08:35 +01:00
self . _parent_index_ ,
2013-10-25 15:29:04 +01:00
self . _highest_parent_ ,
2013-10-16 21:08:35 +01:00
self . _current_slice_ ,
self . _realshape_ ,
self . _realsize_ ,
self . _realndim_ ,
2013-10-22 13:39:58 +01:00
self . _tied_to_me_ ,
self . _tied_to_ ,
self . _updated_ ,
)
2013-10-16 21:08:35 +01:00
)
def __setstate__ ( self , state ) :
super ( Param , self ) . __setstate__ ( state [ 0 ] )
state = list ( state [ 1 ] )
2013-10-22 13:39:58 +01:00
self . _updated_ = state . pop ( )
self . _tied_to_ = state . pop ( )
self . _tied_to_me_ = state . pop ( )
2013-10-16 21:08:35 +01:00
self . _realndim_ = state . pop ( )
self . _realsize_ = state . pop ( )
self . _realshape_ = state . pop ( )
self . _current_slice_ = state . pop ( )
2013-10-25 15:29:04 +01:00
self . _highest_parent_ = state . pop ( )
2013-10-22 13:39:58 +01:00
self . _parent_index_ = state . pop ( )
2013-10-25 15:29:04 +01:00
self . _direct_parent_ = state . pop ( )
2013-11-03 13:58:15 +00:00
self . name = state . pop ( )
2013-10-11 16:44:34 +01:00
#===========================================================================
# get/set parameters
#===========================================================================
2013-11-06 11:40:54 +00:00
def _set_params ( self , param , update = True ) :
2013-10-11 16:44:34 +01:00
self . flat = param
2013-10-27 17:04:46 +00:00
self . _notify_tied_parameters ( )
2013-11-06 11:40:54 +00:00
self . _notify_observers ( )
2013-11-03 13:58:15 +00:00
2013-10-02 12:11:53 +01:00
def _get_params ( self ) :
2013-10-11 16:44:34 +01:00
return self . flat
2013-10-27 17:04:46 +00:00
# @property
# def name(self):
# """
# Name of this parameter.
# This can be a callable without parameters. The callable will be called
# every time the name property is accessed.
# """
2013-11-03 13:58:15 +00:00
# if callable(self.name):
# return self.name()
# return self.name
2013-10-27 17:04:46 +00:00
# @name.setter
# def name(self, new_name):
# from_name = self.name
2013-11-03 13:58:15 +00:00
# self.name = new_name
2013-10-27 17:04:46 +00:00
# self._direct_parent_._name_changed(self, from_name)
2013-10-25 15:29:04 +01:00
@property
def _parameters_ ( self ) :
return [ ]
2013-10-11 16:44:34 +01:00
#===========================================================================
# Fixing Parameters:
#===========================================================================
2013-10-15 09:01:03 +01:00
def constrain_fixed ( self , warning = True ) :
"""
Constrain this paramter to be fixed to the current value it carries .
: param warning : print a warning for overwriting constraints .
"""
2013-10-25 15:29:04 +01:00
self . _highest_parent_ . _fix ( self , warning )
2013-10-16 21:08:35 +01:00
fix = constrain_fixed
2013-10-11 16:44:34 +01:00
def unconstrain_fixed ( self ) :
2013-10-15 09:01:03 +01:00
"""
This parameter will no longer be fixed .
"""
2013-10-25 15:29:04 +01:00
self . _highest_parent_ . _unfix ( self )
2013-10-16 21:08:35 +01:00
unfix = unconstrain_fixed
2013-10-11 16:44:34 +01:00
#===========================================================================
# Constrain operations -> done
#===========================================================================
2013-10-22 13:39:58 +01:00
def constrain ( self , transform , warning = True , update = True ) :
2013-10-15 09:01:03 +01:00
"""
: param transform : the : py : class : ` GPy . core . transformations . Transformation `
to constrain the this parameter to .
: param warning : print a warning if re - constraining parameters .
Constrain the parameter to the given
: py : class : ` GPy . core . transformations . Transformation ` .
"""
2013-10-17 14:33:41 +01:00
if self . _original_ : # this happens when indexing created a copy of the array
2013-10-22 13:39:58 +01:00
self . __setitem__ ( slice ( None ) , transform . initialize ( self ) , update = False )
2013-10-17 14:33:41 +01:00
else :
2013-10-25 15:29:04 +01:00
self . _direct_parent_ . _get_original ( self ) . __setitem__ ( self . _current_slice_ , transform . initialize ( self ) , update = False )
self . _highest_parent_ . _add_constrain ( self , transform , warning )
2013-10-22 13:39:58 +01:00
if update :
2013-10-25 15:29:04 +01:00
self . _highest_parent_ . parameters_changed ( )
2013-10-14 17:58:16 +01:00
def constrain_positive ( self , warning = True ) :
2013-10-15 09:01:03 +01:00
"""
: param warning : print a warning if re - constraining parameters .
Constrain this parameter to the default positive constraint .
"""
2013-10-14 17:58:16 +01:00
self . constrain ( Logexp ( ) , warning )
def constrain_negative ( self , warning = True ) :
2013-10-15 09:01:03 +01:00
"""
: param warning : print a warning if re - constraining parameters .
Constrain this parameter to the default negative constraint .
"""
2013-10-14 17:58:16 +01:00
self . constrain ( NegativeLogexp ( ) , warning )
2013-10-15 09:01:03 +01:00
def constrain_bounded ( self , lower , upper , warning = True ) :
"""
: param lower , upper : the limits to bound this parameter to
: param warning : print a warning if re - constraining parameters .
Constrain this parameter to lie within the given range .
"""
self . constrain ( Logistic ( lower , upper ) , warning )
def unconstrain ( self , * transforms ) :
"""
: param transforms : The transformations to unconstrain from .
remove all : py : class : ` GPy . core . transformations . Transformation `
transformats of this parameter object .
"""
2013-10-25 15:29:04 +01:00
self . _highest_parent_ . _remove_constrain ( self , * transforms )
2013-10-02 19:15:24 +01:00
def unconstrain_positive ( self ) :
2013-10-15 09:01:03 +01:00
"""
Remove positive constraint of this parameter .
"""
2013-10-02 19:15:24 +01:00
self . unconstrain ( Logexp ( ) )
2013-10-07 07:40:48 +01:00
def unconstrain_negative ( self ) :
2013-10-15 09:01:03 +01:00
"""
Remove negative constraint of this parameter .
"""
2013-10-07 07:40:48 +01:00
self . unconstrain ( NegativeLogexp ( ) )
2013-10-15 09:01:03 +01:00
def unconstrain_bounded ( self , lower , upper ) :
"""
: param lower , upper : the limits to unbound this parameter from
Remove ( lower , upper ) bounded constrain from this parameter /
"""
self . unconstrain ( Logistic ( lower , upper ) )
2013-10-11 16:44:34 +01:00
#===========================================================================
# Tying operations -> done
#===========================================================================
def tie_to ( self , param ) :
2013-10-15 09:01:03 +01:00
"""
: param param : the parameter object to tie this parameter to .
Tie this parameter to the given parameter .
Broadcasting is allowed , so you can tie a whole dimension to
one parameter : self [ : , 0 ] . tie_to ( other ) , where other is a one - value
parameter .
2013-10-27 17:04:46 +00:00
Note : this method will tie to the parameter which is the last in
the chain of ties . Thus , if you tie to a tied parameter ,
this tie will be created to the parameter the param is tied
to .
2013-10-15 09:01:03 +01:00
"""
2013-10-11 16:44:34 +01:00
assert isinstance ( param , Param ) , " Argument {1} not of type {0} " . format ( Param , param . __class__ )
try :
2013-10-17 14:33:41 +01:00
if self . _original_ : # this happens when indexing created a copy of the array
self [ : ] = param
else :
2013-10-25 15:29:04 +01:00
self . _direct_parent_ . _get_original ( self ) [ self . _current_slice_ ] = param
2013-10-11 16:44:34 +01:00
except ValueError :
raise ValueError ( " Trying to tie {} with shape {} to {} with shape {} " . format ( self . name , self . shape , param . name , param . shape ) )
2013-10-27 17:04:46 +00:00
if param is self :
raise RuntimeError , ' Cyclic tieing is not allowed '
if len ( param . _tied_to_ ) > 0 :
self . tie_to ( param . _tied_to_ [ 0 ] )
return
2013-10-25 15:29:04 +01:00
self . _direct_parent_ . _get_original ( self ) . _tied_to_ + = [ param ]
2013-10-17 20:47:41 +01:00
param . _add_tie_listener ( self )
2013-10-25 15:29:04 +01:00
self . _highest_parent_ . _set_fixed ( self )
2013-10-27 17:04:46 +00:00
for t in self . _tied_to_me_ . iterkeys ( ) :
t . untie ( )
t . tie_to ( param )
2013-10-25 15:29:04 +01:00
# self._direct_parent_._add_tie(self, param)
2013-10-15 09:01:03 +01:00
2013-10-17 20:47:41 +01:00
def untie ( self , * ties ) :
"""
remove tie of this parameter to ties it was tied to .
"""
2013-10-25 15:29:04 +01:00
[ t . _direct_parent_ . _get_original ( t ) . _remove_tie_listener ( self ) for t in self . _tied_to_ ]
self . _tied_to_ = [ tied_to for tied_to in self . _tied_to_ for t in tied_to . _tied_to_me_ if self . _parent_index_ == t . _direct_parent_ . _get_original ( t ) . _parent_index_ ]
self . _highest_parent_ . _set_unfixed ( self )
# self._direct_parent_._remove_tie(self, *params)
2013-10-27 17:04:46 +00:00
def _notify_tied_parameters ( self ) :
2013-10-17 20:47:41 +01:00
for tied , ind in self . _tied_to_me_ . iteritems ( ) :
2013-10-27 17:04:46 +00:00
tied . _on_tied_parameter_changed ( self . base , list ( ind ) )
2013-10-17 20:47:41 +01:00
def _add_tie_listener ( self , tied_to_me ) :
self . _tied_to_me_ [ tied_to_me ] | = set ( self . _raveled_index ( ) )
def _remove_tie_listener ( self , to_remove ) :
for t in self . _tied_to_me_ . keys ( ) :
2013-10-22 13:39:58 +01:00
if t . _parent_index_ == to_remove . _parent_index_ :
new_index = list ( set ( t . _raveled_index ( ) ) - set ( to_remove . _raveled_index ( ) ) )
if new_index :
2013-10-25 15:29:04 +01:00
tmp = t . _direct_parent_ . _get_original ( t ) [ numpy . unravel_index ( new_index , t . _realshape_ ) ]
2013-10-22 13:39:58 +01:00
self . _tied_to_me_ [ tmp ] = self . _tied_to_me_ [ t ]
del self . _tied_to_me_ [ t ]
if len ( self . _tied_to_me_ [ tmp ] ) == 0 :
del self . _tied_to_me_ [ tmp ]
else :
2013-10-18 16:20:01 +01:00
del self . _tied_to_me_ [ t ]
2013-10-27 17:04:46 +00:00
def _on_tied_parameter_changed ( self , val , ind ) :
2013-10-22 13:39:58 +01:00
if not self . _updated_ : #not fast_array_equal(self, val[ind]):
self . _updated_ = True
2013-10-18 16:20:01 +01:00
if self . _original_ :
2013-10-22 13:39:58 +01:00
self . __setitem__ ( slice ( None ) , val [ ind ] , update = False )
2013-10-18 16:20:01 +01:00
else : # this happens when indexing created a copy of the array
2013-10-25 15:29:04 +01:00
self . _direct_parent_ . _get_original ( self ) . __setitem__ ( self . _current_slice_ , val [ ind ] , update = False )
2013-10-27 17:04:46 +00:00
self . _notify_tied_parameters ( )
2013-10-22 13:39:58 +01:00
self . _updated_ = False
2013-10-16 21:08:35 +01:00
#===========================================================================
# Prior Operations
#===========================================================================
def set_prior ( self , prior ) :
"""
: param prior : prior to be set for this parameter
Set prior for this parameter .
"""
2013-10-25 15:29:04 +01:00
if not hasattr ( self . _highest_parent_ , ' _set_prior ' ) :
raise AttributeError ( " Parent of type {} does not support priors " . format ( self . _highest_parent_ . __class__ ) )
self . _highest_parent_ . _set_prior ( self , prior )
2013-10-16 21:08:35 +01:00
def unset_prior ( self , * priors ) :
"""
: param priors : priors to remove from this parameter
Remove all priors from this parameter
"""
2013-10-25 15:29:04 +01:00
self . _highest_parent_ . _remove_prior ( self , * priors )
2013-10-11 16:44:34 +01:00
#===========================================================================
# Array operations -> done
#===========================================================================
2013-10-02 19:18:23 +01:00
def __getitem__ ( self , s , * args , * * kwargs ) :
2013-10-11 16:44:34 +01:00
if not isinstance ( s , tuple ) :
s = ( s , )
2013-10-18 16:20:01 +01:00
if not reduce ( lambda a , b : a or numpy . any ( b is Ellipsis ) , s , False ) and len ( s ) < = self . ndim :
2013-10-16 21:08:35 +01:00
s + = ( Ellipsis , )
2013-10-27 17:04:46 +00:00
new_arr = super ( Param , self ) . __getitem__ ( s , * args , * * kwargs )
2013-10-17 14:33:41 +01:00
try : new_arr . _current_slice_ = s ; new_arr . _original_ = self . base is new_arr . base
2013-10-11 16:44:34 +01:00
except AttributeError : pass # returning 0d array or float, double etc
2013-10-02 19:18:23 +01:00
return new_arr
2013-10-22 13:39:58 +01:00
def __setitem__ ( self , s , val , update = True ) :
2013-11-06 11:40:54 +00:00
super ( Param , self ) . __setitem__ ( s , val , update = update )
2013-10-27 17:04:46 +00:00
self . _notify_tied_parameters ( )
2013-10-22 13:39:58 +01:00
if update :
2013-10-25 15:29:04 +01:00
self . _highest_parent_ . parameters_changed ( )
2013-10-16 21:08:35 +01:00
#===========================================================================
# Index Operations:
#===========================================================================
def _internal_offset ( self ) :
internal_offset = 0
extended_realshape = numpy . cumprod ( ( 1 , ) + self . _realshape_ [ : 0 : - 1 ] ) [ : : - 1 ]
for i , si in enumerate ( self . _current_slice_ [ : self . _realndim_ ] ) :
if numpy . all ( si == Ellipsis ) :
continue
if isinstance ( si , slice ) :
a = si . indices ( self . _realshape_ [ i ] ) [ 0 ]
elif isinstance ( si , ( list , numpy . ndarray , tuple ) ) :
a = si [ 0 ]
else : a = si
if a < 0 :
a = self . _realshape_ [ i ] + a
internal_offset + = a * extended_realshape [ i ]
return internal_offset
2013-10-17 14:33:41 +01:00
def _raveled_index ( self , slice_index = None ) :
2013-10-16 21:08:35 +01:00
# return an index array on the raveled array, which is formed by the current_slice
# of this object
extended_realshape = numpy . cumprod ( ( 1 , ) + self . _realshape_ [ : 0 : - 1 ] ) [ : : - 1 ]
2013-10-17 14:33:41 +01:00
ind = self . _indices ( slice_index )
2013-10-16 21:08:35 +01:00
if ind . ndim < 2 : ind = ind [ : , None ]
2013-10-22 13:39:58 +01:00
return numpy . asarray ( numpy . apply_along_axis ( lambda x : numpy . sum ( extended_realshape * x ) , 1 , ind ) , dtype = int )
2013-10-17 14:33:41 +01:00
def _expand_index ( self , slice_index = None ) :
2013-10-16 21:08:35 +01:00
# this calculates the full indexing arrays from the slicing objects given by get_item for _real..._ attributes
# it basically translates slices to their respective index arrays and turns negative indices around
# it tells you in the second return argument if it has only seen arrays as indices
2013-10-17 14:33:41 +01:00
if slice_index is None :
slice_index = self . _current_slice_
2013-10-16 21:08:35 +01:00
def f ( a ) :
a , b = a
if a not in ( slice ( None ) , Ellipsis ) :
if isinstance ( a , slice ) :
start , stop , step = a . indices ( b )
return numpy . r_ [ start : stop : step ]
elif isinstance ( a , ( list , numpy . ndarray , tuple ) ) :
a = numpy . asarray ( a , dtype = int )
a [ a < 0 ] = b + a [ a < 0 ]
elif a < 0 :
a = b + a
return numpy . r_ [ a ]
return numpy . r_ [ : b ]
2013-10-18 16:20:01 +01:00
return itertools . imap ( f , itertools . izip_longest ( slice_index [ : self . _realndim_ ] , self . _realshape_ , fillvalue = slice ( self . size ) ) )
2013-10-11 16:44:34 +01:00
#===========================================================================
2013-10-25 15:29:04 +01:00
# Convienience
2013-10-11 16:44:34 +01:00
#===========================================================================
@property
2013-10-25 15:29:04 +01:00
def is_fixed ( self ) :
return self . _highest_parent_ . _is_fixed ( self )
2013-10-11 16:44:34 +01:00
def round ( self , decimals = 0 , out = None ) :
view = super ( Param , self ) . round ( decimals , out ) . view ( Param )
view . __array_finalize__ ( self )
2013-10-03 11:58:09 +01:00
return view
2013-10-11 16:44:34 +01:00
round . __doc__ = numpy . round . __doc__
2013-10-25 15:29:04 +01:00
def _get_original ( self , param ) :
return self
#===========================================================================
# Printing -> done
#===========================================================================
@property
def _description_str ( self ) :
if self . size < = 1 : return [ " %f " % self ]
else : return [ str ( self . shape ) ]
def _parameter_names ( self , add_name ) :
return [ self . name ]
@property
def flattened_parameters ( self ) :
return [ self ]
@property
def parameter_shapes ( self ) :
return [ self . shape ]
@property
def _constraints_str ( self ) :
return [ ' ' . join ( map ( lambda c : str ( c [ 0 ] ) if c [ 1 ] . size == self . _realsize_ else " { " + str ( c [ 0 ] ) + " } " , self . _highest_parent_ . _constraints_iter_items ( self ) ) ) ]
@property
def _ties_str ( self ) :
return [ t . _short ( ) for t in self . _tied_to_ ] or [ ' ' ]
@property
def name_hirarchical ( self ) :
if self . has_parent ( ) :
2013-11-06 11:40:54 +00:00
return self . _direct_parent_ . hirarchy_name ( ) + _adjust_name_for_printing ( self . name )
return _adjust_name_for_printing ( self . name )
2013-10-11 16:44:34 +01:00
def __repr__ ( self , * args , * * kwargs ) :
2013-10-25 15:29:04 +01:00
name = " \033 [1m {x:s} \033 [0;0m: \n " . format (
x = self . name_hirarchical )
return name + super ( Param , self ) . __repr__ ( * args , * * kwargs )
2013-10-17 14:33:41 +01:00
def _ties_for ( self , rav_index ) :
2013-10-18 16:20:01 +01:00
ties = numpy . empty ( shape = ( len ( self . _tied_to_ ) , numpy . size ( rav_index ) ) , dtype = Param )
for i , tied_to in enumerate ( self . _tied_to_ ) :
2013-10-17 20:47:41 +01:00
for t in tied_to . _tied_to_me_ . iterkeys ( ) :
if t . _parent_index_ == self . _parent_index_ :
2013-10-18 16:20:01 +01:00
matches = numpy . where ( rav_index [ : , None ] == t . _raveled_index ( ) [ None , : ] )
tt_rav_index = tied_to . _raveled_index ( )
ties [ i , matches [ 0 ] ] = numpy . take ( tt_rav_index , matches [ 1 ] , mode = ' wrap ' )
#[ties.__setitem__(i, ties[i] + [tied_to]) for i in t._raveled_index()]
return map ( lambda a : sum ( a , [ ] ) , zip ( * [ [ [ tie . flatten ( ) ] if tx != None else [ ] for tx in t ] for t , tie in zip ( ties , self . _tied_to_ ) ] ) )
2013-10-17 14:33:41 +01:00
def _constraints_for ( self , rav_index ) :
2013-10-25 15:29:04 +01:00
return self . _highest_parent_ . _constraints_for ( self , rav_index )
2013-10-17 14:33:41 +01:00
def _indices ( self , slice_index = None ) :
2013-10-15 09:01:03 +01:00
# get a int-array containing all indices in the first axis.
2013-10-17 14:33:41 +01:00
if slice_index is None :
slice_index = self . _current_slice_
if isinstance ( slice_index , ( tuple , list ) ) :
clean_curr_slice = [ s for s in slice_index if numpy . any ( s != Ellipsis ) ]
2013-10-16 21:08:35 +01:00
if ( all ( isinstance ( n , ( numpy . ndarray , list , tuple ) ) for n in clean_curr_slice )
and len ( set ( map ( len , clean_curr_slice ) ) ) < = 1 ) :
2013-10-18 16:20:01 +01:00
return numpy . fromiter ( itertools . izip ( * clean_curr_slice ) ,
dtype = [ ( ' ' , int ) ] * self . _realndim_ , count = len ( clean_curr_slice [ 0 ] ) ) . view ( ( int , self . _realndim_ ) )
expanded_index = list ( self . _expand_index ( slice_index ) )
return numpy . fromiter ( itertools . product ( * expanded_index ) ,
dtype = [ ( ' ' , int ) ] * self . _realndim_ , count = reduce ( lambda a , b : a * b . size , expanded_index , 1 ) ) . view ( ( int , self . _realndim_ ) )
2013-10-16 21:08:35 +01:00
def _max_len_names ( self , gen , header ) :
return reduce ( lambda a , b : max ( a , len ( b ) ) , gen , len ( header ) )
2013-10-07 07:40:48 +01:00
def _max_len_values ( self ) :
2013-10-25 15:29:04 +01:00
return reduce ( lambda a , b : max ( a , len ( " { x:=. {0} g} " . format ( __precision__ , x = b ) ) ) , self . flat , len ( self . name ) )
2013-10-07 07:40:48 +01:00
def _max_len_index ( self , ind ) :
2013-10-16 21:08:35 +01:00
return reduce ( lambda a , b : max ( a , len ( str ( b ) ) ) , ind , len ( __index_name__ ) )
2013-10-25 15:29:04 +01:00
def _short ( self ) :
2013-10-17 14:33:41 +01:00
# short string to print
2013-11-06 11:40:54 +00:00
name = self . _direct_parent_ . hirarchy_name ( ) + _adjust_name_for_printing ( self . name )
2013-10-16 21:08:35 +01:00
if self . _realsize_ < 2 :
2013-10-25 15:29:04 +01:00
return name
ind = self . _indices ( )
2013-10-17 14:33:41 +01:00
if ind . size > 4 : indstr = ' , ' . join ( map ( str , ind [ : 2 ] ) ) + " ... " + ' , ' . join ( map ( str , ind [ - 2 : ] ) )
else : indstr = ' , ' . join ( map ( str , ind ) )
2013-10-25 15:29:04 +01:00
return name + ' [ ' + indstr + ' ] '
2013-10-11 16:44:34 +01:00
def __str__ ( self , constr_matrix = None , indices = None , ties = None , lc = None , lx = None , li = None , lt = None ) :
2013-10-18 16:20:01 +01:00
filter_ = self . _current_slice_
vals = self . flat
if indices is None : indices = self . _indices ( filter_ )
ravi = self . _raveled_index ( filter_ )
2013-10-16 21:08:35 +01:00
if constr_matrix is None : constr_matrix = self . _constraints_for ( ravi )
if ties is None : ties = self . _ties_for ( ravi )
2013-10-18 16:20:01 +01:00
ties = [ ' ' . join ( map ( lambda x : x . _short ( ) , t ) ) for t in ties ]
2013-10-14 17:58:16 +01:00
if lc is None : lc = self . _max_len_names ( constr_matrix , __constraints_name__ )
if lx is None : lx = self . _max_len_values ( )
2013-10-18 16:20:01 +01:00
if li is None : li = self . _max_len_index ( indices )
if lt is None : lt = self . _max_len_names ( ties , __tie_name__ )
2013-10-14 17:58:16 +01:00
header = " { i:^ {2} s} | \033 [1m { x:^ {1} s} \033 [0;0m | { c:^ {0} s} | { t:^ {3} s} " . format ( lc , lx , li , lt , x = self . name , c = __constraints_name__ , i = __index_name__ , t = __tie_name__ ) # nice header for printing
2013-10-18 16:20:01 +01:00
if not ties : ties = itertools . cycle ( [ ' ' ] )
2013-10-25 15:29:04 +01:00
return " \n " . join ( [ header ] + [ " { i!s:^ {3} s} | { x: > {1} . {2} g} | { c:^ {0} s} | { t:^ {4} s} " . format ( lc , lx , __precision__ , li , lt , x = x , c = " " . join ( map ( str , c ) ) , t = ( t or ' ' ) , i = i ) for i , x , c , t in itertools . izip ( indices , vals , constr_matrix , ties ) ] ) # return all the constraints with right indices
2013-10-14 17:58:16 +01:00
#except: return super(Param, self).__str__()
2013-10-15 09:01:03 +01:00
2013-10-07 07:40:48 +01:00
class ParamConcatenation ( object ) :
def __init__ ( self , params ) :
2013-10-11 16:44:34 +01:00
"""
Parameter concatenation for convienience of printing regular expression matched arrays
you can index this concatenation as if it was the flattened concatenation
2013-10-15 09:01:03 +01:00
of all the parameters it contains , same for setting parameters ( Broadcasting enabled ) .
See : py : class : ` GPy . core . parameter . Param ` for more details on constraining .
2013-10-11 16:44:34 +01:00
"""
2013-10-25 15:29:04 +01:00
#self.params = params
self . params = [ ]
for p in params :
for p in p . flattened_parameters :
if p not in self . params :
self . params . append ( p )
2013-10-11 16:44:34 +01:00
self . _param_sizes = [ p . size for p in self . params ]
startstops = numpy . cumsum ( [ 0 ] + self . _param_sizes )
2013-10-22 13:39:58 +01:00
self . _param_slices_ = [ slice ( start , stop ) for start , stop in zip ( startstops , startstops [ 1 : ] ) ]
2013-10-15 09:01:03 +01:00
#===========================================================================
# Get/set items, enable broadcasting
#===========================================================================
2013-10-07 07:40:48 +01:00
def __getitem__ ( self , s ) :
2013-10-11 16:44:34 +01:00
ind = numpy . zeros ( sum ( self . _param_sizes ) , dtype = bool ) ; ind [ s ] = True ;
2013-10-25 15:29:04 +01:00
params = [ p . _get_params ( ) [ ind [ ps ] ] for p , ps in zip ( self . params , self . _param_slices_ ) if numpy . any ( p . _get_params ( ) [ ind [ ps ] ] ) ]
2013-10-11 16:44:34 +01:00
if len ( params ) == 1 : return params [ 0 ]
return ParamConcatenation ( params )
2013-10-22 13:39:58 +01:00
def __setitem__ ( self , s , val , update = True ) :
2013-10-11 16:44:34 +01:00
ind = numpy . zeros ( sum ( self . _param_sizes ) , dtype = bool ) ; ind [ s ] = True ;
vals = self . _vals ( ) ; vals [ s ] = val ; del val
2013-10-27 17:04:46 +00:00
[ numpy . place ( p , ind [ ps ] , vals [ ps ] ) and p . _notify_tied_parameters ( )
2013-10-22 13:39:58 +01:00
for p , ps in zip ( self . params , self . _param_slices_ ) ]
if update :
2013-10-25 15:29:04 +01:00
self . params [ 0 ] . _highest_parent_ . parameters_changed ( )
2013-10-11 16:44:34 +01:00
def _vals ( self ) :
return numpy . hstack ( [ p . _get_params ( ) for p in self . params ] )
2013-10-15 09:01:03 +01:00
#===========================================================================
# parameter operations:
#===========================================================================
2013-10-14 17:58:16 +01:00
def constrain ( self , constraint , warning = True ) :
2013-10-07 07:40:48 +01:00
[ param . constrain ( constraint ) for param in self . params ]
2013-10-15 09:01:03 +01:00
constrain . __doc__ = Param . constrain . __doc__
2013-10-14 17:58:16 +01:00
def constrain_positive ( self , warning = True ) :
[ param . constrain_positive ( warning ) for param in self . params ]
2013-10-15 09:01:03 +01:00
constrain_positive . __doc__ = Param . constrain_positive . __doc__
2013-10-14 17:58:16 +01:00
def constrain_fixed ( self , warning = True ) :
[ param . constrain_fixed ( warning ) for param in self . params ]
2013-10-15 09:01:03 +01:00
constrain_fixed . __doc__ = Param . constrain_fixed . __doc__
2013-10-16 21:08:35 +01:00
fix = constrain_fixed
2013-10-14 17:58:16 +01:00
def constrain_negative ( self , warning = True ) :
[ param . constrain_negative ( warning ) for param in self . params ]
2013-10-15 09:01:03 +01:00
constrain_negative . __doc__ = Param . constrain_negative . __doc__
def constrain_bounded ( self , lower , upper , warning = True ) :
[ param . constrain_bounded ( lower , upper , warning ) for param in self . params ]
constrain_bounded . __doc__ = Param . constrain_bounded . __doc__
2013-10-22 16:16:54 +01:00
def unconstrain ( self , * constraints ) :
[ param . unconstrain ( * constraints ) for param in self . params ]
2013-10-15 09:01:03 +01:00
unconstrain . __doc__ = Param . unconstrain . __doc__
2013-10-07 07:40:48 +01:00
def unconstrain_negative ( self ) :
[ param . unconstrain_negative ( ) for param in self . params ]
2013-10-15 09:01:03 +01:00
unconstrain_negative . __doc__ = Param . unconstrain_negative . __doc__
2013-10-11 16:44:34 +01:00
def unconstrain_positive ( self ) :
[ param . unconstrain_positive ( ) for param in self . params ]
2013-10-15 09:01:03 +01:00
unconstrain_positive . __doc__ = Param . unconstrain_positive . __doc__
2013-10-11 16:44:34 +01:00
def unconstrain_fixed ( self ) :
[ param . unconstrain_fixed ( ) for param in self . params ]
2013-10-15 09:01:03 +01:00
unconstrain_fixed . __doc__ = Param . unconstrain_fixed . __doc__
2013-10-16 21:08:35 +01:00
unfix = unconstrain_fixed
2013-10-15 09:01:03 +01:00
def unconstrain_bounded ( self , lower , upper ) :
[ param . unconstrain_bounded ( lower , upper ) for param in self . params ]
unconstrain_bounded . __doc__ = Param . unconstrain_bounded . __doc__
2013-10-25 15:29:04 +01:00
def untie ( self , * ties ) :
[ param . untie ( * ties ) for param in self . params ]
2013-10-15 09:01:03 +01:00
__lt__ = lambda self , val : self . _vals ( ) < val
__le__ = lambda self , val : self . _vals ( ) < = val
__eq__ = lambda self , val : self . _vals ( ) == val
__ne__ = lambda self , val : self . _vals ( ) != val
__gt__ = lambda self , val : self . _vals ( ) > val
__ge__ = lambda self , val : self . _vals ( ) > = val
2013-10-07 07:40:48 +01:00
def __str__ ( self , * args , * * kwargs ) :
2013-10-17 14:33:41 +01:00
def f ( p ) :
ind = p . _raveled_index ( )
2013-10-25 15:29:04 +01:00
return p . _constraints_for ( ind ) , p . _ties_for ( ind )
params = self . params
constr_matrices , ties_matrices = zip ( * map ( f , params ) )
indices = [ p . _indices ( ) for p in params ]
lc = max ( [ p . _max_len_names ( cm , __constraints_name__ ) for p , cm in itertools . izip ( params , constr_matrices ) ] )
lx = max ( [ p . _max_len_values ( ) for p in params ] )
li = max ( [ p . _max_len_index ( i ) for p , i in itertools . izip ( params , indices ) ] )
lt = max ( [ p . _max_len_names ( tm , __tie_name__ ) for p , tm in itertools . izip ( params , ties_matrices ) ] )
strings = [ p . __str__ ( cm , i , tm , lc , lx , li , lt ) for p , cm , i , tm in itertools . izip ( params , constr_matrices , indices , ties_matrices ) ]
return " \n " . join ( strings )
return " \n {} \n " . format ( " - " + " - | - " . join ( [ ' - ' * l for l in [ li , lx , lc , lt ] ] ) ) . join ( strings )
2013-10-07 07:40:48 +01:00
def __repr__ ( self ) :
2013-10-11 16:44:34 +01:00
return " \n " . join ( map ( repr , self . params ) )
2013-10-07 07:40:48 +01:00
2013-10-02 12:11:53 +01:00
if __name__ == ' __main__ ' :
2013-10-15 09:01:03 +01:00
from GPy . core . parameterized import Parameterized
2013-10-25 15:29:04 +01:00
from GPy . core . parameter import Param
2013-10-16 21:08:35 +01:00
#X = numpy.random.randn(2,3,1,5,2,4,3)
2013-10-25 15:29:04 +01:00
X = numpy . random . randn ( 3 , 2 )
2013-10-17 14:33:41 +01:00
print " random done "
2013-10-25 15:29:04 +01:00
p = Param ( " q_mean " , X )
p1 = Param ( " q_variance " , numpy . random . rand ( * p . shape ) )
p2 = Param ( " Y " , numpy . random . randn ( p . shape [ 0 ] , 1 ) )
p3 = Param ( " variance " , numpy . random . rand ( ) )
p4 = Param ( " lengthscale " , numpy . random . rand ( 2 ) )
2013-10-16 21:08:35 +01:00
m = Parameterized ( )
2013-10-25 15:29:04 +01:00
rbf = Parameterized ( name = ' rbf ' )
rbf . add_parameter ( p3 , p4 )
m . add_parameter ( p , p1 , rbf )
2013-10-17 14:33:41 +01:00
print " setting params "
2013-10-16 21:08:35 +01:00
#print m.q_v[3:5,[1,4,5]]
2013-10-17 14:33:41 +01:00
print " constraining variance "
2013-10-25 15:29:04 +01:00
#m[".*variance"].constrain_positive()
#print "constraining rbf"
#m.rbf_l.constrain_positive()
#m.q_variance[1,[0,5,11,19,2]].tie_to(m.rbf_v)
#m.rbf_v.tie_to(m.rbf_l[0])
#m.rbf_l[0].tie_to(m.rbf_l[1])
2013-10-16 21:08:35 +01:00
#m.q_v.tie_to(m.rbf_v)
2013-10-11 16:44:34 +01:00
# m.rbf_l.tie_to(m.rbf_va)
# pt = numpy.array(params._get_params_transformed())
# ptr = numpy.random.randn(*pt.shape)
2013-10-07 07:40:48 +01:00
# params.X.tie_to(params.rbf_v)