
kieker.analysis.generic.graph.clustering.GraphEditDistance Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of analysis Show documentation
Show all versions of analysis Show documentation
Kieker: Application Performance Monitoring and Dynamic Software Analysis
The newest version!
/***************************************************************************
* Copyright (C) 2017 iObserve Project (https://www.iobserve-devops.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
package kieker.analysis.generic.graph.clustering;
import java.util.Optional;
import com.google.common.graph.MutableNetwork;
import kieker.analysis.generic.clustering.mtree.IDistanceFunction;
import kieker.analysis.generic.graph.IEdge;
import kieker.analysis.generic.graph.INode;
/**
* This class calculates a custom graph edit distance between two Behavior Models.
*
* The following operations are allowed:
*
* insert/delete nodes; insert/delete edges; insert/delete EventGroups; insert/delete
* Events; duplicate/remove duplicate events
*
* Insertion and Deletion always costs the same to satisfy the symmetry property.
*
* @param
* node type
* @param
* edge type
*
* @author Lars Jürgensen
* @since 2.0.0
*/
public class GraphEditDistance implements IDistanceFunction> {
private final BasicCostFunction costFunction;
/**
* This can be used, to set the node, edge and eventgroup insertion cost for the Graph Edit
* Distance Algorithm to the values in the configuration.
*
* @param costFunction
* cost function
*/
public GraphEditDistance(final BasicCostFunction costFunction) {
this.costFunction = costFunction;
}
/**
* Calculates the Graph Edit Distance between two objects.
*
* @param modelA
* The first model.
* @param modelB
* The second model.
*/
@Override
public double calculate(final MutableNetwork modelA, final MutableNetwork modelB) {
double distance = 0;
// check if nodes from model1 are in model2
for (final N node : modelA.nodes()) {
final Optional match = this.findNode(modelB, node.getId());
if (!match.isPresent()) { // node only occurs in one objects => must be inserted
distance += this.nodeInsertionCost(modelA, node);
} else { // node occurs in both objects => must be compared
distance += this.nodeDistance(modelA, node, modelB, match.get());
}
}
// check if nodes from model2 are in model1
for (final N node : modelB.nodes()) {
final Optional match = this.findNode(modelA, node.getId());
// node only occurs in one objects => must be inserted
if (!match.isPresent()) {
distance += this.nodeInsertionCost(modelB, node);
}
}
return distance;
}
private Optional findNode(final MutableNetwork model, final String signature) {
return model.nodes().stream().filter(node -> node.getId().equals(signature)).findFirst();
}
/**
* Calculates the distance between two nodes. This includes the distance between the ingoing
* edges.h
*/
private double nodeDistance(final MutableNetwork modelA, final N nodeA, final MutableNetwork modelB, final N nodeB) {
double distance = this.costFunction.nodeAnnotationDistance(nodeA, nodeB);
for (final E edge : modelA.inEdges(nodeA)) {
final N sourceA = modelA.incidentNodes(edge).source();
final N sourceB = this.findNode(modelB, sourceA.getId()).get();
// find the matching edge.
final Optional match = this.findEdge(modelB, sourceB, nodeB);
if (match.isPresent()) { // edge occurs in both nodes => must be compared
distance += this.costFunction.edgeAnnotationDistance(edge, match.get());
} else { // edge only occurs in one node => must be inserted
distance += this.costFunction.computeEdgeInsertionCost(edge);
}
}
for (final E edge : modelB.inEdges(nodeB)) {
final N sourceB = modelB.incidentNodes(edge).source();
final N sourceA = this.findNode(modelA, sourceB.getId()).get();
final Optional match = this.findEdge(modelA, sourceA, nodeA);
// edge only occurs in one node => must be inserted
if (!match.isPresent()) {
distance += this.costFunction.computeEdgeInsertionCost(edge);
}
}
return distance;
}
private Optional findEdge(final MutableNetwork model, final N source, final N target) {
return model.inEdges(target).stream().filter(edge -> model.incidentNodes(edge).source().equals(source)).findFirst();
}
/**
* calculates the insertion cost of a node including the insertion cost of the ingoing edges.
*
* @param model
* graph
* @param node
* node for which we whan to compute the cost
*/
private double nodeInsertionCost(final MutableNetwork model, final N node) {
double distance = this.costFunction.computeNodeInsertionCost(node);
for (final E edge : model.inEdges(node)) {
distance += this.costFunction.computeEdgeInsertionCost(edge);
}
return distance;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy