def __init__(self, budget_membership_mat, rhs_vec): """ BudgetSet constructor Args: budget_membership_mat: A matrix with 0-1 entries to designate which uncertain parameters participate in each budget constraint. Here, each row is associated with a separate budget constraint. rhs_vec: Vector (``list``) of right-hand side values for the budget constraints. """ # === Non-zero number of columns mat = np.asarray(budget_membership_mat) rhs = np.asarray(rhs_vec) if len(mat.shape) == 1: cols = mat.shape else: cols = mat.shape[1] if cols == 0: raise AttributeError( "Budget membership matrix must have non-zero number of columns." ) # === Assert is valid matrix (same number of columns across all rows if not all(len(row) == cols for row in budget_membership_mat): raise AttributeError( "Budget membership matrix must be a valid matrix, " "e.g. same number of column entries across rows.") # === Matrix dimension compatibility if mat.shape[0] != rhs.shape[0]: raise AttributeError( "Rows of lhs_coefficients_mat matrix must equal rows of rhs_vec lists." ) # === Ensure a 0-1 matrix if any(not np.isclose(elem, 0) and not np.isclose(elem, 1) for row in budget_membership_mat for elem in row): raise AttributeError( "Budget membership matrix must be a matrix of 0's and 1's.") # === No all zero rows if all(elem == 0 for row in budget_membership_mat for elem in row): raise AttributeError( "All zero rows are not permitted in the budget membership matrix." ) # === Ensure 0 <= rhs_i for all i if any(rhs_vec[i] < 0 for i in range(len(rhs_vec))): raise AttributeError("RHS vector entries must be >= 0.") # === Non-emptiness is implied by the set # === Add constraints such that uncertain params are >= 0 # === This adds >=0 bound on all parameters in the set cols = mat.shape[1] identity = np.identity(cols) * -1 for row in identity: budget_membership_mat.append(row.tolist()) for i in range(identity.shape[0]): rhs_vec.append(0) self.coefficients_mat = budget_membership_mat self.rhs_vec = rhs_vec self.type = "budget"
def set_as_constraint(self, uncertain_params, **kwargs): """ Function to generate constraints for the PolyhedralSet uncertainty set. Args: uncertain_params: uncertain parameter objects for writing constraint objects """ # === Ensure valid dimensions of lhs and rhs w.r.t uncertain_params if np.asarray(self.coefficients_mat).shape[1] != len(uncertain_params): raise AttributeError("Columns of coefficients_mat matrix " "must equal length of uncertain parameters list.") set_i = list(range(len(self.coefficients_mat))) conlist = ConstraintList() conlist.construct() for i in set_i: constraint = 0 for j in range(len(uncertain_params)): constraint += float(self.coefficients_mat[i][j]) * uncertain_params[j] conlist.add(constraint <= float(self.rhs_vec[i])) return conlist
def __init__(self, center, shape_matrix, scale=1): """ EllipsoidalSet constructor Args: center: Vector (``list``) of uncertain parameter values around which deviations are restrained. shape_matrix: Positive semi-definite matrix, effectively a covariance matrix for constraint and bounds determination. scale: Right-hand side value for the ellipsoid. """ # === Valid data in lists/matrixes if not all(isinstance(elem, (int, float)) for row in shape_matrix for elem in row): raise AttributeError("Matrix shape_matrix must be real-valued and numeric.") if not all(isinstance(elem, (int, float)) for elem in center): raise AttributeError("Vector center must be real-valued and numeric.") if not isinstance(scale, (int, float)): raise AttributeError("Ellipse scale must be a real-valued numeric.") # === Valid matrix dimensions num_cols = len(shape_matrix[0]) if not all(len(row) == num_cols for row in shape_matrix): raise AttributeError("Shape matrix must have valid matrix dimensions.") # === Ensure shape_matrix is a square matrix array_shape_mat = np.asarray(shape_matrix) if array_shape_mat.shape[0] != array_shape_mat.shape[1]: raise AttributeError("Shape matrix must be square.") # === Ensure dimensions of shape_matrix are same as dimensions of uncertain_params if array_shape_mat.shape[1] != len(center): raise AttributeError("Shape matrix must be " "same dimensions as vector of uncertain parameters.") # === Symmetric shape_matrix if not np.all(np.abs(array_shape_mat-array_shape_mat.T) < 1e-8): raise AttributeError("Shape matrix must be symmetric.") # === Ensure scale is non-negative if scale < 0: raise AttributeError("Scale of ellipse (rhs) must be non-negative.") # === Check if shape matrix is invertible try: np.linalg.inv(shape_matrix) except np.linalg.LinAlgError as err: raise("Error with shape matrix supplied to EllipsoidalSet object being singular. %s" % err) # === Check is shape matrix is positive semidefinite if not all(np.linalg.eigvals(shape_matrix) >= 0): raise("Non positive-semidefinite shape matrix.") # === Ensure matrix is not degenerate, for determining inferred bounds try: for i in range(len(shape_matrix)): np.power(shape_matrix[i][i], 0.5) except: raise AttributeError("Shape matrix must be non-degenerate.") self.center = center self.shape_matrix = shape_matrix self.scale = scale self.type = "ellipsoidal"
def __init__(self, lhs_coefficients_mat, rhs_vec): """ PolyhedralSet constructor Args: lhs_coefficients_mat: Matrix of left-hand side coefficients for the linear inequality constraints defining the polyhedral set. rhs_vec: Vector (``list``) of right-hand side values for the linear inequality constraints defining the polyhedral set. """ # === Real valued data mat = np.asarray(lhs_coefficients_mat) if not all( isinstance(elem, (int, float)) for row in lhs_coefficients_mat for elem in row): raise AttributeError( "Matrix lhs_coefficients_mat must be real-valued and numeric.") if not all(isinstance(elem, (int, float)) for elem in rhs_vec): raise AttributeError( "Vector rhs_vec must be real-valued and numeric.") # === Check columns of A must be same length as rhs if mat.shape[0] != len(rhs_vec): raise AttributeError( "Rows of lhs_coefficients_mat matrix must equal length of rhs_vec list." ) # === Columns are non-zero if mat.shape[1] == 0: raise AttributeError( "Columns of lhs_coefficients_mat must be non-zero.") # === Matrix is not all zeros if all( np.isclose(elem, 0) for row in lhs_coefficients_mat for elem in row): raise AttributeError( "Matrix lhs_coefficients_mat cannot be all zeroes.") # === Non-emptiness res = sp.optimize.linprog(c=np.zeros(mat.shape[1]), A_ub=mat, b_ub=rhs_vec, method="simplex") if not res.success: raise AttributeError( "User-defined PolyhedralSet was determined to be empty. " "Please check the set of constraints supplied during set construction." ) # === Boundedness if res.status == 3: # scipy linprog status == 3 indicates unboundedness raise AttributeError( "User-defined PolyhedralSet was determined to be unbounded. " "Please augment the set of constraints supplied during set construction." ) self.coefficients_mat = lhs_coefficients_mat self.rhs_vec = rhs_vec self.type = "polyhedral"
def parameter_bounds(self): """ Bounds on the realizations of the uncertain parameters, as inferred from the uncertainty set. """ membership_mat = np.asarray(self.coefficients_mat) rhs_vec = self.rhs_vec parameter_bounds = [] for i in range(membership_mat.shape[1]): col = column(membership_mat, i) ub = min(list(col[j] * rhs_vec[j] for j in range(len(rhs_vec)))) lb = 0 parameter_bounds.append((lb, ub)) return parameter_bounds
def __init__(self, origin, number_of_factors, psi_mat, beta): """ FactorModelSet constructor Args: origin: Vector (``list``) of uncertain parameter values around which deviations are restrained. number_of_factors: Natural number representing the dimensionality of the space to which the set projects. psi: Matrix with non-negative entries designating each uncertain parameter's contribution to each factor. Here, each row is associated with a separate uncertain parameter and each column with a separate factor. beta: Number in [0,1] representing the fraction of the independent factors that can simultaneously attain their extreme values. Setting 'beta = 0' will enforce that as many factors will be above 0 as there will be below 0 (i.e., "zero-net-alpha" model). Setting 'beta = 1' produces the hyper-rectangle [origin - psi e, origin + psi e], where 'e' is the vector of ones. """ mat = np.asarray(psi_mat) # === Numeric valued arrays if not all(isinstance(elem, (int, float)) for elem in origin): raise AttributeError( "All elements of origin vector must be numeric.") if not all( isinstance(elem, (int, float)) for row in psi_mat for elem in row): raise AttributeError( "All elements of psi_mat vector must be numeric.") if not isinstance(beta, (int, float)): raise AttributeError("Beta parameter must be numeric.") if not isinstance(number_of_factors, (int)): raise AttributeError("number_of_factors must be integer.") # === Ensure dimensions of psi are n x F if mat.shape != (len(origin), number_of_factors): raise AttributeError( "Psi matrix must be of dimensions n x F where n is dim(uncertain_params)" "and F is number_of_factors.") # === Ensure beta in [0,1] if beta > 1 or beta < 0: raise AttributeError("Beta parameter must be in [0,1].") # === No all zero columns of psi_mat for idx in range(mat.shape[1]): if all(np.isclose(elem, 0) for elem in mat[:, idx]): raise AttributeError( "Psi matrix cannot have all zero columns.") # === Psi must be strictly positive entries for idx in range(mat.shape[1]): if any(elem < 0 for elem in mat[:, idx]): raise AttributeError( "Psi matrix cannot have any negative entries. All factors must be non-negative." ) self.origin = origin self.number_of_factors = number_of_factors self.psi_mat = psi_mat self.beta = beta self.type = "factor_model"
def set_as_constraint(self, uncertain_params, **kwargs): """ Function to generate constraints for the BudgetSet uncertainty set. Args: uncertain_params: uncertain parameter objects for writing constraint objects """ # === Ensure matrix cols == len uncertain params if np.asarray(self.coefficients_mat).shape[1] != len(uncertain_params): raise AttributeError("Budget membership matrix must have compatible " "dimensions with uncertain parameters vector.") conlist = PolyhedralSet.set_as_constraint(self, uncertain_params) return conlist
def point_in_set(self, point): """ Calculates if supplied ``point`` is contained in the uncertainty set. Returns True or False. Args: point: the point being checked for membership in the set """ inv_psi = np.linalg.pinv(self.psi_mat) diff = np.asarray(list(point[i] - self.origin[i] for i in range(len(point)))) cassis = np.dot(inv_psi, np.transpose(diff)) if abs(sum(cassi for cassi in cassis)) <= self.beta * self.number_of_factors and \ all(cassi >= -1 and cassi <= 1 for cassi in cassis): return True else: return False
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): if method == '__call__': # Convert all incoming types to ndarray (to prevent recursion) args = [np.asarray(i) for i in inputs] # Set the return type to be an 'object'. This prevents the # logical operators from casting the result to a bool. This # requires numpy >= 1.6 kwargs['dtype'] = object # Delegate to the base ufunc, but return an instance of this # class so that additional operators hit this method. ans = getattr(ufunc, method)(*args, **kwargs) if isinstance(ans, np.ndarray): if ans.size == 1: return ans[0] return ans.view(NumericNDArray) else: return ans
def dim(self): """ Dimension of the uncertainty set, i.e., number of parameters in “uncertain_params” list. """ return np.asarray(self.coefficients_mat).shape[1]