def get_phi(self, params): """Return wideband timing-model prior.""" # get DMEFAC- and DMEQUAD-adjusted DMX errors dme = self.get_dme(params) # initialize the timing-model "infinite" prior phi = KernelMatrix(1e40 * np.ones(self._npars, "d")) # fill the DMX slots with weighted errors for index, which in zip(self._dmindex, self._dmwhich): phi.set(1.0 / np.sum(1.0 / dme[which] ** 2), index) return phi
class BasisGP(signal_base.Signal): signal_type = "basis" signal_name = name signal_id = name basis_combine = combine def __init__(self, psr): super(BasisGP, self).__init__(psr) self.name = self.psrname + "_" + self.signal_id self._do_selection(psr, priorFunction, basisFunction, coefficients, selection) def _do_selection(self, psr, priorfn, basisfn, coefficients, selection): sel = selection(psr) self._keys = sorted(sel.masks.keys()) self._masks = [sel.masks[key] for key in self._keys] self._prior, self._bases = {}, {} self._params, self._coefficients = {}, {} for key, mask in zip(self._keys, self._masks): pnames = [psr.name, name, key] pname = "_".join([n for n in pnames if n]) self._prior[key] = priorfn(pname, psr=psr) self._bases[key] = basisfn(pname, psr=psr) for par in itertools.chain(self._prior[key]._params.values(), self._bases[key]._params.values()): self._params[par.name] = par if coefficients: # we can only create GPCoefficients parameters if the basis # can be constructed with default arguments # (and does not change size) self._construct_basis() for key in self._keys: pname = "_".join([n for n in [psr.name, name, key] if n]) chain = itertools.chain(self._prior[key]._params.values(), self._bases[key]._params.values()) priorargs = {par.name: self._params[par.name] for par in chain} logprior = parameter.Function(functools.partial(self._get_coefficient_logprior, key), **priorargs) size = self._slices[key].stop - self._slices[key].start cpar = parameter.GPCoefficients(logprior=logprior, size=size)(pname + "_coefficients") self._coefficients[key] = cpar self._params[cpar.name] = cpar @property def basis_params(self): """Get any varying basis parameters.""" ret = [] for basis in self._bases.values(): ret.extend([pp.name for pp in basis.params]) return ret @signal_base.cache_call("basis_params") def _construct_basis(self, params={}): basis, self._labels = {}, {} for key, mask in zip(self._keys, self._masks): basis[key], self._labels[key] = self._bases[key](params=params, mask=mask) nc = sum(F.shape[1] for F in basis.values()) self._basis = np.zeros((len(self._masks[0]), nc)) # TODO: should this be defined here? it will cache phi self._phi = KernelMatrix(nc) self._slices = {} nctot = 0 for key, mask in zip(self._keys, self._masks): Fmat = basis[key] nn = Fmat.shape[1] self._basis[mask, nctot : nn + nctot] = Fmat self._slices.update({key: slice(nctot, nn + nctot)}) nctot += nn # this class does different things (and gets different method # definitions) if the user wants it to model GP coefficients # (e.g., for a hierarchical likelihood) or if they do not if coefficients: def _get_coefficient_logprior(self, key, c, **params): self._construct_basis(params) phi = self._prior[key](self._labels[key], params=params) if phi.ndim == 1: return -0.5 * np.sum(c * c / phi) - 0.5 * np.sum(np.log(phi)) - 0.5 * len(phi) * np.log(2 * np.pi) # note: (2*pi)^(n/2) is not in signal_base likelihood else: # TO DO: this code could be embedded in KernelMatrix phiinv, logdet = KernelMatrix(phi).inv(logdet=True) return -0.5 * np.dot(c, np.dot(phiinv, c)) - 0.5 * logdet - 0.5 * phi.shape[0] * np.log(2 * np.pi) # MV: could assign this to a data member at initialization @property def delay_params(self): return [pp.name for pp in self.params if "_coefficients" in pp.name] @signal_base.cache_call(["basis_params", "delay_params"]) def get_delay(self, params={}): self._construct_basis(params) c = np.zeros(self._basis.shape[1]) for key, slc in self._slices.items(): p = self._coefficients[key] c[slc] = params[p.name] if p.name in params else p.value return np.dot(self._basis, c) def get_basis(self, params={}): return None def get_phi(self, params): return None def get_phiinv(self, params): return None else: @property def delay_params(self): return [] def get_delay(self, params={}): return 0 def get_basis(self, params={}): self._construct_basis(params) return self._basis def get_phi(self, params): self._construct_basis(params) for key, slc in self._slices.items(): phislc = self._prior[key](self._labels[key], params=params) self._phi = self._phi.set(phislc, slc) return self._phi def get_phiinv(self, params): return self.get_phi(params).inv()