def _intersection_mu_special_case(self, a, c2, b, mu): """Membership of b in c2 (other) to c1 (self) is higher than mu (other).""" def makeFun(idx): # need this in order to avoid weird results (defining lambda in loop) return (lambda y: y[idx] - b[idx]) distance = - log(mu / self._mu) / self._c y = [] for i in range(cs._n_dim): if a[i] == b[i]: y.append(a[i]) else: constr = [{"type":"eq", "fun":(lambda y: cs.distance(a,y,self._weights) - distance)}] for j in range(cs._n_dim): if i != j: constr.append({"type":"eq", "fun":makeFun(j)}) if a[i] < b[i]: opt = scipy.optimize.minimize(lambda y: -y[i], b, constraints = constr) if not opt.success: raise Exception("Optimizer failed!") y.append(opt.x[i]) else: opt = scipy.optimize.minimize(lambda y: y[i], b, constraints = constr) if not opt.success: raise Exception("Optimizer failed!") y.append(opt.x[i]) # arrange entries in b and y to make p_min and p_max; make sure we don't fall out of c2 p_min = map(max, map(min, b, y), c2._p_min) p_max = map(min, map(max, b, y), c2._p_max) # take the unification of domains return p_min, p_max
def membership_of(self, point): """Computes the membership of the point in this concept.""" min_distance = reduce( min, map(lambda x: cs.distance(x, point, self._weights), self._core.find_closest_point_candidates(point))) return self._mu * exp(-self._c * min_distance)
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))
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)
def similarity_to(self, other, method="naive"): """Computes the similarity of this concept to the given other concept. The following methods are avaliable: 'naive': similarity of cores' midpoints (used as default) 'Jaccard': Jaccard similarity index (size of intersection over size of union) 'subset': use value returned by subset_of() 'min_core': similarity based on minimum distance of cores 'max_core': similarity based on maximum distance of cores 'min_membership_core': minimal membership of any point in self._core to other 'max_membership_core': maximal membership of any point in self._core to other 'Hausdorff_core': similarity based on Hausdorff distance of cores 'min_center': similarity based on minimum distance of cores' central region 'max_center': similarity based on maximum distance of cores' central region 'Hausdorff_center': similarity based on Hausdorff distance of cores' central region 'min_membership_center': minimal membership of any point in self's central region to other 'max_membership_center': maximal membership of any point in self's central region to other""" # project both concepts onto their common domains to find a common ground common_domains = {} for dom, dims in self._core._domains.iteritems(): if dom in other._core._domains and other._core._domains[ dom] == dims: common_domains[dom] = dims if len(common_domains) == 0: # can't really compare them because they have no common domains --> return 0.0 return 0.0 projected_self = self.project_onto(common_domains) projected_other = other.project_onto(common_domains) if method == "naive": self_midpoint = projected_self._core.midpoint() other_midpoint = projected_other._core.midpoint() sim = exp(-projected_other._c * cs.distance( self_midpoint, other_midpoint, projected_other._weights)) return sim elif method == "Jaccard": intersection = projected_self.intersect_with(projected_other) union = projected_self.unify_with(projected_other) intersection._c = projected_other._c union._c = projected_other._c intersection._weights = projected_other._weights union._weights = projected_other._weights sim = intersection.size() / union.size() return sim elif method == "subset": return projected_self.subset_of(projected_other) elif method == "min_core": min_dist = float("inf") for c1 in projected_self._core._cuboids: for c2 in projected_other._core._cuboids: 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) dist = cs.distance(a, b, projected_other._weights) min_dist = min(min_dist, dist) sim = exp(-projected_other._c * min_dist) return sim elif method == "max_core": max_dist = 0 for c1 in projected_self._core._cuboids: for c2 in projected_other._core._cuboids: a, b = c1.get_most_distant_points(c2) dist = cs.distance(a, b, projected_other._weights) max_dist = max(max_dist, dist) sim = exp(-projected_other._c * max_dist) return sim elif method == "Hausdorff_core": self_candidates = [] other_candidates = [] for c1 in projected_self._core._cuboids: for c2 in projected_other._core._cuboids: a, b = c1.get_most_distant_points(c2) self_candidates.append(a) other_candidates.append(b) max_dist = 0 for self_candidate in self_candidates: min_dist = float("inf") for c2 in projected_other._core._cuboids: p = c2.find_closest_point(self_candidate) min_dist = min( min_dist, cs.distance(self_candidate, p, projected_other._weights)) max_dist = max(max_dist, min_dist) for other_candidate in other_candidates: min_dist = float("inf") for c1 in projected_self._core._cuboids: p = c1.find_closest_point(other_candidate) min_dist = min( min_dist, cs.distance(other_candidate, p, projected_other._weights)) max_dist = max(max_dist, min_dist) sim = exp(-projected_other._c * max_dist) return sim elif method == "min_membership_core": candidates = [] for c1 in projected_self._core._cuboids: for c2 in projected_other._core._cuboids: a, b = c1.get_most_distant_points(c2) candidates.append(a) min_membership = 1.0 for candidate in candidates: min_membership = min(min_membership, projected_other.membership_of(candidate)) return min_membership elif method == "max_membership_core": candidates = [] for c1 in projected_self._core._cuboids: for c2 in projected_other._core._cuboids: a, b = c1.get_closest_points(c2) candidates.append(map(lambda x: x[0], a)) max_membership = 0.0 for candidate in candidates: max_membership = max(max_membership, projected_other.membership_of(candidate)) return max_membership elif method == "min_center": p1 = projected_self._core.get_center() p2 = projected_other._core.get_center() a_range, b_range = p1.get_closest_points(p2) a = map(lambda x: x[0], a_range) b = map(lambda x: x[0], b_range) sim = exp(-projected_other._c * cs.distance(a, b, projected_other._weights)) return sim elif method == "max_center": p1 = projected_self._core.get_center() p2 = projected_other._core.get_center() a, b = p1.get_most_distant_points(p2) sim = exp(-projected_other._c * cs.distance(a, b, projected_other._weights)) return sim elif method == "Hausdorff_center": p1 = projected_self._core.get_center() p2 = projected_other._core.get_center() a_distant, b_distant = p1.get_most_distant_points(p2) a_close, b_close = p1.get_closest_points(p2) a = [] b = [] # find closest point a to b_distant and b to a_distant for i in range(cs._n_dim): if a_close[i][0] <= b_distant[i] <= a_close[i][1]: a.append(b_distant[i]) elif b_distant[i] < a_close[i][0]: a.append(a_close[i][0]) else: a.append(a_close[i][1]) if b_close[i][0] <= a_distant[i] <= b_close[i][1]: b.append(a_distant[i]) elif a_distant[i] < b_close[i][0]: b.append(b_close[i][0]) else: b.append(b_close[i][1]) first_dist = cs.distance(a_distant, b, projected_other._weights) second_dist = cs.distance(b_distant, a, projected_other._weights) sim = exp(-projected_other._c * max(first_dist, second_dist)) return sim elif method == "min_membership_center": center = projected_self._core.get_center() candidates = [] for c2 in projected_other._core._cuboids: a, b = center.get_most_distant_points(c2) candidates.append(a) min_membership = 1.0 for candidate in candidates: min_membership = min(min_membership, projected_other.membership_of(candidate)) return min_membership elif method == "max_membership_center": center = projected_self._core.get_center() candidates = [] for c2 in projected_other._core._cuboids: a, b = center.get_closest_points(c2) candidates.append(map(lambda x: x[0], a)) max_membership = 0.0 for candidate in candidates: max_membership = max(max_membership, projected_other.membership_of(candidate)) return max_membership else: raise Exception("Unknown method")
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