def region_rotation_test(self): p = pc.Region([pc.Polytope(self.A, self.b)]) p1 = pc.Region([pc.Polytope(self.A, self.b)]) p2 = pc.Region([pc.Polytope(self.Ab2[:, 0:2], self.Ab2[:, 2])]) p3 = pc.Region([pc.Polytope(self.Ab3[:, 0:2], self.Ab3[:, 2])]) p4 = pc.Region([pc.Polytope(self.Ab4[:, 0:2], self.Ab4[:, 2])]) p = p.rotation(0, 1, np.pi / 2) print(p.bounding_box) assert (p == p2) assert (not p == p3) assert (not p == p4) assert (not p == p1) assert_allclose(p.chebXc, [-0.5, 0.5]) p = p.rotation(0, 1, np.pi / 2) assert (p == p3) assert_allclose(p.chebXc, [-0.5, -0.5]) p = p.rotation(0, 1, np.pi / 2) assert (p == p4) assert_allclose(p.chebXc, [0.5, -0.5]) p = p.rotation(0, 1, np.pi / 2) assert (p == p1) assert_allclose(p.chebXc, [0.5, 0.5])
def comparison_test(self): p = pc.Polytope(self.A, self.b) p2 = pc.Polytope(self.A, 2*self.b) assert(p <= p2) assert(not p2 <= p) assert(not p2 == p) r = pc.Region([p]) r2 = pc.Region([p2]) assert(r <= r2) assert(not r2 <= r) assert(not r2 == r) # test H-rep -> V-rep -> H-rep v = pc.extreme(p) p3 = pc.qhull(v) assert(p3 == p) # test V-rep -> H-rep with d+1 points p4 = pc.qhull(np.array([[0, 0], [1, 0], [0, 1]])) assert(p4 == pc.Polytope( np.array([[1, 1], [0, -1], [0, -1]]), np.array([1, 0, 0])))
def region_translation_test(self): p = pc.Region([pc.Polytope(self.A, self.b)]) p1 = pc.Region([pc.Polytope(self.A, self.b)]) p2 = pc.Region([pc.Polytope(self.Ab2[:, 0:2], self.Ab2[:, 2])]) p = p.translation([-1, 0]) assert (p == p2) assert (not p == p1) p = p.translation([1, 0]) assert (p == p1)
def region_full_dim_test(self): assert not pc.is_fulldim(pc.Region()) p1 = pc.Polytope(self.A, self.b) p2 = pc.Polytope(self.Ab2[:, 0:2], self.Ab2[:, 2]) reg = pc.Region([p1, p2]) assert pc.is_fulldim(reg) # Adding empty polytopes should not affect the # full-dimensional status of this region. reg.list_poly.append(pc.Polytope()) assert pc.is_fulldim(reg) reg.list_poly.append(pc.Polytope(self.A, self.b - 1e3)) assert pc.is_fulldim(reg)
def erode(poly, eps): """ Given a polytope compute eps erosion and give polytope under approximation For a given polytope a polytopic under approximation of the $eps$-eroded set is computed. An e-eroded Pe set of P is defined as: Pe = {x |x+n in P forall n in Ball(e)} where Ball(e) is the epsilon neighborhood with norm |n|<e The current implementation shifts hyper-planes with eps over there normal, / / / / / / / | + | / / / / / / /| eps | / / / / / / /| + | / / / / / / /| eps | / / / / / / /| + | :param poly: original polytope :param eps: positive scalar value with which the polytope is eroded :return: polytope """ if isinstance(poly, polytope.Region): er_reg = [] for pol in poly.list_poly: assert isinstance(pol, polytope.Polytope) er_reg += [erode(pol, eps)] return polytope.Region(er_reg) A = poly.A b = poly.b b_e = [] for A_i, b_i in itertools.product(A, b): b_e += [[b_i - eps * np.linalg.norm(A_i, 2)]] return polytope.Polytope(A, np.array(b_e))
def part2convex(ppp): """This function takes a proposition preserving partition and generates another proposition preserving partition such that each part in the new partition is a convex polytope @type ppp: L{PropPreservingPartition} @return: refinement into convex polytopes and map from new to old Regions @rtype: (L{PropPreservingPartition}, list) """ cvxpart = PropPreservingPartition(domain=copy.deepcopy(ppp.domain), prop_regions=copy.deepcopy( ppp.prop_regions)) new2old = [] for i in xrange(len(ppp.regions)): simplified_reg = pc.union(ppp.regions[i], ppp.regions[i], check_convex=True) for j in xrange(len(simplified_reg)): region_now = pc.Region([simplified_reg[j]], ppp.regions[i].props) cvxpart.regions.append(region_now) new2old += [i] cvxpart.adj = pc.find_adjacent_regions(cvxpart).copy() return (cvxpart, new2old)
def setUp(self): self.A1 = np.eye(2) self.A2 = np.array([[0, 1], [0, 0]]) self.B1 = np.array([[0] ,[1]]) self.B2 = np.array([[1], [0]]) self.poly1 = pc.Polytope.from_box([[0, 1], [0, 1]]) self.poly2 = pc.Polytope.from_box([[1, 2], [0, 1]]) self.total_box = pc.Region(list_poly=[self.poly1, self.poly2]) self.Uset = pc.Polytope.from_box([[0, 1]]) self.env_labels = ('hi', 'hello') self.sys_labels = ('mode1',) self.disc_domain_size = (2, 1) self.LTI1 = hybrid.LtiSysDyn(A=self.A1, B=self.B1, Uset=self.Uset, domain=self.poly1, time_semantics='sampled', timestep=.1) self.LTI2 = hybrid.LtiSysDyn(A=self.A2, B=self.B2, Uset=self.Uset, domain=self.poly2, time_semantics='sampled', timestep=.1) self.LTI3 = hybrid.LtiSysDyn(A=self.A1, B=self.B1, Uset=self.Uset, domain=self.poly2, time_semantics='sampled', timestep=.1) self.LTI4 = hybrid.LtiSysDyn(A=self.A2, B=self.B2, Uset=self.Uset, domain=self.poly1, time_semantics='sampled', timestep=.1) self.PWA1 = hybrid.PwaSysDyn(list_subsys=[self.LTI1, self.LTI2], domain=self.total_box, time_semantics='sampled', timestep=.1) self.PWA2 = hybrid.PwaSysDyn(list_subsys=[self.LTI3, self.LTI4], domain=self.total_box, time_semantics='sampled', timestep=.1) self.dynamics1 = {(self.env_labels[0], self.sys_labels[0]): self.PWA1, (self.env_labels[1], self.sys_labels[0]): self.PWA2}
def _solve_closed_loop_bounded_horizon( P1, P2, ssys, N, trans_set=None): """Under-approximate states in P1 that can reach P2 in <= N steps. See docstring of function `_solve_closed_loop_fixed_horizon` for details. """ _print_horizon_warning() p1 = P1.copy() # initial set p2 = P2.copy() # terminal set if trans_set is None: pinit = p1 else: pinit = trans_set # backwards in time s = pc.Region() for i in xrange(N, 0, -1): # first step from P1 if i == 1: pinit = p1 p2 = solve_open_loop(pinit, p2, ssys, 1, trans_set) p2 = pc.reduce(p2) # running union s = s.union(p2, check_convex=True) s = pc.reduce(s) # empty target polytope ? if not pc.is_fulldim(p2): break if not pc.is_fulldim(s): return pc.Polytope() s = pc.reduce(s) return s
def region_empty_test(self): # Note that as of commit a037b555758ed9ee736fa7cb324d300b8d622fb4 # Region.__init__ deletes empty polytopes from # the given list of polytopes at instantiation. reg = pc.Region() reg.list_poly = [pc.Polytope(), pc.Polytope()] assert len(reg) > 0 assert pc.is_empty(reg)
def setUp(self): self.A1 = np.eye(2) self.A2 = np.array([[0, 1], [0, 0]]) self.B1 = np.array([[0] ,[1]]) self.B2 = np.array([[1], [0]]) self.poly1 = pc.Polytope.from_box([[0, 1], [0, 1]]) self.poly2 = pc.Polytope.from_box([[1, 2], [0, 1]]) self.total_box = pc.Region(list_poly=[self.poly1, self.poly2]) self.Uset = pc.Polytope.from_box([[0, 1]])
def shrinkRegion(origRegion, epsilon): """Returns a region where all polytopes in the Region origRegion have been shrunk a distance 'epsilon'""" list_poly = copy.deepcopy(origRegion.list_poly) props = copy.deepcopy(origRegion.props) lengthie = len(list_poly) for i in range(lengthie): list_poly[i] = shrinkPoly(list_poly[i], epsilon) return pc.Region(list_poly=list_poly, props=props)
def _import_region(node): # Get the polytope list and import the polytopes polytope_list = node.findall(N_POLYLIST)[0] # Import the polytopes list_poly = _import_xml(polytope_list) return polytope.Region(list_poly=list_poly)
def is_inside_test(self): box = [[0.0, 1.0], [0.0, 2.0]] p = pc.Polytope.from_box(box) point = np.array([0.0, 1.0]) abs_tol = 0.01 assert pc.is_inside(p, point) assert pc.is_inside(p, point, abs_tol) region = pc.Region([p]) assert pc.is_inside(region, point) assert pc.is_inside(region, point, abs_tol) point = np.array([2.0, 0.0]) assert not pc.is_inside(p, point) assert not pc.is_inside(p, point, abs_tol) region = pc.Region([p]) assert not pc.is_inside(region, point) assert not pc.is_inside(region, point, abs_tol) abs_tol = 1.2 assert pc.is_inside(p, point, abs_tol) assert pc.is_inside(region, point, abs_tol)
def unit_polytope(self, i): try: return self._unit_polytope[i] except KeyError: r = self.unit_region[i] if isinstance(r, pt.Region): pass else: if isinstance(r, (tuple, list)): r = pt.box2poly(list(zip(*r))) self._unit_polytope[i] = r r = pt.Region([r]) return r
def prop2part_test(): state_space = pc.Polytope.from_box(np.array([[0., 2.],[0., 2.]])) cont_props = [] A = [] b = [] A.append(np.array([[1., 0.], [-1., 0.], [0., 1.], [0., -1.]])) b.append(np.array([[.5, 0., .5, 0.]]).T) cont_props.append(pc.Polytope(A[0], b[0])) A.append(np.array([[1., 0.], [-1., 0.], [0., 1.], [0., -1.]])) b.append(np.array([[2., -1.5, 2., -1.5]]).T) cont_props.append(pc.Polytope(A[1], b[1])) cont_props_dict = {"C"+str(i) : pc.Polytope(A[i], b[i]) for i in range(2)} mypartition = prop2part(state_space, cont_props_dict) print(mypartition) ref_adjacency = np.array([[1,0,1],[0,1,1],[1,1,1]]) assert np.all(mypartition.adj.todense() == ref_adjacency) assert len(mypartition.regions) == 3 for reg in mypartition.regions[0:2]: assert len(reg.props) == 1 assert len(reg) == 1 assert cont_props_dict == mypartition.prop_regions assert len(mypartition.regions[2].props) == 0 assert len(mypartition.regions[2]) == 3 dum = state_space.copy() for reg in mypartition.regions[0:2]: dum = dum.diff(reg) assert pc.is_empty(dum.diff(mypartition.regions[2]) ) assert pc.is_empty(mypartition.regions[2].diff(dum) ) assert(mypartition.preserves_predicates()) # invalidate it mypartition.regions += [pc.Region([pc.Polytope(A[0], b[0])], {})] assert(not mypartition.preserves_predicates())
def is_cover(self): """Return True if Regions cover domain """ union = pc.Region() for region in self.regions: union += region if not self.domain <= union: msg = 'partition does not cover domain.' logger.Error(msg) warnings.warn(msg) return False else: return True
def volumes_for_reachability(part, max_num_poly): if len(part) <= max_num_poly: return part vol_list = np.zeros(len(part) ) for i in xrange(len(part) ): vol_list[i] = part[i].volume ind = np.argsort(-vol_list) temp = [] for i in ind[range(max_num_poly) ]: temp.append(part[i] ) part = pc.Region(temp, []) return part
def region_contains_test(self): A = np.array([[1.0], [-1.0]]) b = np.array([1.0, 0.0]) poly = pc.Polytope(A, b) polys = [poly] reg = pc.Region(polys) assert 0.5 in reg # small positive tolerance (includes boundary) points = np.array([[-1.0, 0.0, 0.5, 1.0, 2.0]]) c = reg.contains(points) c_ = np.array([[False, True, True, True, False]], dtype=bool) # zero tolerance (excludes boundary) points = np.array([[-1.0, 0.0, 0.5, 1.0, 2.0]]) c = reg.contains(points, abs_tol=0) c_ = np.array([[False, False, True, False, False]], dtype=bool) assert np.all(c == c_), c
def get_bounded_region(self, in_c, in_b): coefficients = numpy.zeros( [len(self.domain.real_vars) * 2 + 1, len(self.domain.real_vars)]) b = numpy.zeros(len(self.domain.real_vars) * 2 + 1) for i in range(len(self.domain.real_vars)): coefficients[2 * i, i] = -1 coefficients[2 * i + 1, i] = 1 lb, ub = self.domain.var_domains[self.domain.real_vars[i]] b[2 * i] = -lb b[2 * i + 1] = ub coefficients[-1, :] = in_c b[-1] = in_b return pc.Region([pc.Polytope(coefficients, b)])
def dilate(poly, eps): """ The function dilates a polytope. For a given polytope a polytopic over apoproximation of the $eps$-dilated set is computed. An e-dilated Pe set of P is defined as: Pe = {x+n|x in P ^ n in Ball(e)} where Ball(e) is the epsilon neighborhood with norm |n|<e The current implementation is quite crude, hyper-boxes are placed over the original vertices and the returned polytope is a qhull of these new vertices. :param poly: original polytope :param eps: positive scalar value with which the polytope is dilated :return: polytope """ if isinstance(poly, polytope.Region): dil_reg = [] for pol in poly.list_poly: assert isinstance(pol, polytope.Polytope) dil_reg += [dilate(pol, eps)] return polytope.Region(dil_reg) vertices = extreme(poly) dim = len(vertices[0]) # this is the dimensionality of the space dil_eps = dim * [[-eps, eps]] dil_eps_v = [np.array(n) for n in itertools.product(*dil_eps) ] # vectors with (+- eps,+- eps, +- eps,...) new_vertices = [] for v, d in itertools.product(vertices, dil_eps_v): new_vertices += [[np.array(v).flatten() + np.array(d).flatten()]] # make box # print("add vertices part:", np.array(v).flatten() + np.array(d).flatten()) VV = np.concatenate(new_vertices) # print("V", VV) return qhull(VV)
def solve_closed_loop( P1, P2, ssys, N, use_all_horizon=False, trans_set=None ): """Compute S0 \subseteq P1 from which P2 is closed-loop N-reachable. @type P1: C{Polytope} or C{Region} @type P2: C{Polytope} or C{Region} @param ssys: system dynamics @param N: horizon length @type N: int > 0 @param use_all_horizon: - if True, then take union of S0 sets - Otherwise, chain S0 sets (funnel-like) @type use_all_horizon: bool @param trans_set: If provided, then intermediate steps are allowed to be in trans_set. Otherwise, P1 is used. """ if use_all_horizon: raise ValueError('solve_closed_loop() with use_all_horizon=True ' 'is still under development\nand currently ' 'unavailable.') p1 = P1.copy() # Initial set p2 = P2.copy() # Terminal set if trans_set is not None: Pinit = trans_set else: Pinit = p1 # backwards in time s0 = pc.Region() reached = False for i in xrange(N, 0, -1): # first step from P1 if i == 1: Pinit = p1 p2 = solve_open_loop(Pinit, p2, ssys, 1, trans_set) s0 = s0.union(p2, check_convex=True) s0 = pc.reduce(s0) # empty target polytope ? if not pc.is_fulldim(p2): break old_reached = reached # overlaps initial set ? if p1.intersect(p2): s0 = s0.union(p2, check_convex=True) s0 = pc.reduce(s0) # we went past it -> don't continue if old_reached is True and reached is False: logger.info('stopped intersecting si') #break if reached is True: break if not pc.is_fulldim(s0): return pc.Polytope() s0 = pc.reduce(s0) return s0
def pwa_shrunk_partition(pwa_sys, ppp, eps, abs_tol=1e-5): """This function takes: - a piecewise affine system C{pwa_sys} and - a proposition-preserving partition C{ppp} whose domain is a subset of the domain of C{pwa_sys} - a shrinkage factor C{eps} and returns a *refined* proposition preserving partition where in each region a unique subsystem of pwa_sys is active and pwa_sys domains are shrunk to account for estimation errors. See Also ======== L{pwa_partition} @type pwa_sys: L{hybrid.PwaSysDyn} @type ppp: L{PropPreservingPartition} @return: new partition and associated maps: - new partition C{new_ppp} - map of C{new_ppp.regions} to C{pwa_sys.list_subsys} - map of C{new_ppp.regions} to C{ppp.regions} @rtype: C{(L{PropPreservingPartition}, list, list)} """ new_list = [] subsys_list = [] parents = [] for i, subsys in enumerate(pwa_sys.list_subsys): dom = shrinkPoly(subsys.domain, eps) for j, region in enumerate(ppp.regions): isect = pc.reduce(region.intersect(dom)) if pc.is_fulldim(isect): rc, xc = pc.cheby_ball(isect) if rc < abs_tol: msg = 'One of the regions in the refined PPP is ' msg += 'too small, this may cause numerical problems' warnings.warn(msg) # not Region yet, but Polytope ? if len(isect) == 0: isect = pc.Region([isect]) # label with AP isect.props = region.props.copy() # store new Region new_list.append(isect) # keep track of original Region in ppp.regions parents.append(j) # index of subsystem active within isect subsys_list.append(i) # compute spatial adjacency matrix n = len(new_list) adj = sp.lil_matrix((n, n), dtype=np.int8) for i, ri in enumerate(new_list): pi = parents[i] for j, rj in enumerate(new_list[0:i]): pj = parents[j] if (ppp.adj[pi, pj] == 1) or (pi == pj): # account for shrinkage in adjacency check if pc.is_adjacent(ri, rj, 2 * eps): adj[i, j] = 1 adj[j, i] = 1 adj[i, i] = 1 new_ppp = PropPreservingPartition(domain=ppp.domain, regions=new_list, adj=adj, prop_regions=ppp.prop_regions) return (new_ppp, subsys_list, parents)
def infer_srtesseler_density(cells, volume_weighted=True, rank=0, **kwargs): """ 2D particle density estimation inspired by: SR-Tesseler: a method to segment and quantify localization-based super-resolution microscopy data. Levet, F., Hosy, E., Kechkar, A., Butler, C., Beghin, A., Choquet, C., Sibarita, J.B. Nature Methods 2015; 12 (11); 1065-1071. This (much) simplified implementation borrows from the above reference the same general principle: * a Voronoi tessellation is made out of all particle locations (one location = one Voronoi cell) * for each Voronoi cell (or particle location), the cell may be extended to merge with its neighbors (see argument `rank`) * the local density is estimated classically as the number of points (of merged cells) divided by the total surface area of the merged cells """ points = cells.locations is_densest_tessellation = isinstance( cells.tessellation, Voronoi ) and cells.number_of_cells == len(points) if is_densest_tessellation: tessellation = cells.tessellation partition = cells else: tessellation = Voronoi() # TODO: try 3D # coords = [ col for col in 'xyz' if col in points.columns ] if "z" in points.columns: import warnings warnings.warn("ignoring coordinate 'z'") tessellation.tessellate(points[["x", "y"]]) partition = Partition(points, tessellation) # estimate the density at each point polygons_required = not is_densest_tessellation and volume_weighted indices, surface_areas, polygons = [], [], [] for i in range(tessellation.number_of_cells): hull = convex_hull(partition=partition, cell_index=i) if hull is None: continue surface_area = hull.volume indices.append(i) surface_areas.append(surface_area) if polygons_required: polygons.append( p.Polytope(hull.equations[:, [0, 1]], -hull.equations[:, 2]) ) indices, surface_areas = np.array(indices), np.array(surface_areas) if rank: if 1 < rank: raise NotImplementedError("rank > 1") index_map = np.full(tessellation.number_of_cells, len(indices)) index_map[indices] = indices extended_areas = np.array(surface_areas) # copy ncells = np.ones_like(surface_areas) regions = [[p] for p in polygons] for i in indices: for j in tessellation.neighbours(i): j = index_map[j] try: extended_areas[i] += surface_areas[j] ncells[i] += 1 if polygons_required: regions[i].append(polygons[j]) except IndexError: pass polygons = [p.Region(ps) for ps in regions] local_density = ncells / extended_areas else: local_density = 1.0 / surface_areas local_density = pd.Series(index=indices, data=local_density) # sum the estimates within each spatial bin if is_densest_tessellation: density = local_density else: if isinstance(cells.cell_index, tuple): pt_ids, cell_ids = cells.cell_index indices, densities = [], [] for i in range(cells.number_of_cells): if isinstance(cells.cell_index, tuple): polygons_i = pt_ids[cell_ids == i] if polygons_i.size == 0: indices.append(i) densities.append(0.0) continue assigned = np.zeros(len(points), dtype=bool) assigned[polygons_i] = True else: assigned = cells.cell_index == i if not np.any(assigned): indices.append(i) densities.append(0.0) continue (polygons_i,) = np.nonzero(assigned) average_density = local_density[polygons_i].mean() indices.append(i) densities.append(average_density) density = pd.Series(index=indices, data=densities) return pd.DataFrame(dict(density=density))
def half_space(self): regions = [] for face in self.faces: regions.append(face.half_space) return pt.Region(regions)
def prop2part(state_space, cont_props_dict): """Main function that takes a domain (state_space) and a list of propositions (cont_props), and returns a proposition preserving partition of the state space. See Also ======== L{PropPreservingPartition}, C{polytope.Polytope} @param state_space: problem domain @type state_space: C{polytope.Polytope} @param cont_props_dict: propositions @type cont_props_dict: dict of C{polytope.Polytope} @return: state space quotient partition induced by propositions @rtype: L{PropPreservingPartition} """ first_poly = [] #Initial Region's polytopes first_poly.append(state_space) regions = [pc.Region(first_poly)] for cur_prop in cont_props_dict: cur_prop_poly = cont_props_dict[cur_prop] num_reg = len(regions) prop_holds_reg = [] for i in xrange(num_reg): #i region counter region_now = regions[i].copy() #loop for prop holds prop_holds_reg.append(0) prop_now = regions[i].props.copy() dummy = region_now.intersect(cur_prop_poly) # does cur_prop hold in dummy ? if pc.is_fulldim(dummy): dum_prop = prop_now.copy() dum_prop.add(cur_prop) # is dummy a Polytope ? if len(dummy) == 0: regions[i] = pc.Region([dummy], dum_prop) else: # dummy is a Region dummy.props = dum_prop.copy() regions[i] = dummy.copy() prop_holds_reg[-1] = 1 else: #does not hold in the whole region # (-> no need for the 2nd loop) regions.append(region_now) continue #loop for prop does not hold regions.append(pc.Region([], props=prop_now)) dummy = region_now.diff(cur_prop_poly) if pc.is_fulldim(dummy): dum_prop = prop_now.copy() # is dummy a Polytope ? if len(dummy) == 0: regions[-1] = pc.Region([pc.reduce(dummy)], dum_prop) else: # dummy is a Region dummy.props = dum_prop.copy() regions[-1] = dummy.copy() else: regions.pop() count = 0 for hold_count in xrange(len(prop_holds_reg)): if prop_holds_reg[hold_count] == 0: regions.pop(hold_count - count) count += 1 mypartition = PropPreservingPartition( domain=copy.deepcopy(state_space), regions=regions, prop_regions=copy.deepcopy(cont_props_dict)) mypartition.adj = pc.find_adjacent_regions(mypartition).copy() return mypartition
def add_grid(ppp, grid_size=None, num_grid_pnts=None, abs_tol=1e-10): """ This function takes a proposition preserving partition ppp and the size of the grid or the number of grids, and returns a refined proposition preserving partition with grids. Input: - `ppp`: a L{PropPreservingPartition} object - `grid_size`: the size of the grid, type: float or list of float - `num_grid_pnts`: the number of grids for each dimension, type: integer or list of integer Output: - A L{PropPreservingPartition} object with grids Note: There could be numerical instabilities when the continuous propositions in ppp do not align well with the grid resulting in very small regions. Performace significantly degrades without glpk. """ if (grid_size != None) & (num_grid_pnts != None): raise Exception("add_grid: Only one of the grid size or number of \ grid points parameters is allowed to be given.") if (grid_size == None) & (num_grid_pnts == None): raise Exception("add_grid: At least one of the grid size or number of \ grid points parameters must be given.") dim = len(ppp.domain.A[0]) domain_bb = ppp.domain.bounding_box size_list = list() if grid_size != None: if isinstance(grid_size, list): if len(grid_size) == dim: size_list = grid_size else: raise Exception( "add_grid: grid_size isn't given in a correct format.") elif isinstance(grid_size, float): for i in xrange(dim): size_list.append(grid_size) else: raise Exception("add_grid: " "grid_size isn't given in a correct format.") else: if isinstance(num_grid_pnts, list): if len(num_grid_pnts) == dim: for i in xrange(dim): if isinstance(num_grid_pnts[i], int): grid_size = (float(domain_bb[1][i]) - float(domain_bb[0][i])) / num_grid_pnts[i] size_list.append(grid_size) else: raise Exception( "add_grid: " "num_grid_pnts isn't given in a correct format.") else: raise Exception( "add_grid: " "num_grid_pnts isn't given in a correct format.") elif isinstance(num_grid_pnts, int): for i in xrange(dim): grid_size = (float(domain_bb[1][i]) - float(domain_bb[0][i])) / num_grid_pnts size_list.append(grid_size) else: raise Exception("add_grid: " "num_grid_pnts isn't given in a correct format.") j = 0 list_grid = dict() while j < dim: list_grid[j] = compute_interval(float(domain_bb[0][j]), float(domain_bb[1][j]), size_list[j], abs_tol) if j > 0: if j == 1: re_list = list_grid[j - 1] re_list = product_interval(re_list, list_grid[j]) else: re_list = product_interval(re_list, list_grid[j]) j += 1 new_list = [] parent = [] for i in xrange(len(re_list)): temp_list = list() j = 0 while j < dim * 2: temp_list.append([re_list[i][j], re_list[i][j + 1]]) j = j + 2 for j in xrange(len(ppp.regions)): tmp = pc.box2poly(temp_list) isect = tmp.intersect(ppp.regions[j], abs_tol) #if pc.is_fulldim(isect): rc, xc = pc.cheby_ball(isect) if rc > abs_tol / 2: if rc < abs_tol: print("Warning: " "One of the regions in the refined PPP is too small" ", this may cause numerical problems") if len(isect) == 0: isect = pc.Region([isect], []) isect.props = ppp.regions[j].props.copy() new_list.append(isect) parent.append(j) adj = sp.lil_matrix((len(new_list), len(new_list)), dtype=np.int8) for i in xrange(len(new_list)): adj[i, i] = 1 for j in xrange(i + 1, len(new_list)): if (ppp.adj[parent[i], parent[j]] == 1) or \ (parent[i] == parent[j]): if pc.is_adjacent(new_list[i], new_list[j]): adj[i, j] = 1 adj[j, i] = 1 return PropPreservingPartition(domain=ppp.domain, regions=new_list, adj=adj, prop_regions=ppp.prop_regions)
def pwa_partition(pwa_sys, ppp, abs_tol=1e-5): """This function takes: - a piecewise affine system C{pwa_sys} and - a proposition-preserving partition C{ppp} whose domain is a subset of the domain of C{pwa_sys} and returns a *refined* proposition preserving partition where in each region a unique subsystem of pwa_sys is active. Reference ========= Modified from Petter Nilsson's code implementing merge algorithm in: Nilsson et al. `Temporal Logic Control of Switched Affine Systems with an Application in Fuel Balancing`, ACC 2012. See Also ======== L{discretize} @type pwa_sys: L{hybrid.PwaSysDyn} @type ppp: L{PropPreservingPartition} @return: new partition and associated maps: - new partition C{new_ppp} - map of C{new_ppp.regions} to C{pwa_sys.list_subsys} - map of C{new_ppp.regions} to C{ppp.regions} @rtype: C{(L{PropPreservingPartition}, list, list)} """ if pc.is_fulldim(ppp.domain.diff(pwa_sys.domain)): raise Exception('pwa system is not defined everywhere ' + 'in state space') # for each subsystem's domain, cut it into pieces # each piece is the intersection with # a unique Region in ppp.regions new_list = [] subsys_list = [] parents = [] for i, subsys in enumerate(pwa_sys.list_subsys): for j, region in enumerate(ppp.regions): isect = region.intersect(subsys.domain) if pc.is_fulldim(isect): rc, xc = pc.cheby_ball(isect) if rc < abs_tol: msg = 'One of the regions in the refined PPP is ' msg += 'too small, this may cause numerical problems' warnings.warn(msg) # not Region yet, but Polytope ? if len(isect) == 0: isect = pc.Region([isect]) # label with AP isect.props = region.props.copy() # store new Region new_list.append(isect) # keep track of original Region in ppp.regions parents.append(j) # index of subsystem active within isect subsys_list.append(i) # compute spatial adjacency matrix n = len(new_list) adj = sp.lil_matrix((n, n), dtype=np.int8) for i, ri in enumerate(new_list): pi = parents[i] for j, rj in enumerate(new_list[0:i]): pj = parents[j] if (ppp.adj[pi, pj] == 1) or (pi == pj): if pc.is_adjacent(ri, rj): adj[i, j] = 1 adj[j, i] = 1 adj[i, i] = 1 new_ppp = PropPreservingPartition(domain=ppp.domain, regions=new_list, adj=adj, prop_regions=ppp.prop_regions) return (new_ppp, subsys_list, parents)
def add_collection(self, unit_regions, label=None): # check if already existing if label in self.index: if 1 < len(self.index): raise RuntimeError( 'cannot overwrite a collection if other collections have already been defined' ) self.__reset__() # i0 = len(self.unit_region) self.unit_region += list(unit_regions) # first group overlapping unit regions in the collection current_index = max(self.group.keys()) + 1 if self.group else 0 not_an_index = -1 n = len(unit_regions) assignment = np.full(n, not_an_index, dtype=int) groups = dict() for i in range(n): region_i = unit_regions[i] if isinstance(region_i, pt.Polytope): region_i = pt.Region([region_i]) _min_i = _max_i = None elif isinstance(region_i, pt.Region): _min_i = _max_i = None else: #if isinstance(region_i, (tuple, list)): _min_i, _max_i = region_i region_i = None group_with = set() if assignment[i] == not_an_index: group_index = current_index group = set([i0 + i]) else: group_index = assignment[i] group_with.add(group_index) group = groups[group_index] assert i0 + i in group for j in range(i + 1, n): if i0 + j in group: continue region_j = unit_regions[j] if isinstance(region_j, (pt.Polytope, pt.Region)): if region_i is None: region_i = pt.box2poly(list(zip(_min_i, _max_i))) self._unit_polytope[i0 + i] = region_i i_and_j_are_adjacent = pt.is_adjacent(region_i, region_j) else: #if isinstance(region_j, (tuple, list)): _min_j, _max_j = region_j if _min_i is None: region_j = pt.box2poly(list(zip(_min_j, _max_j))) self._unit_polytope[i0 + j] = region_j i_and_j_are_adjacent = pt.is_adjacent( region_i, region_j) else: i_and_j_are_adjacent = np.all( _min_i <= _max_j) and np.all(_min_j <= _max_i) if i_and_j_are_adjacent: if assignment[j] == not_an_index: group.add(i0 + j) else: other_group_index = assignment[j] group_with.add(other_group_index) group |= groups.pop(other_group_index) if group_with: group_index = min(group_with) else: current_index += 1 groups[group_index] = group group = np.array(list(group)) - i0 # indices in `assignment` assignment[group] = group_index # # merge the new and existing groups together for g in list(groups.keys()): adjacent = set() for h in self.group: g_and_h_are_adjacent = False for i in groups[g]: for j in self.group[h]: if self.adjacent(i, j): g_and_h_are_adjacent = True break if g_and_h_are_adjacent: adjacent.add(h) break if adjacent: h = min(adjacent) for i in adjacent - {h}: self.group[h] |= self.group.pop(i) self.group[h] |= groups.pop(g) assignment[assignment == g] = h if groups: self.group.update(groups) # if label is None: label = '' self.index[label] = assignment # self.reverse_index = np.c_[ np.repeat(np.arange(len(self.index) ), [len(self.index[s]) for s in self.index]), np.concatenate([np.arange(len(self.index[s])) for s in self.index])]