27 The classes in this file were taken from https://github.com/PierreAstier/bfptc
28 by Pierre Astier (Laboratoire de Physique Nucléaire et de Hautes Energies (LPNHE),
29 Sorbonne Université, Paris, France).
31 File: bfptc/py/fitparameters.py
32 Commit hash: d46ba836fd5feb1c0065b61472c5f31b73b8480f
35 Notes from original code
36 -----------------------
38 This module contains utility classes to handle parameters in linear and
39 non-linear least square fits implemented in linearmodels and
40 nonlinearmodels. It provide 2 main features:
42 - `StructArray`: a derivative of numpy class ndarray to manage large
43 vectors organized into named subgroups.
45 - `FitParameters` : a class to manage large parameter vectors. It
46 allows to easily fix/release specific parameters or entire subgroups,
47 and remap the remaining free parameters into a contiguous vector.
52 """ Collection of named slices
54 Slices are specified by a name and a length. If omitted, the length
55 default to one and the name to __0__ for the first unamed slice, __1__
56 for the second and so on.
60 >>> s = Structure([('a', 7), 3])
63 >>> for name in s: print name
66 >>> print len(Structure([('a', 3), 'b']))
70 if isinstance(groups, Structure):
71 self.
slicesslices = groups.slices.copy()
72 self.
_len_len = groups._len
78 if isinstance(group, int):
79 name =
"__%d__" % _n_unnamed
82 elif isinstance(group, str):
89 raise TypeError(
'Structure specification not understood: %s' % repr(group))
90 self.
slicesslices[name] = slice(i, i + _len)
95 if isinstance(arg, str):
96 return self.
slicesslices[arg]
108 """Decorate numpy arrays with a collection of named slices.
110 Array slices becomes accessible by their name. This is applicable to
111 nd array, although the same `Structure` is shared between all
116 >>> v = StructArray(np.zeros(10), [('a', 3), ('b', 7)])
120 >>> C = StructArray(np.zeros((10,10)), [('a', 2), ('b', 8)])
121 >>> print C['a', 'a']
126 obj = array.view(cls)
133 self.
structstruct = getattr(obj,
'struct', [])
136 if not isinstance(args, tuple):
138 newargs = tuple([self.
structstruct[arg]
for arg
in args])
139 return np.asarray(self)[newargs]
142 if not isinstance(args, tuple):
144 newargs = tuple([self.
structstruct[arg]
for arg
in args])
145 np.asarray(self)[newargs] = val
148 return self.
__getitem____getitem__(slice(start, stop))
152 pickled_state = super(StructArray, self).
__reduce__()
154 new_state = pickled_state[2] + (self.
structstruct,)
156 return (pickled_state[0], pickled_state[1], new_state)
159 self.
structstruct = state[-1]
165 """ Manages a vector of fit parameters with the possibility to mark a subset of
166 them as fixed to a given value.
168 The parameters can be organized in named slices (block of contiguous
169 values) accessible through indexing by their name as in `StructArray`.
171 >>> p_salt = FitParameters(['X0', 'X1', 'Color', 'Redshift'])
172 >>> p_dice = FitParameters([('alpha', 2), ('S', 10), ('dSdT', 10), 'idark'])
174 It is possible to modify the parameters in place. Using the indexing
175 of slices by name simplifies somewhat the operations, as one does
176 not need to care about the position of a slice within the entire
179 >>> p_dice['idark'][0] = -1.0232E-12
180 >>> p_dice['S'][4] = 25.242343E-9
181 >>> p_dice['dSdT'][:] = 42.
183 alpha: array([ 0., 0.])
184 S: array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
185 0.00000000e+00, 2.52423430e-08, 0.00000000e+00,
186 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
188 dSdT: array([ 42., 42., 42., 42., 42., 42., 42., 42., 42., 42.])
189 idark: array([ -1.02320000e-12])
191 It is also possible to mark parameters as fixed to a value.
192 >>> p_dice.fix(0, 12.)
194 Value is optional. The above is equivalent to:
198 Again named slices simplifies the operations:
199 >>> p_dice['S'].fix([0, -1], 12.)
200 >>> p_dice['dSdT'].fix([0, -1])
202 One can fix entire slices at once:
203 >>> p_dice['idark'].fix()
204 >>> p_salt['Redshift'].fix(val=0.23432)
206 The property ``full'' give access to the vector of parameters. The
207 property "free" gives access to the free parameters:
208 >>> print len(p_dice.free), len(p_dice.full)
211 Note that free relies on fancy indexing. Access thus trigger a
212 copy. As a consequence, the following will not actually alter the
214 >>> p_dice.free[0] = 12.
215 >>> print p_dice.free[0]
218 It is still possible to set slices of free parameters as a
219 contiguous vector. For example:
220 >>> p_dice['S'].free = 12.
221 >>> print p_dice['S'].free
222 [ 12. 12. 12. 12. 12. 12. 12. 12.]
224 >>> p_dice[:5].free = 4.
225 >>> print p_dice[:5].free
228 In particular, the typical use case which consists in updating the
229 free parameters with the results of a fit works as expected:
230 >>> p = np.arange(len(p_dice.free))
233 Last the class provide a convenience function that return the index
234 of a subset of parameters in the global free parameters vector, and
235 -1 for fixed parameters:
236 >>> print p_dice['dSdT'].indexof()
237 [-1 9 10 11 12 13 14 15 16 -1]
238 >>> print p_dice['dSdT'].indexof([1,2])
248 self.
_base_base = self
263 def fix(self, keys=slice(
None), val=
None):
264 self.
_free_free[keys] =
False
266 self.
_pars_pars[keys] = val
267 self.
_base_base._reindex()
270 self.
_free_free[keys] =
True
271 self.
_base_base._reindex()
274 return self.
_index_index[indices]
278 if isinstance(args, int):
279 args = slice(args, args + 1)
280 new = FitParameters.__new__(FitParameters)
281 new._free = self.
_free_free[args]
282 new._pars = self.
_pars_pars[args]
283 new._index = self.
_index_index[args]
284 new._base = self.
_base_base
288 self.
_pars_pars[args] = val
300 return self.
_pars_pars
304 self.
_pars_pars = val
307 if hasattr(self,
'_struct'):
308 s =
"\n".join([
'%s: %s' % (key, repr(self.
_pars_pars[key]))
for key
in self.
_struct_struct])
310 s = repr(self.
_pars_pars)
def release(self, keys=slice(None))
def indexof(self, indices=slice(None))
def __getitem__(self, args)
def __init__(self, groups)
def __setitem__(self, args, val)
def fix(self, keys=slice(None), val=None)
def __getslice__(self, start, stop)
def __getitem__(self, args)
def __array_finalize__(self, obj)
def __new__(cls, array, struct=[])
def __setstate__(self, state)
def __setitem__(self, args, val)
def __getitem__(self, arg)
def __init__(self, groups)