'Returns the similarity of the given concepts,
by cross-comparing shortest path distance between k concept properties.
A given concept can also be a flat list of properties, e.g. ["creepy"].
The given heuristic is a tuple of two functions:
1) function(concept) returns a list of salient properties,
2) function(edge) returns the cost for traversing this edge (0.0-1.0).'

def similarity(self, concept1, concept2, k=3, heuristic=COMMONALITY):

    if isinstance(concept1, basestring):
        concept1 = self[concept1]
    if isinstance(concept2, basestring):
        concept2 = self[concept2]
    if isinstance(concept1, Node):
        concept1 = heuristic[0](concept1)
    if isinstance(concept2, Node):
        concept2 = heuristic[0](concept2)
    if isinstance(concept1, list):
        concept1 = [((isinstance(n, Node) and n) or self[n]) for n in concept1]
    if isinstance(concept2, list):
        concept2 = [((isinstance(n, Node) and n) or self[n]) for n in concept2]
    h = (lambda id1, id2: heuristic[1](self.edge(id1, id2)))
    w = 0.0
    for p1 in concept1[:k]:
        for p2 in concept2[:k]:
            p = self.shortest_path(p1, p2, heuristic=h)
            w += (1.0 / (((p is None) and 10000000000.0) or len(p)))
    return (w / k)
