def random_state(self, seed: RandomStateArgType): """Get or set the RandomState object of the random variable. This can be either None or an existing RandomState object. If None (or np.random), use the RandomState singleton used by np.random. If already a RandomState instance, use it. If an int, use a new RandomState instance seeded with seed. """ self._random_state = _utils.as_random_state(seed)
def __init__( self, shape: ShapeArgType, dtype: DTypeArgType, random_state: RandomStateArgType = None, parameters: Optional[Dict[str, Any]] = None, sample: Optional[Callable[[ShapeType], _ValueType]] = None, in_support: Optional[Callable[[_ValueType], bool]] = None, cdf: Optional[Callable[[_ValueType], np.float_]] = None, logcdf: Optional[Callable[[_ValueType], np.float_]] = None, quantile: Optional[Callable[[FloatArgType], _ValueType]] = None, mode: Optional[Callable[[], _ValueType]] = None, median: Optional[Callable[[], _ValueType]] = None, mean: Optional[Callable[[], _ValueType]] = None, cov: Optional[Callable[[], _ValueType]] = None, var: Optional[Callable[[], _ValueType]] = None, std: Optional[Callable[[], _ValueType]] = None, entropy: Optional[Callable[[], np.float_]] = None, as_value_type: Optional[Callable[[Any], _ValueType]] = None, ): # pylint: disable=too-many-arguments,too-many-locals """Create a new random variable.""" self.__shape = _utils.as_shape(shape) # Data Types self.__dtype = np.dtype(dtype) self.__median_dtype = RandomVariable.infer_median_dtype(self.__dtype) self.__moment_dtype = RandomVariable.infer_moment_dtype(self.__dtype) self._random_state = _utils.as_random_state(random_state) # Probability distribution of the random variable self.__parameters = parameters.copy() if parameters is not None else {} self.__sample = sample self.__in_support = in_support self.__cdf = cdf self.__logcdf = logcdf self.__quantile = quantile # Properties of the random variable self.__mode = mode self.__median = median self.__mean = mean self.__cov = cov self.__var = var self.__std = std self.__entropy = entropy # Utilities self.__as_value_type = as_value_type
def random_spd_matrix( dim: IntArgType, spectrum: Sequence = None, random_state: Optional[RandomStateArgType] = None, ) -> np.ndarray: """Random symmetric positive definite matrix. Constructs a random symmetric positive definite matrix from a given spectrum. An orthogonal matrix :math:`Q` with :math:`\\operatorname{det}(Q)` (a rotation) is sampled with respect to the Haar measure and the diagonal matrix containing the eigenvalues is rotated accordingly resulting in :math:`A=Q \\operatorname{diag}(\\lambda_1, \\dots, \\lambda_n)Q^\\top`. If no spectrum is provided, one is randomly drawn from a Gamma distribution. Parameters ---------- dim Matrix dimension. spectrum Eigenvalues of the matrix. random_state Random state of the random variable. If None (or np.random), the global :mod:`numpy.random` state is used. If integer, it is used to seed the local :class:`~numpy.random.RandomState` instance. See Also -------- random_sparse_spd_matrix : Generate a random sparse symmetric positive definite matrix. Examples -------- >>> from probnum.problems.zoo.linalg import random_spd_matrix >>> mat = random_spd_matrix(dim=5, random_state=0) >>> mat array([[10.49868572, -0.80840778, 0.79781892, 1.9229059 , 0.73413367], [-0.80840778, 15.79117417, 0.52641887, -1.8727916 , -0.9309482 ], [ 0.79781892, 0.52641887, 15.56457452, 1.26004438, -1.44969733], [ 1.9229059 , -1.8727916 , 1.26004438, 8.59057287, -0.44955394], [ 0.73413367, -0.9309482 , -1.44969733, -0.44955394, 9.77198568]]) Check for symmetry and positive definiteness. >>> np.all(mat == mat.T) True >>> np.linalg.eigvals(mat) array([ 6.93542496, 10.96494454, 9.34928449, 16.25401501, 16.71332395]) """ # Initialization random_state = _utils.as_random_state(random_state) if spectrum is None: # Create a custom ordered spectrum if none is given. spectrum_shape: float = 10.0 spectrum_scale: float = 1.0 spectrum_offset: float = 0.0 spectrum = scipy.stats.gamma.rvs( spectrum_shape, loc=spectrum_offset, scale=spectrum_scale, size=dim, random_state=random_state, ) spectrum = np.sort(spectrum)[::-1] else: spectrum = np.asarray(spectrum) if not np.all(spectrum > 0): raise ValueError( f"Eigenvalues must be positive, but are {spectrum}.") # Early exit for d=1 -- special_ortho_group does not like this case. if dim == 1: return spectrum.reshape((1, 1)) # Draw orthogonal matrix with respect to the Haar measure orth_mat = scipy.stats.special_ortho_group.rvs(dim, random_state=random_state) spd_mat = orth_mat @ np.diag(spectrum) @ orth_mat.T # Symmetrize to avoid numerically not symmetric matrix # Since A commutes with itself (AA' = A'A = AA) the eigenvalues do not change. return 0.5 * (spd_mat + spd_mat.T)
def random_sparse_spd_matrix( dim: IntArgType, density: float, chol_entry_min: float = 0.1, chol_entry_max: float = 1.0, random_state: Optional[RandomStateArgType] = None, ) -> np.ndarray: """Random sparse symmetric positive definite matrix. Constructs a random sparse symmetric positive definite matrix for a given degree of sparsity. The matrix is constructed from its Cholesky factor :math:`L`. Its diagonal is set to one and all other entries of the lower triangle are sampled from a uniform distribution with bounds :code:`[chol_entry_min, chol_entry_max]`. The resulting sparse matrix is then given by :math:`A=LL^\\top`. Parameters ---------- dim Matrix dimension. density Degree of sparsity of the off-diagonal entries of the Cholesky factor. Between 0 and 1 where 1 represents a dense matrix. chol_entry_min Lower bound on the entries of the Cholesky factor. chol_entry_max Upper bound on the entries of the Cholesky factor. random_state Random state of the random variable. If None (or np.random), the global :mod:`numpy.random` state is used. If integer, it is used to seed the local :class:`~numpy.random.RandomState` instance. See Also -------- random_spd_matrix : Generate a random symmetric positive definite matrix. Examples -------- >>> from probnum.problems.zoo.linalg import random_sparse_spd_matrix >>> sparsemat = random_sparse_spd_matrix(dim=5, density=0.1, random_state=42) >>> sparsemat array([[1. , 0. , 0. , 0. , 0. ], [0. , 1. , 0. , 0. , 0. ], [0. , 0. , 1. , 0. , 0.24039507], [0. , 0. , 0. , 1. , 0. ], [0. , 0. , 0.24039507, 0. , 1.05778979]]) """ # Initialization random_state = _utils.as_random_state(random_state) if not 0 <= density <= 1: raise ValueError(f"Density must be between 0 and 1, but is {density}.") chol = np.eye(dim) num_off_diag_cholesky = int(0.5 * dim * (dim - 1)) num_nonzero_entries = int(num_off_diag_cholesky * density) if num_nonzero_entries > 0: # Draw entries of lower triangle (below diagonal) according to sparsity level entry_ids = np.mask_indices(n=dim, mask_func=np.tril, k=-1) idx_samples = random_state.choice(a=num_off_diag_cholesky, size=num_nonzero_entries, replace=False) nonzero_entry_ids = (entry_ids[0][idx_samples], entry_ids[1][idx_samples]) # Fill Cholesky factor chol[nonzero_entry_ids] = random_state.uniform( low=chol_entry_min, high=chol_entry_max, size=num_nonzero_entries) return chol @ chol.T
def probsolve_qp( fun: Callable[[FloatArgType], FloatArgType], fun_params0: Optional[Union[np.ndarray, randvars.RandomVariable]] = None, assume_fun: Optional[str] = None, tol: FloatArgType = 10**-5, maxiter: IntArgType = 10**4, noise_cov: Optional[Union[np.ndarray, linops.LinearOperator]] = None, callback: Optional[Callable[ [FloatArgType, FloatArgType, randvars.RandomVariable], None]] = None, random_state: RandomStateArgType = None, ) -> Tuple[float, randvars.RandomVariable, randvars.RandomVariable, Dict]: """Probabilistic 1D Quadratic Optimization. PN method solving unconstrained one-dimensional (noisy) quadratic optimization problems only needing access to function evaluations. Parameters ---------- fun : Quadratic objective function to optimize. fun_params0 : *(shape=(3, ) or (3, 1))* -- Prior on the parameters of the objective function or initial guess for the parameters. assume_fun : Type of probabilistic numerical method to use. The available options are ===================== ============= automatic selection ``None`` exact observations ``"exact"`` noisy observations ``"noise"`` ===================== ============= If ``None`` the type of method is inferred from the problem ``fun`` and prior ``fun_params0``. tol : Convergence tolerance. maxiter : Maximum number of iterations. noise_cov : *(shape=(3, 3))* -- Covariance of the additive noise on the parameters of the noisy objective function. callback : Callback function returning intermediate quantities of the optimization loop. Note that depending on the function supplied, this can slow down the solver considerably. random_state : Random state of the solver. If None (or ``np.random``), the global ``np.random`` state is used. If integer, it is used to seed the local :class:`~numpy.random.RandomState` instance. Returns ------- x_opt : Estimated minimum of the objective function. fun_opt : Belief over the optimal value of the objective function. fun_params : Belief over the parameters of the objective function. info : Additional information about the optimization, e.g. convergence. Examples -------- >>> f = lambda x: 2.0 * x ** 2 - 0.75 * x + 0.2 >>> x_opt, fun_opt, fun_params_opt, info = probsolve_qp(f) >>> print(info["iter"]) 3 """ # Choose a variant of the PN method if assume_fun is None: # Infer PN variant to use based on the problem if noise_cov is not None or fun(1.0) != fun(1.0): assume_fun = "noise" else: assume_fun = "exact" # Select appropriate prior based on the problem fun_params0 = _choose_prior(fun_params0=fun_params0) # Create a local instance of the random number generator if none is provided random_state = _utils.as_random_state(random_state) if assume_fun == "exact": # Exact 1D quadratic optimization probquadopt = ProbabilisticQuadraticOptimizer( fun_params_prior=fun_params0, policy=partial(stochastic_policy, random_state=random_state), observation_operator=function_evaluation, belief_update=partial(gaussian_belief_update, noise_cov=np.zeros(3)), stopping_criteria=[ partial(parameter_uncertainty, abstol=tol, reltol=tol), partial(maximum_iterations, maxiter=maxiter), ], ) elif assume_fun == "noise": # Noisy 1D quadratic optimization probquadopt = ProbabilisticQuadraticOptimizer( fun_params_prior=fun_params0, policy=partial(explore_exploit_policy, random_state=random_state), observation_operator=function_evaluation, belief_update=partial(gaussian_belief_update, noise_cov=noise_cov), stopping_criteria=[ partial(parameter_uncertainty, abstol=tol, reltol=tol), partial(maximum_iterations, maxiter=maxiter), ], ) else: raise ValueError( f'Unknown assumption on function evaluations: "{assume_fun}".') # Run optimization iteration x_opt0, fun_opt0, fun_params0, info = probquadopt.optimize( fun=fun, callback=callback) # Return output with information (e.g. on convergence) info["assume_fun"] = assume_fun return x_opt0, fun_opt0, fun_params0, info