def workit(self, xdata, ydata, invvar, action, lower, upper): """An internal routine for bspline_extract and bspline_radial which solve a general banded correlation matrix which is represented by the variable "action". This routine only solves the linear system once, and stores the coefficients in sset. A non-zero return value signifies a failed inversion Parameters ---------- xdata : `numpy.ndarray`_ Independent variable. ydata : `numpy.ndarray`_ Dependent variable. invvar : `numpy.ndarray`_ Inverse variance of `ydata`. action : `numpy.ndarray`_ Banded correlation matrix lower : `numpy.ndarray`_ A list of pixel positions, each corresponding to the first occurence of position greater than breakpoint indx upper : `numpy.ndarray`_ Same as lower, but denotes the upper pixel positions Returns ------- success : :obj:`int` Method error code: 0 is good; -1 is dropped breakpoints, try again; -2 is failure, should abort. yfit : `numpy.ndarray`_ Evaluation of the b-spline yfit at the input values. """ goodbk = self.mask[self.nord:] # KBW: Interesting: x.sum() is actually a bit faster than np.sum(x) nn = goodbk.sum() if nn < self.nord: warnings.warn( 'Fewer good break points than order of b-spline. Returning...') # KBW: Why is the dtype set to 'f' = np.float32? return -2, np.zeros(ydata.shape, dtype=float) alpha, beta = solution_arrays(nn, self.npoly, self.nord, ydata, action, invvar, upper, lower) nfull = nn * self.npoly # Right now we are not returning the covariance, although it may arise that we should # covariance = alpha err, a = cholesky_band(alpha, mininf=1.0e-10 * invvar.sum() / nfull) # successful cholseky_band returns -1 if not isinstance(err, int) or err != -1: return self.maskpoints(err), \ self.value(xdata, x2=xdata, action=action, upper=upper, lower=lower)[0] # NOTE: cholesky_solve ALWAYS returns err == -1; don't even catch it. sol = cholesky_solve(a, beta)[1] if self.coeff.ndim == 2: self.icoeff[:, goodbk] = np.array(a[0, :nfull].T.reshape(self.npoly, nn, order='F'), dtype=a.dtype) self.coeff[:, goodbk] = np.array(sol[:nfull].T.reshape(self.npoly, nn, order='F'), dtype=sol.dtype) else: self.icoeff[goodbk] = np.array(a[0, :nfull], dtype=a.dtype) self.coeff[goodbk] = np.array(sol[:nfull], dtype=sol.dtype) return 0, self.value(xdata, x2=xdata, action=action, upper=upper, lower=lower)[0]
def fit(self, xdata, ydata, invvar, x2=None): """Calculate a B-spline in the least-squares sense. Fit is based on two variables: x which is sorted and spans a large range where bkpts are required y which can be described with a low order polynomial. Parameters ---------- xdata : `numpy.ndarray`_ Independent variable. ydata : `numpy.ndarray`_ Dependent variable. invvar : `numpy.ndarray`_ Inverse variance of `ydata`. x2 : `numpy.ndarray`_, optional Orthogonal dependent variable for 2d fits. Returns ------- :obj:`tuple` A tuple containing an integer error code, and the evaluation of the b-spline at the input values. An error code of -2 is a failure, -1 indicates dropped breakpoints, 0 is success, and positive integers indicate ill-conditioned breakpoints. """ goodbk = self.mask[self.nord:] nn = goodbk.sum() if nn < self.nord: yfit = np.zeros(ydata.shape, dtype=float) return (-2, yfit) nfull = nn * self.npoly bw = self.npoly * self.nord a1, lower, upper = self.action(xdata, x2=x2) foo = np.tile(invvar, bw).reshape(bw, invvar.size).transpose() a2 = a1 * foo alpha = np.zeros((bw, nfull + bw), dtype=float) beta = np.zeros((nfull + bw, ), dtype=float) bi = np.arange(bw, dtype=int) bo = np.arange(bw, dtype=int) for k in range(1, bw): bi = np.append(bi, np.arange(bw - k, dtype=int) + (bw + 1) * k) bo = np.append(bo, np.arange(bw - k, dtype=int) + bw * k) for k in range(nn - self.nord + 1): itop = k * self.npoly ibottom = min(itop, nfull) + bw - 1 ict = upper[k] - lower[k] + 1 if ict > 0: work = np.dot(a1[lower[k]:upper[k] + 1, :].T, a2[lower[k]:upper[k] + 1, :]) wb = np.dot(ydata[lower[k]:upper[k] + 1], a2[lower[k]:upper[k] + 1, :]) alpha.T.flat[bo + itop * bw] += work.flat[bi] beta[itop:ibottom + 1] += wb min_influence = 1.0e-10 * invvar.sum() / nfull errb = cholesky_band(alpha, mininf=min_influence) # ,verbose=True) if isinstance(errb[0], int) and errb[0] == -1: a = errb[1] else: yfit, foo = self.value(xdata, x2=x2, action=a1, upper=upper, lower=lower) return (self.maskpoints(errb[0]), yfit) errs = cholesky_solve(a, beta) if isinstance(errs[0], int) and errs[0] == -1: sol = errs[1] else: # # It is not possible for this to get called, because cholesky_solve # has only one return statement, & that statement guarantees that # errs[0] == -1 # yfit, foo = self.value(xdata, x2=x2, action=a1, upper=upper, lower=lower) return (self.maskpoints(errs[0]), yfit) if self.coeff.ndim == 2: # JFH made major bug fix here. self.icoeff[:, goodbk] = np.array(a[0, 0:nfull].T.reshape(self.npoly, nn, order='F'), dtype=a.dtype) self.coeff[:, goodbk] = np.array(sol[0:nfull].T.reshape(self.npoly, nn, order='F'), dtype=sol.dtype) else: self.icoeff[goodbk] = np.array(a[0, 0:nfull], dtype=a.dtype) self.coeff[goodbk] = np.array(sol[0:nfull], dtype=sol.dtype) yfit, foo = self.value(xdata, x2=x2, action=a1, upper=upper, lower=lower) return (0, yfit)