def distance2(self, graph1, graph2, permutation): """ Compute a graph distance metric between two graphs give a permutation vector. This is given by F(P) = (1-alpha)/(||W1||^2_F + ||W2||^2_F) (||W1 - P W2 P.T||^2_F) - alpha 1/(||V1||_F^2 + ||V2||_F^2) ||V1 - P.T V2||^2_F and is bounded between 0 and 1. :param graph1: A graph object :param graph2: The second graph object to match :param permutation: An array of permutation indices matching the first to second graph :type permutation: `numpy.ndarray` """ if self.useWeightM: W1 = graph1.getWeightMatrix() W2 = graph2.getWeightMatrix() else: W1 = graph1.adjacencyMatrix() W2 = graph2.adjacencyMatrix() if W1.shape[0] < W2.shape[0]: W1 = Util.extendArray(W1, W2.shape) elif W2.shape[0] < W1.shape[0]: W2 = Util.extendArray(W2, W1.shape) n = W1.shape[0] P = numpy.zeros((n, n)) P[(numpy.arange(n), permutation)] = 1 dist1 = numpy.linalg.norm(W1 - P.dot(W2).dot(P.T))**2 #Now compute the vertex similarities distance V1 = graph1.getVertexList().getVertices() V2 = graph2.getVertexList().getVertices() if V1.shape[0] < V2.shape[0]: V1 = Util.extendArray(V1, V2.shape) elif V2.shape[0] < V1.shape[0]: V2 = Util.extendArray(V2, V1.shape) dist2 = numpy.sum((V1 - P.T.dot(V2))**2) norm1 = ((W1**2).sum() + (W2**2).sum()) norm2 = ((V1**2).sum() + (V2**2).sum()) if norm1!= 0: dist1 = dist1/norm1 if norm2!= 0: dist2 = dist2/norm2 dist = (1-self.alpha)*dist1 + self.alpha*dist2 return dist
def testExtendArray(self): X = numpy.random.rand(5, 5) X2 = Util.extendArray(X, (10, 5)) nptst.assert_array_equal(X, X2[0:5, :]) nptst.assert_array_equal(0, X2[5:, :]) X2 = Util.extendArray(X, (10, 5), 1.23) nptst.assert_array_equal(X, X2[0:5, :]) nptst.assert_array_equal(1.23, X2[5:, :]) #Now try extending using an array X2 = Util.extendArray(X, (10, 5), numpy.array([1, 2, 3, 4, 5])) nptst.assert_array_equal(X, X2[0:5, :]) for i in range(5, 10): nptst.assert_array_equal(numpy.array([1, 2, 3, 4, 5]), X2[i, :])
def distance(self, graph1, graph2, permutation, normalised=False, nonNeg=False, verbose=False): """ Compute the graph distance metric between two graphs given a permutation vector. This is given by F(P) = (1-alpha)/(||W1||^2_F + ||W2||^2_F)(||W1 - P W2 P.T||^2_F) - alpha 1/||C||_F tr(C.T P) in the normalised case. If we want an unnormalised solution it is computed as (1-alpha)/(||W1 - P W2 P.T||^2_F) - alpha tr C.T P and finally there is a standardised case in which the distance is between 0 and 1, where ||C||_F is used to normalise the vertex similarities and we assume 0 <= C_ij <= 1. :param graph1: A graph object :param graph2: The second graph object to match :param permutation: An array of permutation indices matching the first to second graph :type permutation: `numpy.ndarray` :param normalised: Specify whether to normalise the objective function :type normalised: `bool` :param nonNeg: Specify whether we want a non-negative solution. :type nonNeg: `bool` :param verbose: Specify whether to return graph and label distance :type nonNeg: `bool` """ if graph1.size == 0 and graph2.size == 0: if not verbose: return 0.0 else: return 0.0, 0.0, 0.0 elif graph1.size == 0 or graph2.size == 0: if normalised: if not verbose: return 1-self.alpha else: return 1-self.alpha, 1-self.alpha, 0.0 else: raise ValueError("Unsupported case") if self.useWeightM: W1 = graph1.getWeightMatrix() W2 = graph2.getWeightMatrix() else: W1 = graph1.adjacencyMatrix() W2 = graph2.adjacencyMatrix() if W1.shape[0] < W2.shape[0]: W1 = Util.extendArray(W1, W2.shape, self.rho) elif W2.shape[0] < W1.shape[0]: W2 = Util.extendArray(W2, W1.shape, self.rho) n = W1.shape[0] P = numpy.zeros((n, n)) P[(numpy.arange(n), permutation)] = 1 dist1 = numpy.linalg.norm(W1 - P.dot(W2).dot(P.T))**2 #Now compute the vertex similarities trace C = self.vertexSimilarities(graph1, graph2) minC = numpy.min(C) maxC = numpy.max(C) C = Util.extendArray(C, (n, n), minC + self.gamma*(maxC-minC)) dist2 = numpy.trace(C.T.dot(P)) if normalised: norm1 = ((W1**2).sum() + (W2**2).sum()) norm2 = numpy.linalg.norm(C) if norm1!= 0: dist1 = dist1/norm1 if norm2!= 0: dist2 = dist2/norm2 dist = (1-self.alpha)*dist1 - self.alpha*dist2 #If nonNeg = True then we add a term to the distance to ensure it is #always positive. The numerator is an upper bound on tr(C.T P) if nonNeg and normalised: normC = norm2 logging.debug("Graph distance: " + str(dist1) + " label distance: " + str(dist2) + " distance offset: " + str(self.alpha*n/normC) + " graph sizes: " + str((graph1.size, graph2.size))) if normC != 0: dist = dist + self.alpha*n/normC else: logging.debug("Graph distance: " + str(dist1) + " label distance: " + str(dist2) + " graph sizes: " + str((graph1.size, graph2.size))) if verbose: return dist, dist1, dist2 else: return dist
def addVertices(self, n): """ Adds n vertices to this object. """ self.V = Util.extendArray(self.V, (self.V.shape[0] + n, self.V.shape[1]))
def addVertices(self, n): """ Adds n vertices to this object. """ self.V = Util.extendArray(self.V, (self.V.shape[0] + n, self.V.shape[1]))