def test_pow2(self): if not numpy_available: raise unittest.SkipTest('Numpy is not available') x_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] c_bounds = [(-2.5, 2.8), (0.5, 2.8), (0, 2.8), (1, 2.8), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: m = pyo.Block(concrete=True) m.x = pyo.Var(bounds=(xl, xu)) m.y = pyo.Var() m.c = pyo.Constraint( expr=pyo.inequality(body=m.y**m.x, lower=cl, upper=cu)) self.tightener(m) x = np.linspace(pyo.value(m.x.lb) + 1e-6, pyo.value(m.x.ub), 100, endpoint=False) z = np.linspace(pyo.value(m.c.lower) + 1e-6, pyo.value(m.c.upper), 100, endpoint=False) if m.y.lb is None: yl = -np.inf else: yl = m.y.lb if m.y.ub is None: yu = np.inf else: yu = m.y.ub y = np.exp(np.split(np.log(np.abs(z)), len(z)) / x) self.assertTrue(np.all(yl <= y)) self.assertTrue(np.all(yu >= y))
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 _set_axis_limits(g, axis_limits, theta_vals, theta_star): if theta_star is not None: theta_vals = theta_vals.append(theta_star, ignore_index=True) if axis_limits is None: axis_limits = {} for col in theta_vals.columns: theta_range = np.abs(theta_vals[col].max() - theta_vals[col].min()) if theta_range < 1e-10: theta_range = theta_vals[col].max()/10 axis_limits[col] = [theta_vals[col].min() - theta_range/4, theta_vals[col].max() + theta_range/4] for ax in g.fig.get_axes(): xvar, yvar, (xloc, yloc) = _get_variables(ax,theta_vals.columns) if xloc != yloc: # not on diagonal ax.set_ylim(axis_limits[yvar]) ax.set_xlim(axis_limits[xvar]) else: # on diagonal ax.set_xlim(axis_limits[xvar])
def solve_tear_direct(self, G, order, function, tears, outEdges, iterLim, tol, tol_type, report_diffs): """ Use direct substitution to solve tears. If multiple tears are given they are solved simultaneously. Arguments --------- order List of lists of order in which to calculate nodes tears List of tear edge indexes iterLim Limit on the number of iterations to run tol Tolerance at which iteration can be stopped Returns ------- list List of lists of diff history, differences between input and output values at each iteration """ hist = [] # diff at each iteration in every variable if not len(tears): # no need to iterate just run the calculations self.run_order(G, order, function, tears) return hist logger.info("Starting Direct tear convergence") ignore = tears + outEdges itercount = 0 while True: svals, dvals = self.tear_diff_direct(G, tears) err = self.compute_err(svals, dvals, tol_type) hist.append(err) if report_diffs: print("Diff matrix:\n%s" % err) if numpy.max(numpy.abs(err)) < tol: break if itercount >= iterLim: logger.warning("Direct failed to converge in %s iterations" % iterLim) return hist self.pass_tear_direct(G, tears) itercount += 1 logger.info("Running Direct iteration %s" % itercount) self.run_order(G, order, function, ignore) self.pass_edges(G, outEdges) logger.info("Direct converged in %s iterations" % itercount) return hist
def solve_tear_wegstein(self, G, order, function, tears, outEdges, iterLim, tol, tol_type, report_diffs, accel_min, accel_max): """ Use Wegstein to solve tears. If multiple tears are given they are solved simultaneously. Arguments --------- order List of lists of order in which to calculate nodes tears List of tear edge indexes iterLim Limit on the number of iterations to run tol Tolerance at which iteration can be stopped accel_min Minimum value for Wegstein acceleration factor accel_max Maximum value for Wegstein acceleration factor tol_type Type of tolerance value, either "abs" (absolute) or "rel" (relative to current value) Returns ------- list List of lists of diff history, differences between input and output values at each iteration """ hist = [] # diff at each iteration in every variable if not len(tears): # no need to iterate just run the calculations self.run_order(G, order, function, tears) return hist logger.info("Starting Wegstein tear convergence") itercount = 0 ignore = tears + outEdges gofx = self.generate_gofx(G, tears) x = self.generate_first_x(G, tears) err = self.compute_err(gofx, x, tol_type) hist.append(err) if report_diffs: print("Diff matrix:\n%s" % err) # check if it's already solved if numpy.max(numpy.abs(err)) < tol: logger.info("Wegstein converged in %s iterations" % itercount) return hist # if not solved yet do one direct step x_prev = x gofx_prev = gofx x = gofx self.pass_tear_wegstein(G, tears, gofx) while True: itercount += 1 logger.info("Running Wegstein iteration %s" % itercount) self.run_order(G, order, function, ignore) gofx = self.generate_gofx(G, tears) err = self.compute_err(gofx, x, tol_type) hist.append(err) if report_diffs: print("Diff matrix:\n%s" % err) if numpy.max(numpy.abs(err)) < tol: break if itercount > iterLim: logger.warning("Wegstein failed to converge in %s iterations" % iterLim) return hist denom = x - x_prev # this will divide by 0 at some points but we handle that below, # so ignore division warnings old_settings = numpy.seterr(divide='ignore', invalid='ignore') slope = numpy.divide((gofx - gofx_prev), denom) numpy.seterr(**old_settings) # if isnan or isinf then x and x_prev were the same, # so just do direct sub for those elements slope[numpy.isnan(slope)] = 0 slope[numpy.isinf(slope)] = 0 accel = slope / (slope - 1) accel[accel < accel_min] = accel_min accel[accel > accel_max] = accel_max x_prev = x gofx_prev = gofx x = accel * x_prev + (1 - accel) * gofx_prev self.pass_tear_wegstein(G, tears, x) self.pass_edges(G, outEdges) logger.info("Wegstein converged in %s iterations" % itercount) return hist