def cut_at(self, dimension, value): """Cuts the given core into two parts (at the given value on the given dimension). Returns the lower part and the upper part as a tuple (lower, upper).""" lower_cuboids = [] upper_cuboids = [] for cuboid in self._cuboids: if value >= cuboid._p_max[dimension]: lower_cuboids.append(cuboid) elif value <= cuboid._p_min[dimension]: upper_cuboids.append(cuboid) else: p_min = list(cuboid._p_min) p_min[dimension] = value p_max = list(cuboid._p_max) p_max[dimension] = value lower_cuboids.append( cub.Cuboid(list(cuboid._p_min), p_max, cuboid._domains)) upper_cuboids.append( cub.Cuboid(p_min, list(cuboid._p_max), cuboid._domains)) lower_core = None if len(lower_cuboids) == 0 else Core( lower_cuboids, self._domains) upper_core = None if len(upper_cuboids) == 0 else Core( upper_cuboids, self._domains) return lower_core, upper_core
def from_cuboids(cuboids, domains): """Create a core from possibly non-intersecting cuboids by applying the repair mechanism.""" # first: simplify the cuboids to make life easier (and avoid weird results down the road) cubs = simplify(cuboids) if check(cubs, domains): return Core(cubs, domains) # all cuboids already intersect --> nothing to do # need to perform repair mechanism midpoints = [] for cuboid in cubs: # midpoint of each cuboid midpoints.append( map(lambda x, y: 0.5 * (x + y), cuboid._p_min, cuboid._p_max)) # sum up all midpoints & divide by number of cuboids midpoint = reduce(lambda x, y: map(lambda a, b: a + b, x, y), midpoints) midpoint = map(lambda x: x / len(cubs), midpoint) # extend cuboids modified_cuboids = [] for cuboid in cubs: p_min = map(min, cuboid._p_min, midpoint) p_max = map(max, cuboid._p_max, midpoint) modified_cuboids.append(cub.Cuboid(p_min, p_max, cuboid._domains)) return Core(modified_cuboids, domains)
def _check_crisp_betweenness(points, first, second): """Returns a list of boolean flags indicating which of the given points are strictly between the first and the second concept.""" # store whether the ith point has already be shown to be between the two other cores betweenness = [False] * len(points) for c1 in first._core._cuboids: for c2 in second._core._cuboids: if not c1._compatible(c2): raise Exception("Incompatible cuboids") p_min = map(min, c1._p_min, c2._p_min) p_max = map(max, c1._p_max, c2._p_max) dom_union = dict(c1._domains) dom_union.update(c2._domains) bounding_box = cub.Cuboid(p_min, p_max, dom_union) local_betweenness = [True] * len(points) # check if each point is contained in the bounding box for i in range(len(points)): local_betweenness[i] = bounding_box.contains(points[i]) if reduce(lambda x, y: x or y, local_betweenness ) == False: # no need to check inequalities continue # check additional contraints for each domain for domain in dom_union.values(): if len(domain ) < 2: # we can safely ignore one-dimensional domains continue for i in range(len(domain)): for j in range(i + 1, len(domain)): # look at all pairs of dimensions within this domain d1 = domain[i] d2 = domain[j] # create list of inequalities inequalities = [] def makeInequality(p1, p2, below): sign = -1 if below else 1 a = (p2[1] - p1[1]) if p2[0] > p1[0] else (p1[1] - p2[1]) b = -abs(p1[0] - p2[0]) c = -1 * (a * p1[0] + b * p1[1]) return (lambda x: (sign * (a * x[0] + b * x[1] + c) <= 0)) # different cases if c2._p_max[d1] > c1._p_max[d1] and c2._p_min[ d2] > c1._p_min[d2]: inequalities.append( makeInequality([c1._p_max[d1], c1._p_min[d2]], [c2._p_max[d1], c2._p_min[d2]], False)) if c2._p_max[d1] > c1._p_max[d1] and c1._p_max[ d2] > c2._p_max[d2]: inequalities.append( makeInequality(c1._p_max, c2._p_max, True)) if c2._p_min[d1] > c1._p_min[d1] and c2._p_max[ d2] > c1._p_max[d2]: inequalities.append( makeInequality([c1._p_min[d1], c1._p_max[d2]], [c2._p_min[d1], c2._p_max[d2]], True)) if c2._p_min[d1] > c1._p_min[d1] and c2._p_min[ d2] < c1._p_min[d2]: inequalities.append( makeInequality(c1._p_min, c2._p_min, False)) if c1._p_max[d1] > c2._p_max[d1] and c1._p_min[ d2] > c2._p_min[d2]: inequalities.append( makeInequality([c1._p_max[d1], c1._p_min[d2]], [c2._p_max[d1], c2._p_min[d2]], False)) if c1._p_max[d1] > c2._p_max[d1] and c2._p_max[ d2] > c1._p_max[d2]: inequalities.append( makeInequality(c1._p_max, c2._p_max, True)) if c1._p_min[d1] > c2._p_min[d1] and c1._p_max[ d2] > c2._p_max[d2]: inequalities.append( makeInequality([c1._p_min[d1], c1._p_max[d2]], [c2._p_min[d1], c2._p_max[d2]], True)) if c1._p_min[d1] > c2._p_min[d1] and c1._p_min[ d2] < c2._p_min[d2]: inequalities.append( makeInequality(c1._p_min, c2._p_min, False)) for k in range(len(points)): for ineq in inequalities: local_betweenness[k] = local_betweenness[ k] and ineq([points[k][d1], points[k][d2]]) if not reduce(lambda x, y: x or y, local_betweenness): break if not reduce(lambda x, y: x or y, local_betweenness): break if not reduce(lambda x, y: x or y, local_betweenness): break betweenness = map(lambda x, y: x or y, betweenness, local_betweenness) if reduce(lambda x, y: x and y, betweenness): return betweenness return betweenness
def _intersect_fuzzy_cuboids(self, c1, c2, other): """Find the highest intersection of the two cuboids (c1 from this, c2 from the other concept).""" crisp_intersection = c1.intersect_with(c2) if (crisp_intersection != None): # crisp cuboids already intersect return min(self._mu, other._mu), crisp_intersection # already compute new set of domains new_domains = dict(c1._domains) new_domains.update(c2._domains) # get ranges of closest points, store which dimensions need to be extruded, pick example points a_range, b_range = c1.get_closest_points(c2) a = map(lambda x: x[0], a_range) b = map(lambda x: x[0], b_range) extrude = map(lambda x: x[0] != x[1], a_range) mu = None p_min = None p_max = None if self._mu * exp( -self._c * cs.distance(a, b, self._weights)) >= other._mu: # intersection is part of other cuboid mu = other._mu p_min, p_max = self._intersection_mu_special_case(a, c2, b, mu) elif other._mu * exp( -other._c * cs.distance(a, b, other._weights)) >= self._mu: # intersection is part of this cuboid mu = self._mu p_min, p_max = other._intersection_mu_special_case(b, c1, a, mu) else: # intersection is in the cuboid between a and b # --> find point with highest identical membership to both cuboids # only use the relevant dimensions in order to make optimization easier def membership(x, point, mu, c, weights): x_new = [] j = 0 for dim in range(cs._n_dim): if extrude[dim]: x_new.append(point[dim]) else: x_new.append(x[j]) j += 1 return mu * exp(-c * cs.distance(point, x_new, weights)) bounds = [] for dim in range(cs._n_dim): if not extrude[dim]: bounds.append((min(a[dim], b[dim]), max(a[dim], b[dim]))) first_guess = map(lambda (x, y): (x + y) / 2.0, bounds) to_minimize = lambda x: -membership(x, a, self._mu, self._c, self. _weights) constr = [{ "type": "eq", "fun": (lambda x: abs( membership(x, a, self._mu, self._c, self._weights) - membership(x, b, other._mu, other._c, other._weights))) }] opt = scipy.optimize.minimize(to_minimize, first_guess, constraints=constr, bounds=bounds, options={"eps": cs._epsilon }) #, "maxiter":500 if not opt.success and abs(opt.fun - membership( opt.x, b, other._mu, other._c, other._weights)) < 1e-06: # if optimizer failed to find exact solution, but managed to find approximate solution: take it raise Exception("Optimizer failed!") # reconstruct full x by inserting fixed coordinates that will be extruded later x_star = [] j = 0 for dim in range(cs._n_dim): if extrude[dim]: x_star.append(a[dim]) else: x_star.append(opt.x[j]) j += 1 mu = membership(opt.x, a, self._mu, self._c, self._weights) # check if the weights are linearly dependent w.r.t. all relevant dimensions relevant_dimensions = [] for i in range(cs._n_dim): if not extrude[i]: relevant_dimensions.append(i) relevant_domains = self._reduce_domains(cs._domains, relevant_dimensions) t = None weights_dependent = True for (dom, dims) in relevant_domains.items(): for dim in dims: if t is None: # initialize t = (self._weights._domain_weights[dom] * sqrt(self._weights._dimension_weights[dom][dim]) ) / (other._weights._domain_weights[dom] * sqrt( other._weights._dimension_weights[dom][dim])) else: # compare t_prime = ( self._weights._domain_weights[dom] * sqrt(self._weights._dimension_weights[dom][dim]) ) / (other._weights._domain_weights[dom] * sqrt(other._weights._dimension_weights[dom][dim])) if round(t, 10) != round(t_prime, 10): weights_dependent = False break if not weights_dependent: break if weights_dependent and len(relevant_domains.keys()) > 1: # weights are linearly dependent and at least two domains are involved # --> need to find all possible corner points of resulting cuboid epsilon_1 = -log(mu / self._mu) / self._c epsilon_2 = -log(mu / other._mu) / other._c points = [] for num_free_dims in range(1, len(relevant_dimensions)): # start with a single free dimensions (i.e., edges of the bounding box) and increase until we find a solution for free_dims in itertools.combinations( relevant_dimensions, num_free_dims): # free_dims is the set of dimensions that are allowed to vary, all other ones are fixed binary_vecs = list( itertools.product([False, True], repeat=len(relevant_dimensions) - num_free_dims)) for vec in binary_vecs: # compute the difference between the actual distance and the desired epsilon-distance def epsilon_difference(x, point, weights, epsilon): i = 0 j = 0 x_new = [] # puzzle together our large x vector based on the fixed and the free dimensions for dim in range(cs._n_dim): if dim in free_dims: x_new.append(x[i]) i += 1 elif extrude[dim]: x_new.append(a[dim]) else: x_new.append( a[dim] if vec[j] else b[dim]) j += 1 return abs( cs.distance(point, x_new, weights) - epsilon) bounds = [] for dim in free_dims: bounds.append( (min(a[dim], b[dim]), max(a[dim], b[dim]))) first_guess = map(lambda (x, y): (x + y) / 2.0, bounds) to_minimize = lambda x: max( epsilon_difference(x, a, self._weights, epsilon_1)**2, epsilon_difference(x, b, other._weights, epsilon_2)**2) opt = scipy.optimize.minimize( to_minimize, first_guess) #tol = 0.000001 if opt.success: dist1 = epsilon_difference( opt.x, a, self._weights, epsilon_1) dist2 = epsilon_difference( opt.x, b, other._weights, epsilon_2) between = True k = 0 for dim in free_dims: if not (min(a[dim], b[dim]) <= opt.x[k] <= max(a[dim], b[dim])): between = False break k += 1 # must be between a and b on all free dimensions AND must be a sufficiently good solution if dist1 < 0.00001 and dist2 < 0.00001 and between: point = [] i = 0 j = 0 # puzzle together our large x vector based on the fixed and the free dimensions for dim in range(cs._n_dim): if dim in free_dims: point.append(opt.x[i]) i += 1 elif extrude[dim]: point.append(a[dim]) else: point.append( a[dim] if vec[j] else b[dim]) j += 1 points.append(point) if len(points) > 0: # if we found a solution for num_free_dims: stop looking at higher values for num_free_dims p_min = [] p_max = [] for i in range(cs._n_dim): p_min.append( max(min(a[i], b[i]), reduce(min, map(lambda x: x[i], points)))) p_max.append( min(max(a[i], b[i]), reduce(max, map(lambda x: x[i], points)))) break if p_min == None or p_max == None: # this should never happen - if the weights are dependent, there MUST be a solution raise Exception( "Could not find solution for dependent weights") else: # weights are not linearly dependent: use single-point cuboid p_min = list(x_star) p_max = list(x_star) pass # round everything, because we only found approximate solutions anyways mu = cs.round(mu) p_min = map(cs.round, p_min) p_max = map(cs.round, p_max) # extrude in remaining dimensions for i in range(len(extrude)): if extrude[i]: p_max[i] = a_range[i][1] # finally, construct a cuboid and return it along with mu cuboid = cub.Cuboid(p_min, p_max, new_domains) return mu, cuboid