Esempio n. 1
0
    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))
Esempio n. 4
0
 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