def _loss_and_grad_on_velocity_field(self, u): u_field = self.reshape_velocity_field(u) grad = np.zeros(u_field.shape) nx, ny, nz = self.node_nums() assert nx == ny loss = 0 cnt = 0 outlet_bd = int(np.ceil(self._outlet_range * nx)) eps = 1e-8 outlet_idx = [] for j in range(outlet_bd, ny): for k in range(nz): outlet_idx.append((nx - 1, j, k)) for i in range(outlet_bd, nx): for k in range(nz): outlet_idx.append((i, ny - 1, k)) for i, j, k in outlet_idx: cnt += 1 ux, uy, _ = u_field[i, j, k] angle = np.arctan2(uy, ux) loss += np.abs(angle - np.pi / 4) dangle_ux = -uy / (ux**2 + uy**2 + eps) dangle_uy = ux / (ux**2 + uy**2 + eps) grad[i, j, k] += ndarray([ np.sign(angle - np.pi / 4) * dangle_ux, np.sign(angle - np.pi / 4) * dangle_uy, 0 ]) loss /= cnt grad /= cnt return loss, ndarray(grad).ravel()
def __init__(self, seed, folder): np.random.seed(seed) cell_nums = (64, 48) E = 100 nu = 0.499 vol_tol = 1e-3 edge_sample_num = 2 EnvBase.__init__(self, cell_nums, E, nu, vol_tol, edge_sample_num, folder) # Initialize the parametric shapes. self._parametric_shape_info = [('bezier', 8), ('bezier', 8), ('bezier', 8)] # Initialize the node conditions. self._node_boundary_info = [] inlet_velocity = 10 inlet_range = ndarray([0.45, 0.55]) inlet_lb, inlet_ub = inlet_range * cell_nums[0] outlet_range = ndarray([0.4, 0.6]) outlet_lb, outlet_ub = outlet_range * cell_nums[1] for i in range(cell_nums[0] + 1): if inlet_lb < i < inlet_ub: self._node_boundary_info.append(((i, 0, 0), 0)) self._node_boundary_info.append(((i, 0, 1), inlet_velocity)) # Initialize the interface. self._interface_boundary_type = 'free-slip' # Other data members. self._inlet_velocity = inlet_velocity self._inlet_range = inlet_range self._outlet_range = outlet_range
def __init__(self, nu, scale): cell_nums = (int(32 * scale), int(24 * scale)) E = 100 vol_tol = 1e-3 edge_sample_num = 2 EnvBase.__init__(self, cell_nums, E, nu, vol_tol, edge_sample_num, None) # Initial condition. control_points_lower = ndarray([[32, 10], [22, 10], [12, 10], [0, 4] ]) * scale control_points_upper = ndarray([[0, 20], [12, 14], [22, 14], [32, 14] ]) * scale self._sample = np.concatenate( [control_points_lower.ravel(), control_points_upper.ravel()]) # Initialize the parametric shapes. self._parametric_shape_info = [('bezier', 8), ('bezier', 8)] # Initialize the node conditions. self._node_boundary_info = [] inlet_velocity = 1.0 for j in range(cell_nums[1] + 1): if control_points_lower[3, 1] < j < control_points_upper[0, 1]: self._node_boundary_info.append(((0, j, 0), inlet_velocity)) self._node_boundary_info.append(((0, j, 1), 0)) # Initialize the interface. self._interface_boundary_type = 'free-slip' self._scale = scale
def loss_and_grad(x): cell.Initialize(E, nu, threshold, edge_sample_num, x) loss = ndarray(loss_func()).ravel().dot(weight) grad = np.zeros(4) for i in range(4): grad[i] = ndarray(grad_func(i)).ravel().dot(weight) return loss, grad
def __init__( self, cell_nums, # 2D or 3D int array. E, # Young's modulus. nu, # Poisson's ratio. vol_tol, # vol_tol <= mixed cell <= 1 - vol_tol. edge_sample_num, # The number of samples inside each cell's axis. folder # The folder that stores temporary files. ): # The following data members are provided: # - _cell_nums and _node_nums; # - _dim; # - _cell_options; # - _folder; # - _parametric_shape_info: a list of (string, int); # - _node_boundary_info: a list of (int, float) or ((int, int, int), float) (2D) or # ((int, int, int, int), float) (3D) that describes the boundary conditions. # - _interface_boundary_type: either 'no-slip' or 'free-slip' (default). # Sanity check the inputs. cell_nums = ndarray(cell_nums).astype(np.int32) assert cell_nums.size in [2, 3] # The value of E does not quite matter as it simply scale the QP problem by a constant factor. E = float(E) assert E > 0 nu = float(nu) assert 0 < nu < 0.5 vol_tol = float(vol_tol) assert 0 < vol_tol < 0.5 edge_sample_num = int(edge_sample_num) assert edge_sample_num > 1 if folder is not None: folder = Path(folder) create_folder(folder, exist_ok=True) # Create data members. self._cell_nums = np.copy(cell_nums) self._node_nums = ndarray([n + 1 for n in cell_nums]).astype(np.int32) self._dim = self._cell_nums.size self._cell_options = { 'E': E, 'nu': nu, 'vol_tol': vol_tol, 'edge_sample_num': edge_sample_num } self._folder = folder ########################################################################### # Derived classes should implement these data members. ########################################################################### self._parametric_shape_info = [] self._node_boundary_info = [] self._interface_boundary_type = 'free-slip'
def loss_and_grad(x): shape.Initialize(cell_nums, x, True) sdf = ndarray(shape.signed_distances()) loss = sdf_weight.ravel().dot(sdf) grad = 0 for i in range(nx): for j in range(ny): grad += sdf_weight[i, j] * ndarray(shape.signed_distance_gradients((i, j))) return loss, grad
def _render_customized_2d(self, scene, ax): nx, ny = self._node_nums lines = [] for i in range(nx): for j in range(ny): v_begin = ndarray([i, j]) v_end = v_begin + ndarray(scene.GetFluidicForceDensity((i, j))) lines.append((v_begin, v_end)) ax.add_collection( mc.LineCollection(lines, colors='tab:red', linestyle='-'))
def sample(self): control_points_lower = ndarray([[34, 10], [22, 10], [12, 10], [ -2, 4 ]]) + np.random.normal(size=(4, 2)) * 0.01 control_points_upper = ndarray([[-2, 20], [12, 14], [22, 14], [ 34, 14 ]]) + np.random.normal(size=(4, 2)) * 0.01 x0 = np.concatenate( [control_points_lower.ravel(), control_points_upper.ravel()]) return x0
def __init__(self, seed, folder): np.random.seed(seed) cell_nums = (64, 64, 4) E = 100 nu = 0.499 vol_tol = 1e-2 edge_sample_num = 2 EnvBase.__init__(self, cell_nums, E, nu, vol_tol, edge_sample_num, folder) # Initialize the parametric shapes. self._parametric_shape_info = [('bezier', 11), ('bezier', 11), ('bezier', 11)] # Initialize the node conditions. self._node_boundary_info = [] inlet_range = ndarray([0.4, 0.6]) outlet_range = 0.8 cx, cy, _ = self.cell_nums() assert cx == cy nx, ny, nz = self.node_nums() inlet_bd = inlet_range * cx outlet_bd = outlet_range * cx inlet_velocity = ndarray([1.0, 0.0]) for j in range(ny): for k in range(nz): # Set the inlet at i = 0. if inlet_bd[0] < j < inlet_bd[1]: self._node_boundary_info.append( ((0, j, k, 0), inlet_velocity[0])) self._node_boundary_info.append(((0, j, k, 1), 0)) self._node_boundary_info.append(((0, j, k, 2), 0)) self._node_boundary_info.append( ((j, 0, k, 0), inlet_velocity[1])) self._node_boundary_info.append(((j, 0, k, 1), 0)) self._node_boundary_info.append(((j, 0, k, 2), 0)) # Set the top and bottom plane. for i in range(nx): for j in range(ny): for k in [0, nz - 1]: self._node_boundary_info.append(((i, j, k, 2), 0)) # Initialize the interface. self._interface_boundary_type = 'free-slip' # Other data members. self._inlet_range = inlet_range self._outlet_range = outlet_range self._inlet_bd = inlet_bd self._outlet_bd = outlet_bd self._inlet_velocity = inlet_velocity
def _loss_and_grad_on_velocity_field(self, u): u_field = self.reshape_velocity_field(u) grad = np.zeros(u_field.shape) cnt = 0 loss = 0 for j, (ux, uy) in enumerate(u_field[-1]): if ux > 0: cnt += 1 u_diff = ndarray([ux, uy]) - ndarray( [self._outlet_velocity, 0]) loss += u_diff.dot(u_diff) grad[-1, j] += 2 * u_diff loss /= cnt grad /= cnt return loss, ndarray(grad).ravel()
def _loss_and_grad_on_velocity_field(self, u): u_field = self.reshape_velocity_field(u) grad = np.zeros(u_field.shape) nx, ny, nz = self.node_nums() assert nx == ny loss = 0 cnt = 0 for i in range(nx): for j in range(ny): if self._outlet_bezier.signed_distance((i, j)) > 0: cnt += 1 uxy = u_field[i, j, nz - 1, :2] ux_pos = u_field[i + 1, j, nz - 1, :2] uy_pos = u_field[i, j + 1, nz - 1, :2] # Compute the curl. curl = ux_pos[1] - uy_pos[0] - uxy[1] + uxy[0] loss += (curl - self._desired_omega)**2 # ux_pos[1] grad[i + 1, j, nz - 1, 1] += 2 * (curl - self._desired_omega) grad[i, j + 1, nz - 1, 0] += -2 * (curl - self._desired_omega) grad[i, j, nz - 1, 1] += -2 * (curl - self._desired_omega) grad[i, j, nz - 1, 0] += 2 * (curl - self._desired_omega) loss /= cnt grad /= cnt return loss, ndarray(grad).ravel()
def _variables_to_shape_params(self, x): x = ndarray(x).copy().ravel() assert x.size == 26 cx, cy, _ = self._cell_nums assert cx == cy # Since there are two modes, we have two sets of parameters. def get_params_and_grads(rotation_angle_idx): params = np.concatenate([ x[:24] * cx, ndarray([0.6 * cx, 0.5 * cy, x[rotation_angle_idx]]), (self._upper * cx).ravel(), ndarray([0.0, self._dir_offset, 1.0]), (self._lower * cx).ravel(), ndarray([0.0, -self._dir_offset, 1.0]), (self._right * cx).ravel(), ndarray([self._dir_offset, 0.0, 1.0]), ]) grads = np.zeros((params.size, x.size)) for i in range(24): grads[i, i] = cx grads[26, rotation_angle_idx] = 1 grads = ndarray(grads) return params, grads params_on, grads_on = get_params_and_grads(24) params_off, grads_off = get_params_and_grads(25) return [(params_on, grads_on), (params_off, grads_off)]
def test_shape_composition_2d_single(shape_info, verbose): np.random.seed(42) cell_nums = (32, 24) shape = ShapeComposition2d() params = [] for name, param in shape_info: shape.AddParametricShape(name, param.size) params.append(ndarray(param).ravel()) params = np.concatenate(params) params += np.random.normal(size=params.size) * 0.01 shape.Initialize(cell_nums, params, True) if verbose: visualize_level_set(shape) # Verify the gradients. nx = shape.node_num(0) ny = shape.node_num(1) sdf_weight = np.random.normal(size=(nx, ny)) def loss_and_grad(x): shape.Initialize(cell_nums, x, True) sdf = ndarray(shape.signed_distances()) loss = sdf_weight.ravel().dot(sdf) grad = 0 for i in range(nx): for j in range(ny): grad += sdf_weight[i, j] * ndarray(shape.signed_distance_gradients((i, j))) return loss, grad from py_diff_stokes_flow.common.grad_check import check_gradients return check_gradients(loss_and_grad, params.ravel(), verbose=verbose)
def test_shape_composition_3d(verbose): return test_shape_composition_3d_single( [('polar_bezier3', ndarray([ 1, 4, 1, 4, 1, 4, 1, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 1, 4, 1, 4, 1, 4, 1, 5, 5, np.pi / 3 ]))], verbose)
def get_params_and_grads(rotation_angle_idx): params = np.concatenate([ x[:24] * cx, ndarray([0.6 * cx, 0.5 * cy, x[rotation_angle_idx]]), (self._upper * cx).ravel(), ndarray([0.0, self._dir_offset, 1.0]), (self._lower * cx).ravel(), ndarray([0.0, -self._dir_offset, 1.0]), (self._right * cx).ravel(), ndarray([self._dir_offset, 0.0, 1.0]), ]) grads = np.zeros((params.size, x.size)) for i in range(24): grads[i, i] = cx grads[26, rotation_angle_idx] = 1 grads = ndarray(grads) return params, grads
def _loss_and_grad_on_velocity_field(self, u): u_field = self.reshape_velocity_field(u) grad = np.zeros(u_field.shape) nx, ny, nz = self.node_nums() loss = 0 cnt = 0 for j in range(ny): for k in range(nz): if self._outlet_bd[0, 0] < j < self._outlet_bd[0, 1] or \ self._outlet_bd[1, 0] < j < self._outlet_bd[1, 1]: cnt += 1 u_diff = u_field[nx - 1, j, k] - ndarray([0.5, 0, 0]) loss += u_diff.dot(u_diff) grad[nx - 1, j, k] += 2 * u_diff loss /= cnt grad /= cnt return loss, ndarray(grad).ravel()
def get_bezier(radius): bezier = ShapeComposition2d() params = np.concatenate( [np.full(8, radius) * cx, ndarray([0.5 * cx, 0.5 * cy, 0])]) bezier.AddParametricShape('polar_bezier', params.size) cxy = StdIntArray2d((int(cx), int(cy))) bezier.Initialize(cxy, params, True) return bezier
def reshape_velocity_field(self, u): u_array = ndarray(u) assert len(u_array.shape) in [1, self._dim + 1] assert u_array.size == np.prod(self._node_nums) * self._dim if len(u_array.shape) == 1: return np.copy(u_array).reshape( tuple(self._node_nums) + (self._dim, )) else: return np.copy(u_array).ravel()
def plot_velocity_field(ax, u_field, s, u_max): x_size = ndarray(np.arange(u_field.shape[0])) / s y_size = ndarray(np.arange(u_field.shape[1])) / s X, Y = np.meshgrid(x_size, y_size) u_norm = np.sqrt(np.sum(u_field**2, axis=(2, ))) strm = ax.streamplot(X, Y, u_field[:, :, 0].T, u_field[:, :, 1].T, density=(cell_nums[0] / cell_nums[1] * 3, 3), color=u_norm.T, norm=matplotlib.colors.Normalize(0, u_max), arrowstyle='->', linewidth=1.5, cmap='coolwarm') ax.set_xlim([0, cell_nums[0]]) ax.set_ylim([0, cell_nums[1]]) ax.set_xticks([]) ax.set_yticks([])
def _variables_to_shape_params(self, x): x = ndarray(x).copy().ravel() assert x.size == 5 cx, cy = self._cell_nums # Convert x to the shape parameters. lower = ndarray([ [1, x[4]], x[2:4], x[:2], [0, self._inlet_range[0]], ]) lower[:, 0] *= cx lower[:, 1] *= cy upper = ndarray([ [0, self._inlet_range[1]], [x[0], 1 - x[1]], [x[2], 1 - x[3]], [1, 1 - x[4]], ]) upper[:, 0] *= cx upper[:, 1] *= cy params = np.concatenate([lower.ravel(), upper.ravel()]) # Jacobian. J = np.zeros((params.size, x.size)) J[1, 4] = 1 J[2, 2] = 1 J[3, 3] = 1 J[4, 0] = 1 J[5, 1] = 1 J[10, 0] = 1 J[11, 1] = -1 J[12, 2] = 1 J[13, 3] = -1 J[15, 4] = -1 # Scale it by cx and cy. J[:, 0] *= cx J[:, 2] *= cx J[:, 1] *= cy J[:, 3] *= cy J[:, 4] *= cy return ndarray(params).copy(), ndarray(J).copy()
def _variables_to_shape_params(self, x): x = ndarray(x).copy().ravel() assert x.size == 32 cx, cy, _ = self._cell_nums assert cx == cy params = np.concatenate([ np.full(8, self._inlet_radius), x, np.full(8, self._outlet_radius), ndarray([0.5, 0.5, 0]), ]) params[:-1] *= cx # Jacobian. J = np.zeros((params.size, x.size)) for i in range(x.size): J[8 + i, i] = cx return ndarray(params).copy(), ndarray(J).copy()
def test_bezier_2d(verbose): np.random.seed(42) folder = Path('bezier_2d') cell_nums = (32, 24) control_points = ndarray([[32, 12], [22, 6], [12, 18], [0, 12]]) control_points[:, 1] += np.random.normal(size=4) bezier = Bezier2d() bezier.Initialize(cell_nums, control_points.ravel(), True) sdf = ndarray(bezier.signed_distances()) sdf_master = np.load(folder / 'sdf_master.npy') if np.max(np.abs(sdf - sdf_master)) > 0: if verbose: print_error('Incorrect signed distance function.') return False if verbose: visualize_level_set(bezier) # Verify the gradients. nx = bezier.node_num(0) ny = bezier.node_num(1) sdf_weight = np.random.normal(size=(nx, ny)) def loss_and_grad(x): bezier = Bezier2d() bezier.Initialize(cell_nums, x.ravel(), True) sdf = ndarray(bezier.signed_distances()) loss = sdf_weight.ravel().dot(sdf) grad = 0 for i in range(nx): for j in range(ny): grad += sdf_weight[i, j] * ndarray( bezier.signed_distance_gradients((i, j))) return loss, grad from py_diff_stokes_flow.common.grad_check import check_gradients return check_gradients(loss_and_grad, control_points.ravel(), verbose=verbose)
def test_shape_composition_2d(verbose): flag = test_shape_composition_2d_single([ ( 'bezier', ndarray([ [32, 10], [22, 10], [12, 4], [0, 4] ]) ), ( 'bezier', ndarray([ [0, 20], [12, 20], [22, 14], [32, 14] ]) ), ], verbose) if not flag: return False flag = test_shape_composition_2d_single([ ( 'plane', ndarray([1, 0.1, -16]) ), ( 'sphere', ndarray([16, 12, 8]) ), ], verbose) if not flag: return False flag = test_shape_composition_2d_single([ ( 'polar_bezier', ndarray([ 4, 8, 4, 8, 4, 8, 4, 8, 16, 12, np.pi / 3 ]) ), ], verbose) if not flag: return False return True
def _loss_and_grad_on_velocity_field(self, u): u_field = self.reshape_velocity_field(u) grad = np.zeros(u_field.shape) nx, ny, nz = self.node_nums() assert nx == ny loss = 0 cnt = 0 target_velocity = ndarray([self._inlet_velocity[0], self._inlet_velocity[1], 0.]) for j in range(ny): for k in range(nz): if j > self._outlet_bd: cnt += 1 u_diff = u_field[nx - 1, j, k] - target_velocity loss += u_diff.dot(u_diff) grad[nx - 1, j, k] += 2 * u_diff cnt += 1 u_diff = u_field[j, ny - 1, k] - target_velocity loss += u_diff.dot(u_diff) grad[j, ny - 1, k] += 2 * u_diff loss /= cnt grad /= cnt return loss, ndarray(grad).ravel()
def loss_and_grad(u_single, target_field): u_field = self.reshape_velocity_field(u_single) loss = 0 grad = np.zeros(u_field.shape) cnt = 0 for j in range(ny): for k in range(nz): if self._boundary_bezier.signed_distance( (int(nx - 1), int(j), int(k))) < 0: uxyz = u_field[nx - 1, j, k] u_diff = uxyz - target_field[j, k] loss += u_diff.dot(u_diff) grad[nx - 1, j, k] += 2 * u_diff cnt += 1 loss /= cnt grad /= cnt return loss, ndarray(grad).ravel()
def _variables_to_shape_params(self, x): x = ndarray(x).copy().ravel() assert x.size == 5 cx, cy, _ = self._cell_nums assert cx == cy lower_left = ndarray([ [self._inlet_range[0], 0], [x[4], x[0]], [x[0], x[4]], [0, self._inlet_range[0]] ]) right = ndarray([ [1., self._outlet_range], [x[2], x[3]], [self._inlet_range[1], x[1]], [self._inlet_range[1], 0] ]) upper = ndarray([ [0, self._inlet_range[1]], [x[1], self._inlet_range[1]], [x[3], x[2]], [self._outlet_range, 1.] ]) lower_left *= cx right *= cx upper *= cx params = np.concatenate([lower_left.ravel(), [-0.01, -0.01, 1], right.ravel(), [0.01, 0, 1], upper.ravel(), [0, 0.01, 1], ]) # Jacobian. J = np.zeros((params.size, x.size)) J[2, 4] = J[3, 0] = 1 J[4, 0] = J[5, 4] = 1 J[13, 2] = J[14, 3] = 1 J[16, 1] = 1 J[24, 1] = J[26, 3] = J[27, 2] = 1 J *= cx return ndarray(params).copy(), ndarray(J).copy()
def test_cell_2d(verbose): np.random.seed(42) cell = Cell2d() E = 1e5 nu = 0.45 threshold = 1e-3 edge_sample_num = 3 # Consider a line that passes (0.5, 0.5) with a slope between 1/3 and 1. p = ndarray([0.5, 0.5]) k = np.random.uniform(low=1 / 3, high=1) # Line equation: (y - p[1]) / (x - p[0]) = k. # y - p[1] = kx - kp[0]. # kx - y + p[1] - kp[0]. line_eq = ndarray([k, -1, p[1] - k * p[0]]) # Solid area: line_eq >= 0. # So, the lower part is the solid area. # This means corner distance from [0, 0] and [1, 0] are positive. sdf_at_corners = [] for c in [(0, 0), (0, 1), (1, 0), (1, 1)]: sdf_at_corners.append( (line_eq[0] * c[0] + line_eq[1] * c[1] + line_eq[2]) / np.linalg.norm(line_eq[:2])) cell.Initialize(E, nu, threshold, edge_sample_num, sdf_at_corners) # Check if all areas are correct. dx = 1 / 3 x_intercept = (-line_eq[1] * dx - line_eq[2]) / line_eq[0] area_00 = x_intercept * x_intercept * k * 0.5 area_01 = dx**2 - (dx - x_intercept)**2 * k * 0.5 area_02 = dx**2 area_10 = 0 area_11 = dx**2 * 0.5 area_12 = dx**2 area_20 = 0 area_21 = dx**2 - area_01 area_22 = dx**2 - area_00 area = ndarray([ area_00, area_01, area_02, area_10, area_11, area_12, area_20, area_21, area_22 ]) area_from_cell = ndarray(cell.sample_areas()) if not np.allclose(area, area_from_cell): if verbose: print_error('area is inconsistent.') return False # Check if all line segments are correct. line_00 = np.sqrt(1 + k**2) * x_intercept line_01 = np.sqrt(1 + k**2) * (dx - x_intercept) line_02 = 0 line_10 = 0 line_11 = np.sqrt(1 + k**2) * dx line_12 = 0 line_20 = 0 line_21 = line_01 line_22 = line_00 line = ndarray([ line_00, line_01, line_02, line_10, line_11, line_12, line_20, line_21, line_22 ]) line_from_cell = ndarray(cell.sample_boundary_areas()) if not np.allclose(line, line_from_cell): if verbose: print_error('boundary area is inconsistent.') return False # Test the gradients. for loss_func, grad_func, name in [ (cell.py_normal, cell.py_normal_gradient, 'normal'), (cell.offset, cell.py_offset_gradient, 'offset'), (cell.sample_areas, cell.py_sample_areas_gradient, 'sample_areas'), (cell.sample_boundary_areas, cell.py_sample_boundary_areas_gradient, 'sample_boundary_areas'), (cell.area, cell.py_area_gradient, 'area'), (cell.py_energy_matrix, cell.py_energy_matrix_gradient, 'energy_matrix'), (cell.py_dirichlet_vector, cell.py_dirichlet_vector_gradient, 'dirichlet_vector') ]: if verbose: print_info('Checking loss and gradient:', name) dim = ndarray(loss_func()).size weight = np.random.normal(size=dim) def loss_and_grad(x): cell.Initialize(E, nu, threshold, edge_sample_num, x) loss = ndarray(loss_func()).ravel().dot(weight) grad = np.zeros(4) for i in range(4): grad[i] = ndarray(grad_func(i)).ravel().dot(weight) return loss, grad if not check_gradients( loss_and_grad, ndarray(sdf_at_corners), verbose=verbose): if verbose: print_error('Gradient check failed.') return False return True
def _variables_to_shape_params(self, x): x = ndarray(x).copy().ravel() assert x.size == 8 cx, cy, _ = self._cell_nums lower = ndarray([ [1, self._outlet_range[0, 0]], x[2:4], x[:2], [0, self._inlet_range[0, 0]], ]) right = ndarray([ [1, self._outlet_range[1, 0]], [x[4], 1 - x[5]], x[4:6], [1, self._outlet_range[0, 1]], ]) upper = ndarray([ [0, self._inlet_range[1, 1]], [x[0], 1 - x[1]], [x[2], 1 - x[3]], [1, self._outlet_range[1, 1]], ]) left = ndarray([ [0, self._inlet_range[0, 1]], x[6:8], [x[6], 1 - x[7]], [0, self._inlet_range[1, 0]], ]) cxy = ndarray([cx, cy]) lower *= cxy right *= cxy upper *= cxy left *= cxy params = np.concatenate([lower.ravel(), [0, -0.01, 1], right.ravel(), [0.01, 0, 1], upper.ravel(), [0, 0.01, 1], left.ravel(), [-0.01, 0, 1] ]) # Jacobian. J = np.zeros((params.size, x.size)) J[2, 2] = J[3, 3] = 1 J[4, 0] = J[5, 1] = 1 J[13, 4] = 1 J[14, 5] = -1 J[15, 4] = J[16, 5] = 1 J[24, 0] = 1 J[25, 1] = -1 J[26, 2] = 1 J[27, 3] = -1 J[35, 6] = J[36, 7] = 1 J[37, 6] = 1 J[38, 7] = -1 J[:, ::2] *= cx J[:, 1::2] *= cy return ndarray(params).copy(), ndarray(J).copy()
def upper_bound(self): return ndarray([.49, .49, .99, .49, .99, .49, .49, .49])
def lower_bound(self): return ndarray([.01, .01, .49, .01, .49, .01, .01, .01])