def test_construct_fast(self): np.random.seed(1234) c = np.array([[1, 4], [2, 5], [3, 6]], dtype=float) x = np.array([0, 0.5, 1]) p = PPoly.construct_fast(c, x) assert_allclose(p(0.3), 1 * 0.3**2 + 2 * 0.3 + 3) assert_allclose(p(0.7), 4 * (0.7 - 0.5)**2 + 5 * (0.7 - 0.5) + 6)
def test_construct_fast(self): np.random.seed(1234) c = np.array([[1, 4], [2, 5], [3, 6]], dtype=float) x = np.array([0, 0.5, 1]) p = PPoly.construct_fast(c, x) assert_allclose(p(0.3), 1*0.3**2 + 2*0.3 + 3) assert_allclose(p(0.7), 4*(0.7-0.5)**2 + 5*(0.7-0.5) + 6)
def cdf_approx(X): #, smoothness_factor=1): """ Generates a ppoly spline to approximate the cdf of a random variable, from a 1-D array of i.i.d. samples thereof. Args: X: a collection of i.i.d. samples from a random variable. args, kwargs: any options to forward to the cvxopt qp solver Returns: scipy.interpolate.PPoly object, estimating the cdf of the random variable. Raises: TODO """ # Pre-format input as ordered numpy array X = np.asarray(X) diff_X = np.diff(X) if not (diff_X > 0).all(): X.sort() diff_X = np.diff(X) assert (diff_X.all()) # avoids case of duplicate X-values n = len(X) scale_axi, scale_ei = make_obj_scale(X) #, smoothness_factor) P, q = make_P_q(X, scale_a=scale_axi, scale_e=scale_ei) G, h = make_G_h(X) #A, b = make_A_b(X) # simply unnecessary bmid_c_init = bmid_c_init_state(X) qp_res = cvxopt.solvers.qp( cvxopt.matrix(P), cvxopt.matrix(q), cvxopt.matrix(G), cvxopt.matrix(h), #cvxopt.matrix(A), #cvxopt.matrix(b), #*args, **kwargs ) X, P_X, dP_X, d2P_X = clean_optimizer_results(np.array(qp_res['x']), X) return PPoly.construct_fast(np.stack((d2P_X, dP_X, P_X)), X, extrapolate=True)
def synth_from_pp(breaks, order, coeffs, time, radius, theta, phi, *, nmax=None, source=None, deriv=None, grid=None): """ Compute radial, colatitude and azimuthal field components from the magnetic potential in terms of a spherical harmonic expansion in form of a piecewise polynomial. Parameters ---------- breaks : ndarray, shape (m+1,) 1-D array, containing `m+1` break points (without endpoint repeats) for `m` intervals. order : int, positive Order `k` of piecewise polynomials (4 = cubic). coeffs : ndarray, shape (k, m, nmax*(nmax+2)) Coefficients of the piecewise polynomials, where `m` is the number of polynomial pieces. The trailing dimension is equal to the number of expansion coefficients for each interval. time : ndarray, shape (...) Array containing the time in days. radius : ndarray, shape (...) or float Array containing the radius in kilometers. theta : ndarray, shape (...) or float Array containing the colatitude in degrees :math:`[0^\\circ,180^\\circ]`. phi : ndarray, shape (...) or float Array containing the longitude in degrees. nmax : int, positive, optional Maximum degree harmonic expansion (default is given by ``coeffs``, but can also be smaller, if specified). source : {'internal', 'external'}, optional Magnetic field source (default is an internal source). deriv : int, positive, optional Derivative to be taken (default is 0). grid : bool, optional If ``True``, field components are computed on a regular grid. Arrays ``theta`` and ``phi`` must have one dimension less than the output grid since the grid will be created as their outer product. Returns ------- B_radius, B_theta, B_phi : ndarray, shape (...) Radial, colatitude and azimuthal field components. See Also -------- synth_values """ # handle optional argument: nmax nmax_coeffs = int(np.sqrt(coeffs.shape[-1] + 1) - 1) # degree for coeffs if nmax is None: nmax = nmax_coeffs elif nmax > nmax_coeffs: warnings.warn('Supplied nmax = {0} is incompatible with number of ' 'model coefficients. Using nmax = {1} instead.'.format( nmax, nmax_coeffs)) nmax = nmax_coeffs # handle optional argument: source source = 'internal' if source is None else source # compute SH coefficients from pp-form and take derivatives if needed deriv = 0 if deriv is None else deriv # set grid option to false grid = False if grid is None else grid PP = PPoly.construct_fast(coeffs[..., :nmax*(nmax+2)], breaks, extrapolate=False) PP = PP.derivative(nu=deriv) gauss_coeffs = PP(time) * 365.25**deriv B_radius, B_theta, B_phi = synth_values( gauss_coeffs, radius, theta, phi, nmax=nmax, source=source, grid=grid) return B_radius, B_theta, B_phi
def __call__(self, x: Sequence[UnivariateDataType], nu: Optional[Tuple[int, ...]] = None, extrapolate: Optional[bool] = None) -> np.ndarray: """Evaluate the spline for given data Parameters ---------- x : tuple of 1-d array-like The tuple of point values for each dimension to evaluate the spline at. nu : [*Optional*] tuple of int Orders of derivatives to evaluate. Each must be non-negative. extrapolate : [*Optional*] bool Whether to extrapolate to out-of-bounds points based on first and last intervals, or to return NaNs. Returns ------- y : array-like Interpolated values. Shape is determined by replacing the interpolation axis in the original array with the shape of x. """ x = ndgrid_prepare_data_vectors(x, 'x', min_size=1) if len(x) != self.ndim: raise ValueError( f"'x' sequence must have length {self.ndim} according to 'breaks'") if nu is None: nu = (0,) * len(x) if extrapolate is None: extrapolate = True shape = tuple(x.size for x in x) coeffs = ndg_coeffs_to_flatten(self.coeffs) coeffs_shape = coeffs.shape ndim_m1 = self.ndim - 1 permuted_axes = (ndim_m1, *range(ndim_m1)) for i in reversed(range(self.ndim)): umv_ndim = prod(coeffs_shape[:ndim_m1]) c_shape = (umv_ndim, self.pieces[i] * self.order[i]) if c_shape != coeffs_shape: coeffs = coeffs.reshape(c_shape) coeffs_cnl = umv_coeffs_to_canonical(coeffs, self.pieces[i]) spline = PPoly.construct_fast(coeffs_cnl, self.breaks[i], axis=1) coeffs = spline(x[i], nu=nu[i], extrapolate=extrapolate) shape_r = (*coeffs_shape[:ndim_m1], shape[i]) coeffs = coeffs.reshape(shape_r).transpose(permuted_axes) coeffs_shape = coeffs.shape return coeffs.reshape(shape)