def eval_3diou(dt_floor_coor, dt_ceil_coor, gt_floor_coor, gt_ceil_coor, ch=-1.6, coorW=1024, coorH=512): ''' Evaluate 3D IoU of "convex layout". Instead of voxelization, this function use halfspace intersection to evaluate the volume. Input parameters: dt_ceil_coor, dt_floor_coor, gt_ceil_coor, gt_floor_coor have to be in shape [N, 2] and in the format of: [[x, y], ...] listing the corner position from left to right on the equirect image. ''' dt_floor_coor = np.array(dt_floor_coor) dt_ceil_coor = np.array(dt_ceil_coor) gt_floor_coor = np.array(gt_floor_coor) gt_ceil_coor = np.array(gt_ceil_coor) assert (dt_floor_coor[:, 0] != dt_ceil_coor[:, 0]).sum() == 0 assert (gt_floor_coor[:, 0] != gt_ceil_coor[:, 0]).sum() == 0 N = len(dt_floor_coor) dt_floor_xyz = np.hstack([ np_coor2xy(dt_floor_coor, ch, coorW, coorH), np.zeros((N, 1)) + ch, ]) gt_floor_xyz = np.hstack([ np_coor2xy(gt_floor_coor, ch, coorW, coorH), np.zeros((N, 1)) + ch, ]) dt_c = np.sqrt((dt_floor_xyz[:, :2]**2).sum(1)) gt_c = np.sqrt((gt_floor_xyz[:, :2]**2).sum(1)) dt_v2 = np_coory2v(dt_ceil_coor[:, 1], coorH) gt_v2 = np_coory2v(gt_ceil_coor[:, 1], coorH) dt_ceil_z = dt_c * np.tan(dt_v2) gt_ceil_z = gt_c * np.tan(gt_v2) dt_ceil_xyz = dt_floor_xyz.copy() dt_ceil_xyz[:, 2] = dt_ceil_z gt_ceil_xyz = gt_floor_xyz.copy() gt_ceil_xyz[:, 2] = gt_ceil_z dt_halfspaces = xyzlst2halfspaces(dt_floor_xyz, dt_ceil_xyz) gt_halfspaces = xyzlst2halfspaces(gt_floor_xyz, gt_ceil_xyz) in_halfspaces = HalfspaceIntersection( np.concatenate([dt_halfspaces, gt_halfspaces]), np.zeros(3)) dt_halfspaces = HalfspaceIntersection(dt_halfspaces, np.zeros(3)) gt_halfspaces = HalfspaceIntersection(gt_halfspaces, np.zeros(3)) in_volume = ConvexHull(in_halfspaces.intersections).volume dt_volume = ConvexHull(dt_halfspaces.intersections).volume gt_volume = ConvexHull(gt_halfspaces.intersections).volume un_volume = dt_volume + gt_volume - in_volume return in_volume / un_volume
def build_polys(self, poly): if poly.inner is None: A = np.vstack([p.T for p in poly.points]) poly.inner = ConvexHull(A, incremental=True) self.feasible_point = np.sum(A, axis=0) / A.shape[0] else: try: poly.inner.add_points(poly.points[-1].T, restart=False) except QhullError: print("Incremental processing failed") A = np.vstack([p.T for p in poly.points]) poly.inner = ConvexHull(A, incremental=True) if poly.outer is None: if poly.inner is None: raise AssertionError( "Inner polygon should have been initialized") A = np.vstack(poly.directions) b = np.vstack(poly.offsets) poly.outer = HalfspaceIntersection(np.hstack((A, -b)), self.feasible_point, incremental=True) else: hs = np.zeros((1, poly.outer.halfspaces.shape[1])) hs[:, :-1] = poly.directions[-1] hs[:, -1] = -poly.offsets[-1] try: poly.outer.add_halfspaces(hs) except QhullError: print("Incremental halfspaces failed") A = np.vstack(poly.directions) b = np.vstack(poly.offsets) c = np.zeros((A.shape[1], )) c[-1] = -1 res = linprog(c, A_ub=np.hstack( (A[:, :-1], np.ones((A.shape[0], 1)))), b_ub=-A[:, -1:], bounds=(None, None)) if res.success: self.feasible_point = res.x[:-1] else: raise RuntimeError("No interior point found") poly.outer = HalfspaceIntersection(np.hstack((A, -b)), self.feasible_point, incremental=True)
def to_hsi(r: Cuboid) -> HalfspaceIntersection: hs_norms, hs_origins = cube_to_normals(np.array(r.position), np.array(r.dimensions), np.array(r.orientation)) hs_ineqs = halfspaces_to_inequalities(hs_norms, hs_origins) in_point = np.array(r.position) return HalfspaceIntersection(hs_ineqs, in_point)
def proj_hsi_to_plane(hsi: HalfspaceIntersection, p_norm: Vector3D, p_origin: Vector3D) -> Optional[HalfspaceIntersection]: projected_halfspaces = [] rev_rot = rotation_to_euler(p_norm, Vector3D(0, 0, 1)) for hs in hsi.halfspaces: hs_norm = hs[:-1] dp = np.dot(hs_norm, p_norm) if 1.0 - np.abs(dp) >= 1e-9: hs_origin = -hs[-1] * hs[:-1] projected_origin = project_to_plane_intersection( hs_origin, hs_norm, hs_origin, p_norm, p_origin) projected_norm = hs_norm - dp * p_norm projected_norm = projected_norm / np.linalg.norm(projected_norm) aa_norm = rotate_euler(projected_norm, rev_rot) new_b = -np.dot(projected_origin - p_origin, projected_norm) projected_halfspaces.append(np.append(aa_norm[:-1], new_b)) projected_halfspaces = np.array(projected_halfspaces) proj_feasible_point = feasible_point(projected_halfspaces) if proj_feasible_point is None: return None return HalfspaceIntersection(projected_halfspaces, proj_feasible_point)
def __post_init__(self): self.cross_point_dicts = {} large_minus_number = -1e4 for name, ce in self.charge_energies_dict.items(): half_spaces = [] for charge, corr_energy in ce.charge_energies: half_spaces.append([-charge, 1, -corr_energy]) half_spaces.append([-1, 0, self.e_min]) half_spaces.append([1, 0, -self.e_max]) half_spaces.append([0, -1, large_minus_number]) feasible_point = np.array([(self.e_min + self.e_max) / 2, -1e3]) hs = HalfspaceIntersection(np.array(half_spaces), feasible_point) boundary_points = [] inner_cross_points = [] for intersection in hs.intersections: x, y = np.round(intersection, 8) if self.e_min + 0.001 < x < self.e_max - 0.001: inner_cross_points.append([x, y]) elif y > large_minus_number + 1: boundary_points.append([x, y]) self.cross_point_dicts[name] = CrossPoints(inner_cross_points, boundary_points)
def plot_halfspace(self, x, y, ax, sys, idx_offset=0): dim = self.model.dim comple_dim = np.asarray( [True if i in [x, y] else False for i in range(dim)]) 'Halfspace constraint matrix' A = sys.A b = sys.b phase_intersect = np.hstack((A, -np.asarray([b]).T)) center_pt = np.asarray(sys.chebyshev_center.center) 'Run scipy.spatial.HalfspaceIntersection.' hs = HalfspaceIntersection(phase_intersect, center_pt) vertices = np.asarray(hs.intersections) proj_vertices = np.unique(vertices[:, comple_dim], axis=0).tolist() 'Sort by polar coordinates to ensure proper plotting of boundary' proj_vertices.sort( key=lambda v: math.atan2(v[1] - center_pt[1], v[0] - center_pt[0])) ptope = pat.Polygon(proj_vertices, fill=True, color=f"C{idx_offset}", alpha=0.4) ax.add_patch(ptope) inter_x, inter_y = zip(*proj_vertices) ax.scatter(inter_x, inter_y, s=0.1)
def cross_points(self, ef_min, ef_max, base_ef=0.0): large_minus_number = -1e4 half_spaces = [] for charge, energy, correction in zip(self.charges, self.energies, self.corrections): corrected_energy = energy + correction half_spaces.append([-charge, 1, -corrected_energy]) half_spaces.append([-1, 0, ef_min]) half_spaces.append([1, 0, -ef_max]) half_spaces.append([0, -1, large_minus_number]) feasible_point = np.array([(ef_min + ef_max) / 2, -1e3]) hs = HalfspaceIntersection(np.array(half_spaces), feasible_point) boundary_points = [] inner_cross_points = [] for intersection in hs.intersections: x, y = np.round(intersection, 8) if ef_min + 0.001 < x < ef_max - 0.001: inner_cross_points.append([x - base_ef, y]) elif y > large_minus_number + 1: boundary_points.append([x - base_ef, y]) return CrossPoints(inner_cross_points, boundary_points)
def non_trivial_vertices(halfspaces): """ Returns all vertex, label pairs (ignoring the origin). Parameters: halfspaces: a numpy array Returns: generator """ feasible_point = find_feasible_point(halfspaces) hs = HalfspaceIntersection(halfspaces, feasible_point) hs.close() return ((v, labels(v, halfspaces)) for v in hs.intersections if max(v) > 0)
def to_hsi(r: Rectangle3D) -> HalfspaceIntersection: hs_origins = np.array([[r.width / 2.0, 0], [0, r.length / 2.0], [-r.width / 2.0, 0], [0, -r.length / 2.0]]) hs_norms = np.array([[-1, 0], [0, -1], [1, 0], [0, 1]]) hs_ineqs = halfspaces_to_inequalities(hs_norms, hs_origins) return HalfspaceIntersection(hs_ineqs, np.array([0.0, 0.0]))
def set_offset(self, offset, face_index): '''Sets the offset value of one or all faces. @param offset The offset value. Must be a float >= 0.0. @param face_index If < 0, sets *all* faces, otherwise a valid index sets the single, indexed face. ''' if (offset < 0): offset = 0.0 if (face_index < 0): self.deltas[:] = offset else: self.deltas[face_index] = offset temp_planes = self.planes.copy() temp_planes[:, 3] -= self.deltas hs = HalfspaceIntersection(temp_planes, self.feasible_point) verts = hs.intersections # (N, 3) shape -- each row is a vertex # TODO: Due to numerical imprecision, I can end up with vertices in verts that are # *almost* the same (and *should* be the same. I should go through and merge them. # Implications: # faces = [] for i, f in enumerate(self.faces): d = temp_planes[i, 3] # the const for the ith plane n = self.normals[:, i] # The norm for the ith plane, (3,) shape dist = np.dot(verts, n) + d # (N, 3) * (3,) -> (N,) indices = np.where(np.abs(dist) < 1e-6)[0] # (M,) matrix if (list(indices)): faces.append(orderVertices(list(indices), n, verts)) else: faces.append([]) self.hull = SimpleMesh(verts, faces, self.normals)
def plot2DPhase(self, x, y): Timer.start('Phase') x_var, y_var = self.vars[x], self.vars[y] 'Define the following projected normal vectors.' norm_vecs = np.zeros([6, self.dim_sys]) norm_vecs[0][x] = 1 norm_vecs[1][y] = 1 norm_vecs[2][x] = -1 norm_vecs[3][y] = -1 norm_vecs[4][x] = 1 norm_vecs[4][y] = 1 norm_vecs[5][x] = -1 norm_vecs[5][y] = -1 fig, ax = plt.subplots(1) comple_dim = [i for i in range(self.dim_sys) if i not in [x, y]] 'Initialize objective function for Chebyshev intersection LP routine.' c = [0 for _ in range(self.dim_sys + 1)] c[-1] = 1 for bund in self.flowpipe: bund_A, bund_b = bund.getIntersect() 'Compute the normal vector offsets' bund_off = np.empty([len(norm_vecs), 1]) for i in range(len(norm_vecs)): bund_off[i] = minLinProg(np.negative(norm_vecs[i]), bund_A, bund_b).fun 'Remove irrelevant dimensions. Mostly doing this to make HalfspaceIntersection happy.' phase_intersect = np.hstack((norm_vecs, bund_off)) phase_intersect = np.delete(phase_intersect, comple_dim, axis=1) 'Compute Chebyshev center of intersection.' row_norm = np.reshape(np.linalg.norm(norm_vecs, axis=1), (norm_vecs.shape[0], 1)) center_A = np.hstack((norm_vecs, row_norm)) neg_bund_off = np.negative(bund_off) center_pt = maxLinProg(c, center_A, list(neg_bund_off.flat)).x center_pt = np.asarray( [b for b_i, b in enumerate(center_pt) if b_i in [x, y]]) 'Run scipy.spatial.HalfspaceIntersection.' hs = HalfspaceIntersection(phase_intersect, center_pt) inter_x, inter_y = zip(*hs.intersections) ax.set_xlabel('{}'.format(x_var)) ax.set_ylabel('{}'.format(y_var)) ax.fill(inter_x, inter_y, 'b') fig.show() phase_time = Timer.stop('Phase') print("Plotting phase for dimensions {}, {} done -- Time Spent: {}". format(x_var, y_var, phase_time))
def eval_3diou(dt_floor_coor, dt_ceil_coor, gt_floor_coor, gt_ceil_coor, ch=-1.6, coorW=1024, coorH=512, floorW=1024, floorH=512): ''' Evaluate 3D IoU using halfspace intersection ''' dt_floor_coor = np.array(dt_floor_coor) dt_ceil_coor = np.array(dt_ceil_coor) gt_floor_coor = np.array(gt_floor_coor) gt_ceil_coor = np.array(gt_ceil_coor) assert (dt_floor_coor[:, 0] != dt_ceil_coor[:, 0]).sum() == 0 assert (gt_floor_coor[:, 0] != gt_ceil_coor[:, 0]).sum() == 0 N = len(dt_floor_coor) dt_floor_xyz = np.hstack([ post_proc.np_coor2xy(dt_floor_coor, ch, coorW, coorH, floorW=1, floorH=1), np.zeros((N, 1)) + ch, ]) gt_floor_xyz = np.hstack([ post_proc.np_coor2xy(gt_floor_coor, ch, coorW, coorH, floorW=1, floorH=1), np.zeros((N, 1)) + ch, ]) dt_c = np.sqrt((dt_floor_xyz[:, :2] ** 2).sum(1)) gt_c = np.sqrt((gt_floor_xyz[:, :2] ** 2).sum(1)) dt_v2 = post_proc.np_coory2v(dt_ceil_coor[:, 1], coorH) gt_v2 = post_proc.np_coory2v(gt_ceil_coor[:, 1], coorH) dt_ceil_z = dt_c * np.tan(dt_v2) gt_ceil_z = gt_c * np.tan(gt_v2) dt_ceil_xyz = dt_floor_xyz.copy() dt_ceil_xyz[:, 2] = dt_ceil_z gt_ceil_xyz = gt_floor_xyz.copy() gt_ceil_xyz[:, 2] = gt_ceil_z dt_halfspaces = xyzlst2halfspaces(dt_floor_xyz, dt_ceil_xyz) gt_halfspaces = xyzlst2halfspaces(gt_floor_xyz, gt_ceil_xyz) in_halfspaces = HalfspaceIntersection(np.concatenate([dt_halfspaces, gt_halfspaces]), np.zeros(3)) dt_halfspaces = HalfspaceIntersection(dt_halfspaces, np.zeros(3)) gt_halfspaces = HalfspaceIntersection(gt_halfspaces, np.zeros(3)) in_volume = ConvexHull(in_halfspaces.intersections).volume dt_volume = ConvexHull(dt_halfspaces.intersections).volume gt_volume = ConvexHull(gt_halfspaces.intersections).volume un_volume = dt_volume + gt_volume - in_volume return 100 * in_volume / un_volume
def vertices(self): """ Returns the set of vertices of the polyhdron. It assumes the polyhedron to be bounded (i.e. to be a polytope) and full dimensional (equality constraints are allowed but inequalities cannot make the polytope lower dimensional). Returns ---------- vertices : list of numpy.ndarray List of the vertices of the bounded polyhedron (None if the polyhedron is unbounded or empty). """ # check if it has been already checked if self._vertices is not None: return self._vertices # check boundedness if not self.bounded: return None # check full dimensionality tol = 1.e-7 if self.radius < tol: return None # handle equalities if self.C.shape[0] > 0: A, b, N, R = self._remove_equalities() T = np.hstack((N, R)) center = np.linalg.inv(T).dot(self.center) center = center[:N.shape[1]] else: A = self.A b = self.b center = self.center # handle 1d cases if A.shape[1] == 1: p = Polyhedron(A, b) p.remove_redundant_inequalities() self._vertices = [np.array([p.b[i] / p.A[i, 0]]) for i in [0, 1]] # call qhull through scipy else: halfspaces = np.column_stack((A, -b)) polyhedron = HalfspaceIntersection(halfspaces, center) V = polyhedron.intersections self._vertices = [V[i] for i in range(V.shape[0])] # go back to the original coordinates in case of equalities if self.C.shape[0] > 0: r = np.linalg.inv(self.C.dot(R)).dot(self.d) self._vertices = [ T.dot(np.concatenate((v, r))) for v in self._vertices ] return self._vertices
def _intersect(hulls, feas_point=None): all_eq = np.vstack([h.equations for h in hulls]) if feas_point is None: #points = np.vstack([h.points[h.vertices, :] for h in hulls]) #feas_point = np.sum(points, axis=0)/points.shape[0] feas_point = feasible_point(all_eq) intersected = ConvexHull(HalfspaceIntersection(all_eq, feas_point).intersections) return intersected
def find_direction(self, poly, plot=False): self.build_polys(poly) volumes = [] ineq = poly.inner.equations for line in ineq: key = hashlib.sha1(line).hexdigest() if key in poly.hull_dic: volumes.append(poly.hull_dic[key].volume) else: #TODO: How to compute outer initial vrep ? # We use CDD for now, but is there any way to get # initial feasible point ? #A_e = cdd.Matrix(np.hstack((-poly.outer.equations[:, -1:], # -poly.outer.equations[:, :-1]))) #A_e.rep_type = cdd.RepType.INEQUALITY #m = np.hstack((line[-1:], line[:-1])) #A_e.extend(cdd.Matrix(m.reshape((1, m.size)))) #points = np.array(cdd.Polyhedron(A_e).get_generators())[:, 1:] A_e = np.vstack((poly.outer.halfspaces, -line)) c = np.zeros((A_e.shape[1], )) c[-1] = -1 res = linprog(c, A_ub=np.hstack( (A_e[:, :-1], np.ones((A_e.shape[0], 1)))), b_ub=-A_e[:, -1:], bounds=(None, None)) if res.success: feasible_point = res.x[:-1] try: poly.hrep_dic[key] = HalfspaceIntersection( A_e, feasible_point, incremental=True) poly.hull_dic[key] = ConvexHull( poly.hrep_dic[key].intersections) volumes.append(poly.hull_dic[key].volume) except QhullError: volumes.append(0.) else: print("Error : {}".format(res.message)) volumes.append(0.) if plot and res.success: poly.reset_fig() poly.plot_polyhedrons() poly.plot_polyhedron(poly.hull_dic[key], 'm', 0.5) poly.show() maxv = max(volumes) alli = [i for i, v in enumerate(volumes) if v == maxv] i = random.choice(alli) return ineq[i, :-1]
def test_halfspace_convpg_noIntersection(self): hs = HalfSpace(Vector3D(0, 4, 0), Vector3D(0, 1, 0)) hsi = HalfspaceIntersection( np.array([[1, 0, -3], [0, 1, -3], [-1, 0, 0], [0, -1, 0]]), np.array([1.0, 1.0])) cp = ConvexPolygon3D(hsi=hsi, origin=Vector3D(0, 0, 0), rot=Vector3D(0, 0, 0)) it = intersect(hs, cp) self.assertTrue(isinstance(it, Empty))
def visualize(program, steps): variable_names_to_idx = {v : i for i,v in enumerate(program.variables.keys())} A_ub = np.array(generate_A(program, variable_names_to_idx)) b_ub = np.array(generate_b(program)) res = linprog(method='interior-point', c=np.zeros(A_ub.shape[1]), A_ub=A_ub, b_ub=b_ub) feasible_point = res.x b_ub = np.expand_dims(-1*b_ub, 1) halfspaces = np.hstack((A_ub, b_ub)) N = len(program.variables) shape = (N, N + 1) v_mat = np.zeros(shape) for idx in range(N): v_mat[idx, idx] = -1 halfspaces = np.vstack((halfspaces, v_mat)) hs = HalfspaceIntersection(halfspaces, feasible_point) points = np.around(hs.intersections, 3) points = points[:, :2] xlim = (-1, max([p[0] for p in points]) + 1) ylim = (-1, max([p[1] for p in points]) + 1) steps = np.array(steps) steps = steps[:, :2] figs = [] for i, (p,q) in enumerate(steps): fig = plt.figure() ax = fig.add_subplot('111', aspect='equal') ax.set_xlim(xlim) ax.set_ylim(ylim) x = np.linspace(*xlim, 100) x, y = zip(*points) # points = list(zip(x, y)) convex_hull = ConvexHull(points) polygon = Polygon([points[v] for v in convex_hull.vertices], color="#34495e") ax.add_patch(polygon) ax.plot(x, y, 'o', color="#e67e22") ax.plot(p, q, 'o', markersize=15) byarray = io.BytesIO() fig.savefig(byarray, format='png', bbox_inches="tight") b64 = base64.b64encode(byarray.getvalue()).decode("utf-8").replace("\n", "") b64 = 'data:image/png;base64,%s' % b64 figs.append(b64) return figs
def find_point_volume_direction(self, poly, point): normals, offset = poly.inner.equations[:, : -1], poly.inner.equations[:, -1] outside = (normals.dot(point) + offset) > 0 if np.sum(outside) == 0: raise ValueError("The point is inside!") else: volumes = {} for i in np.argwhere(outside): index = i.item(0) line = poly.inner.equations[i, :] key = hashlib.sha1(line).hexdigest() if key in poly.hull_dic: volumes[index] = poly.hull_dic[key].volume else: #TODO: How to compute outer initial vrep ? # We use CDD for now, but is there any way to get # initial feasible point ? #A_e = cdd.Matrix(np.hstack((-poly.outer.equations[:, -1:], # -poly.outer.equations[:, :-1]))) #A_e.rep_type = cdd.RepType.INEQUALITY #m = np.hstack((line[-1:], line[:-1])) #A_e.extend(cdd.Matrix(m.reshape((1, m.size)))) #points = np.array(cdd.Polyhedron(A_e).get_generators())[:, 1:] A_e = np.vstack((poly.outer.halfspaces, -line)) c = np.zeros((A_e.shape[1], )) c[-1] = -1 res = linprog(c, A_ub=np.hstack( (A_e[:, :-1], np.ones( (A_e.shape[0], 1)))), b_ub=-A_e[:, -1:], bounds=(None, None)) if res.success: feasible_point = res.x[:-1] try: poly.hrep_dic[key] = HalfspaceIntersection( A_e, feasible_point, incremental=True) poly.hull_dic[key] = ConvexHull( poly.hrep_dic[key].intersections) volumes[index] = poly.hull_dic[key].volume except QhullError: volumes[index] = 0. else: print("Error : {}".format(res.message)) volumes[index] = .0 maxv = max(volumes.values()) alli = [i for i, v in volumes.items() if v == maxv] i = random.choice(alli) return normals[i:i + 1, :]
def from_halfspaces(cls, halfspaces, interior_point): """Construct a polyhedron from its half-spaces and one interior point. Parameters ---------- halfspaces : array-like The coefficients of the hgalfspace equations in normal form. interior_point : array-like A point on the interior. Returns ------- :class:`compas.geometry.Polyhedron` Examples -------- >>> from compas.geometry import Plane >>> left = Plane([-1, 0, 0], [-1, 0, 0]) >>> right = Plane([+1, 0, 0], [+1, 0, 0]) >>> top = Plane([0, 0, +1], [0, 0, +1]) >>> bottom = Plane([0, 0, -1], [0, 0, -1]) >>> front = Plane([0, -1, 0], [0, -1, 0]) >>> back = Plane([0, +1, 0], [0, +1, 0]) >>> import numpy as np >>> halfspaces = np.array([left.abcd, right.abcd, top.abcd, bottom.abcd, front.abcd, back.abcd], dtype=float) >>> interior = np.array([0, 0, 0], dtype=float) >>> p = Polyhedron.from_halfspaces(halfspaces, interior) """ from itertools import combinations from numpy import asarray from scipy.spatial import HalfspaceIntersection, ConvexHull from compas.datastructures import Mesh, mesh_merge_faces from compas.geometry import length_vector, dot_vectors, cross_vectors halfspaces = asarray(halfspaces, dtype=float) interior_point = asarray(interior_point, dtype=float) hsi = HalfspaceIntersection(halfspaces, interior_point) hull = ConvexHull(hsi.intersections) mesh = Mesh.from_vertices_and_faces( [hsi.intersections[i] for i in hull.vertices], hull.simplices) mesh.unify_cycles() to_merge = [] for a, b in combinations(mesh.faces(), 2): na = mesh.face_normal(a) nb = mesh.face_normal(b) if dot_vectors(na, nb) >= 1: if length_vector(cross_vectors(na, nb)) < 1e-6: to_merge.append([a, b]) for faces in to_merge: mesh_merge_faces(mesh, faces) vertices, faces = mesh.to_vertices_and_faces() return cls(vertices, faces)
def erode_hsis(original: HalfspaceIntersection, eroder: HalfspaceIntersection): tightest_bs = [] for hs in original.halfspaces: result = linprog(-hs[:-1], eroder.halfspaces[:, :-1], -eroder.halfspaces[:, -1], bounds=(None, None)) assert result.status == 0 tightest_bs.append(hs[-1] - result.fun) eroded_halfspaces = np.column_stack( (original.halfspaces[:, :-1], tightest_bs)) return HalfspaceIntersection(eroded_halfspaces, original.interior_point)
def CHAMP_2D(G, all_parts, gamma_0, gamma_f, single_threaded=False): """Calculates the pruned set of partitions from CHAMP on gamma_0 <= gamma <= gamma_f :param G: graph of interest :param all_parts: partitions to prune :param gamma_0: starting gamma value :param gamma_f: ending gamma value :param single_threaded: if True, run without parallelization :return: list of [(domain_gamma_start, domain_gamma_end, membership), ...] """ # TODO: remove this filter once scipy updates their library # scipy.linprog currently uses deprecated numpy behavior, so we suppress this warning to avoid output clutter warnings.filterwarnings("ignore", category=VisibleDeprecationWarning) if len(all_parts) == 0: return [] all_parts = list(all_parts) num_partitions = len(all_parts) partition_coefficients = partition_coefficients_2D( G, all_parts, single_threaded=single_threaded) A_hats, P_hats = partition_coefficients top = max(A_hats - P_hats * gamma_0) # Could potentially be optimized right = gamma_f # Could potentially use the max intersection x value halfspaces = np.vstack( (halfspaces_from_coefficients_2D(*partition_coefficients), np.array([[0, 1, -top], [1, 0, -right]]))) # Could potentially scale axes so Chebyshev center is better for problem interior_point = get_interior_point(halfspaces) hs = HalfspaceIntersection(halfspaces, interior_point) # scipy does not support facets by halfspace directly, so we must compute them facets_by_halfspace = defaultdict(list) for v, idx in zip(hs.intersections, hs.dual_facets): assert np.isfinite(v).all() for i in idx: if i < num_partitions: facets_by_halfspace[i].append(v) ranges = [] for i, intersections in facets_by_halfspace.items(): x1, x2 = intersections[0][0], intersections[1][0] if x1 > x2: x1, x2 = x2, x1 ranges.append((x1, x2, all_parts[i])) return sorted(ranges, key=lambda x: x[0])
def compute_vertexes(a_mat: types.Matrix, b_vec: types.ColVector, feasible_point: types.ColVector) -> types.Matrix: """ Compute all vertexes of a polyhedron. Constraints have to be expressed as: A*x + b <= 0. :param a_mat: A matrix in A*x + b <= 0. :param b_vec: b vector in A*x + b <= 0. :param feasible_point: Initial feasible point. :return: vertexes: Matrix with rows given by the vertexes of the feasible set. """ halfspaces = np.hstack((a_mat, -b_vec)) halfspaces_intersections = HalfspaceIntersection(halfspaces, feasible_point) vertexes = halfspaces_intersections.intersections return vertexes
def polytope_vertices(A: np.ndarray, b: np.ndarray, interior_pt: np.ndarray = None) -> np.ndarray: """Return the vertices of the halfspace intersection Ax <= b. Equivalently, return the V-representation of some polytope given the H-representation. Provide an interior point to improve computation time. Args: A (np.ndarray): LHS coefficents of the halfspaces. b (np.ndarray): RHS coefficents of the halfspaces. interior_pt (np.ndarray): Interior point of the halfspace intersection. Returns: np.ndarray: Vertices of the halfspace intersection. """ try: if interior_pt is None: interior_pt = interior_point(A, b) interior_pt = interior_pt.astype(float) A_b = np.hstack((A, -b)) vertices = HalfspaceIntersection(A_b, interior_pt).intersections vertices = np.round(np.array(vertices), 12) except NoInteriorPoint: vertices = [] m, n = A.shape for B in itertools.combinations(range(m), n): try: x = np.linalg.solve(A[B, :], b[B, :])[:, 0] if all(np.matmul(A, x) <= b[:, 0] + 1e-12): vertices.append(x) except np.linalg.LinAlgError: pass vertices = np.round(np.array(vertices), 12) vertices = np.unique(vertices, axis=0) return [np.array([v]).transpose() for v in vertices]
def test_halfspace_convpg_intersection(self): hs = HalfSpace(Vector3D(0, 2, 0), Vector3D(0, 1, 0)) hsi = HalfspaceIntersection( np.array([[1, 0, -3], [0, 1, -3], [-1, 0, 0], [0, -1, 0]]), np.array([1.0, 1.0])) cp = ConvexPolygon3D(hsi=hsi, origin=Vector3D(0, 0, 0), rot=Vector3D(0, 0, 0)) it = intersect(hs, cp) int_point_3d: Vector3D = rotate_euler_v3d( Vector3D(*it.hsi.interior_point, 0.0), it.rot) + it.origin self.assertTrue(isinstance(it, ConvexPolygon3D)) self.assertTrue(contains_point(hs, int_point_3d)) self.assertTrue(contains_point(cp, int_point_3d))
def _get_domains(self) -> Dict[str, np.ndarray]: """Returns a dictionary of domains as {formula: np.ndarray}""" hyperplanes = self._hyperplanes border_hyperplanes = self._border_hyperplanes entries = self._hyperplane_entries hs_hyperplanes = np.vstack([hyperplanes, border_hyperplanes]) interior_point = np.min(self.lims, axis=1) + 1e-1 hs_int = HalfspaceIntersection(hs_hyperplanes, interior_point) domains = {entry.composition.reduced_formula: [] for entry in entries} # type: ignore for intersection, facet in zip(hs_int.intersections, hs_int.dual_facets): for v in facet: if v < len(entries): this_entry = entries[v] formula = this_entry.composition.reduced_formula domains[formula].append(intersection) return {k: np.array(v) for k, v in domains.items() if v}
def v_cell(i0: int, tacke: List[List[float]]) -> List[List[float]]: t0 = tacke[i0] half_spaces = [] for i in range(len(tacke)): if i != i0: t = tacke[i] x1 = t[0] x2 = t0[0] y1 = t[1] y2 = t0[1] a = x1 - x2 b = y1 - y2 c = (-x1 * x1 + x2 * x2 - y1 * y1 + y2 * y2) / 2 #if(x1*a + y1*b + c > 0): bis_k = [a, b, c] half_spaces.append(bis_k) #print(np.array(half_spaces,dtype = float)) hs = HalfspaceIntersection(np.array(half_spaces), np.array(t0)) ch = ConvexHull(hs.intersections) return Polygon([list(ch.points[i]) for i in ch.vertices])
def alt_reduce(self, contingency): self = MT.grid from scipy.spatial import HalfspaceIntersection A = [] b = [] for ptdf in contingency: ram_array = self.update_ram(ptdf, option="array") ram_pos = ram_array[:, 0] ram_neg = ram_array[:, 1] tmp_A = np.concatenate((-ptdf, ptdf), axis=0) tmp_b = np.concatenate((-ram_pos, ram_neg), axis=0) A += tmp_A.tolist() b += tmp_b.tolist() A = np.array(A) b = np.array(b).reshape(len(b), 1) # len(self.nodes) Ab = np.concatenate((A, b), axis=1) test = HalfspaceIntersection(Ab, np.zeros(len(self.nodes)), qhull_options="QJ") # dir(test) nr = test.dual_vertices return(Ab, nr)
def get_pourbaix_domains(pourbaix_entries, limits=None): """ Returns a set of pourbaix stable domains (i. e. polygons) in pH-V space from a list of pourbaix_entries This function works by using scipy's HalfspaceIntersection function to construct all of the 2-D polygons that form the boundaries of the planes corresponding to individual entry gibbs free energies as a function of pH and V. Hyperplanes of the form a*pH + b*V + 1 - g(0, 0) are constructed and supplied to HalfspaceIntersection, which then finds the boundaries of each pourbaix region using the intersection points. Args: pourbaix_entries ([PourbaixEntry]): Pourbaix entries with which to construct stable pourbaix domains limits ([[float]]): limits in which to do the pourbaix analysis Returns: Returns a dict of the form {entry: [boundary_points]}. The list of boundary points are the sides of the N-1 dim polytope bounding the allowable ph-V range of each entry. """ if limits is None: limits = [[-2, 16], [-4, 4]] # Get hyperplanes hyperplanes = [ np.array([-PREFAC * entry.npH, -entry.nPhi, 0, -entry.energy]) * entry.normalization_factor for entry in pourbaix_entries ] hyperplanes = np.array(hyperplanes) hyperplanes[:, 2] = 1 max_contribs = np.max(np.abs(hyperplanes), axis=0) g_max = np.dot(-max_contribs, [limits[0][1], limits[1][1], 0, 1]) # Add border hyperplanes and generate HalfspaceIntersection border_hyperplanes = [ [-1, 0, 0, limits[0][0]], [1, 0, 0, -limits[0][1]], [0, -1, 0, limits[1][0]], [0, 1, 0, -limits[1][1]], [0, 0, -1, 2 * g_max], ] hs_hyperplanes = np.vstack([hyperplanes, border_hyperplanes]) interior_point = np.average(limits, axis=1).tolist() + [g_max] hs_int = HalfspaceIntersection(hs_hyperplanes, np.array(interior_point)) # organize the boundary points by entry pourbaix_domains = {entry: [] for entry in pourbaix_entries} for intersection, facet in zip(hs_int.intersections, hs_int.dual_facets): for v in facet: if v < len(pourbaix_entries): this_entry = pourbaix_entries[v] pourbaix_domains[this_entry].append(intersection) # Remove entries with no pourbaix region pourbaix_domains = {k: v for k, v in pourbaix_domains.items() if v} pourbaix_domain_vertices = {} for entry, points in pourbaix_domains.items(): points = np.array(points)[:, :2] # Initial sort to ensure consistency points = points[np.lexsort(np.transpose(points))] center = np.average(points, axis=0) points_centered = points - center # Sort points by cross product of centered points, # isn't strictly necessary but useful for plotting tools points_centered = sorted( points_centered, key=cmp_to_key(lambda x, y: x[0] * y[1] - x[1] * y[0])) points = points_centered + center # Create simplices corresponding to pourbaix boundary simplices = [ Simplex(points[indices]) for indices in ConvexHull(points).simplices ] pourbaix_domains[entry] = simplices pourbaix_domain_vertices[entry] = points return pourbaix_domains, pourbaix_domain_vertices
def get_intersection(coef_array, max_pt=None): ''' Calculate the intersection of the halfspaces (planes) that form the convex hull :param coef_array: NxM array of M coefficients across each row representing N partitions :type coef_array: array :param max_pt: Upper bound for the domains (in the xy plane). This will restrict the convex hull \ to be within the specified range of gamma/omega (such as the range of parameters originally searched using Louvain). :type max_pt: (float,float) or float :return: dictionary mapping the index of the elements in the convex hull to the points defining the boundary of the domain ''' halfspaces = create_halfspaces_from_array(coef_array) num_input_halfspaces = len(halfspaces) singlelayer = False if halfspaces.shape[1] - 1 == 2: # 2D case, halfspaces.shape is (number of halfspaces, dimension+1) singlelayer = True # Create Boundary Halfspaces - These will always be included in the convex hull # and need to be removed before returning dictionary boundary_halfspaces = [] if not singlelayer: # origin boundaries boundary_halfspaces.extend([np.array([0, -1.0, 0, 0]), np.array([-1.0, 0, 0, 0])]) if max_pt is not None: boundary_halfspaces.extend([np.array([0, 1.0, 0, -1.0 * max_pt[0]]), np.array([1.0, 0, 0, -1.0 * max_pt[1]])]) else: boundary_halfspaces.extend([np.array([-1.0, 0, 0]), # y-axis np.array([0, -1.0, 0])]) # x-axis if max_pt is not None: boundary_halfspaces.append(np.array([1.0, 0, -1.0 * max_pt])) # We expect infinite vertices in the halfspace intersection, so we can ignore numpy's floating point warnings old_settings = np.seterr(divide='ignore', invalid='ignore') halfspaces = np.vstack((halfspaces, ) + tuple(boundary_halfspaces)) if max_pt is None: if not singlelayer: # in this case, we will calculate max boundary planes later, so we'll impose x, y <= 10.0 # for the interior point calculation here. interior_pt = get_interior_point(np.vstack((halfspaces,) + (np.array([0, 1.0, 0, -10.0]), np.array([1.0, 0, 0, -10.0])))) else: # similarly, in the 2D case, we impose x <= 10.0 for the interior point calculation interior_pt = get_interior_point(np.vstack((halfspaces,) + (np.array([1.0, 0, -10.0]),))) else: interior_pt = get_interior_point(halfspaces) # Find boundary intersection of half spaces joggled = False try: hs_inter = HalfspaceIntersection(halfspaces, interior_pt) except QhullError: warnings.warn("Qhull input might be sub-dimensional, attempting to fix...", RuntimeWarning) # move the offset of the the first two boundary halfspaces (x >= 0 and y >= 0) so that # the joggled intersections are not outside our boundaries. joggled = True halfspaces[num_input_halfspaces][-1] = -1e-5 halfspaces[num_input_halfspaces + 1][-1] = -1e-5 hs_inter = HalfspaceIntersection(halfspaces, interior_pt, qhull_options="QJ") non_inf_vert = np.array([v for v in hs_inter.intersections if np.isfinite(v).all()]) mx = np.max(non_inf_vert, axis=0) if joggled: # find largest (x,y) values of halfspace intersections and refuse to continue if too close to (0,0) max_xy_intersections = mx[:2] if max(max_xy_intersections) < 1e-2: raise ValueError("All intersections are less than ({:.3f},{:.3f}). " "Invalid input set, try setting max_pt.".format(*max_xy_intersections)) # max intersection on y-axis (x=0) implies there are no intersections in gamma direction. if np.abs(mx[0]) < np.power(10.0, -15) and np.abs(mx[1]) < np.power(10.0, -15): raise ValueError("Max intersection detected at (0,0). Invalid input set.") if np.abs(mx[1]) < np.power(10.0, -15): mx[1] = mx[0] if np.abs(mx[0]) < np.power(10.0, -15): mx[0] = mx[1] # At this point we include max boundary planes and recalculate the intersection # to correct inf points. We only do this for single layer if max_pt is None: if not singlelayer: boundary_halfspaces.extend([np.array([0, 1.0, 0, -1.0 * mx[1]]), np.array([1.0, 0, 0, -1.0 * mx[0]])]) halfspaces = np.vstack((halfspaces, ) + tuple(boundary_halfspaces[-2:])) if not singlelayer: # Find boundary intersection of half spaces interior_pt = get_interior_point(halfspaces) hs_inter = HalfspaceIntersection(halfspaces, interior_pt) # revert numpy floating point warnings np.seterr(**old_settings) # scipy does not support facets by halfspace directly, so we must compute them facets_by_halfspace = defaultdict(list) for v, idx in zip(hs_inter.intersections, hs_inter.dual_facets): if np.isfinite(v).all(): for i in idx: facets_by_halfspace[i].append(v) ind_2_domain = {} dimension = 2 if singlelayer else 3 for i, vlist in facets_by_halfspace.items(): # Empty domains if len(vlist) == 0: continue # these are the boundary planes appended on end if not i < num_input_halfspaces: continue pts = sort_points(vlist) pt2rm = [] for j in range(len(pts) - 1): if comp_points(pts[j], pts[j + 1]): pt2rm.append(j) pt2rm.reverse() for j in pt2rm: pts.pop(j) if len(pts) >= dimension: # must be at least 2 pts in 2D, 3 pt in 3D, etc. ind_2_domain[i] = pts # use non-inf vertices to return return ind_2_domain
from compas_view2.app import App left = Plane([-1, 0, 0], [-1, 0, 0]) right = Plane([+1, 0, 0], [+1, 0, 0]) top = Plane([0, 0, +1], [0, 0, +1]) bottom = Plane([0, 0, -1], [0, 0, -1]) front = Plane([0, -1, 0], [0, -1, 0]) back = Plane([0, +1, 0], [0, +1, 0]) halfspaces = array( [left.abcd, right.abcd, top.abcd, bottom.abcd, front.abcd, back.abcd], dtype=float) interior = array([0, 0, 0], dtype=float) hsi = HalfspaceIntersection(halfspaces, interior) hull = ConvexHull(hsi.intersections) mesh = Mesh.from_vertices_and_faces( [hsi.intersections[i] for i in hull.vertices], hull.simplices) mesh.unify_cycles() to_merge = [] for a, b in combinations(mesh.faces(), 2): na = Vector(*mesh.face_normal(a)) nb = Vector(*mesh.face_normal(b)) if na.dot(nb) >= 1: if na.cross(nb).length < 1e-6: to_merge.append([a, b]) for faces in to_merge: