muMLE/pattern_matching/searchGraph.py

115 lines
No EOL
4.1 KiB
Python

# coding: utf-8
"""
Author: Sten Vercamman
Univeristy of Antwerp
Example code for paper: Efficient model transformations for novices
url: http://msdl.cs.mcgill.ca/people/hv/teaching/MSBDesign/projects/Sten.Vercammen
The main goal of this code is to give an overview, and an understandable
implementation, of known techniques for pattern matching and solving the
sub-graph homomorphism problem. The presented techniques do not include
performance adaptations/optimizations. It is not optimized to be efficient
but rather for the ease of understanding the workings of the algorithms.
The paper does list some possible extensions/optimizations.
It is intended as a guideline, even for novices, and provides an in-depth look
at the workings behind various techniques for efficient pattern matching.
"""
from graph import *
import math
class SearchGraph(Graph):
"""
A SearchGraph is an extended Graph, it keeps traks of statistics
for creating the cost model when generating a search plan.
It stire the amount of edges for each edge.type per vertex.type.
"""
def __init__(self, orig=None, deepCopy=False):
Graph.__init__(self)
# member variables:
self.nr_of_inc_edges = {} # {vertex_type, {edge_type, nr of incoming edges of edge_type for vertex_type } }
self.nr_of_out_edges = {} # {vertex_type, {edge_type, nr of outgoing edges of edge_type for vertex_type } }
if orig != None:
if not (isinstance(orig, Graph) or isinstance(orig, SearchGraph)):
raise TypeError('Can only create SearchGraph from Graph and SearchGraph types')
if not deepCopy:
# copy all memeber elements:
self.vertices = orig.vertices # this is a reference
self.edges = orig.edges # this is a reference
# udpate the edge counters for each edge
for _, edges in self.edges.items():
for edge in edges:
self.addToEdgeCounters(edge)
else: # TODO: deepcopy (not really needed)
pass
def addCreateEdge(self, src, tgt, str_type):
"""
Creates edge of str_type from src to tgt, and returns it,
so that properties can be added to the edge.
This also add the Edge to the Edge counters
"""
# call parent fucntion, this function is an extention
edge = Graph.addCreateEdge(self, src, tgt, str_type)
self.updateEdgeCounters(edge)
return edge
def addToEdgeCounters(self, edge):
"""
Add the Edge to the Edge counters.
"""
# get {edge.type, counter} for tgt vertex of edge (or create it)
edge_counters = self.nr_of_inc_edges.setdefault(edge.tgt.type, {})
# increase counter of edge.type by 1
edge_counters[edge.type] = edge_counters.get(edge.type, 0) + 1
# get {edge.type, counter} for src vertex of edge (or create it)
edge_counters = self.nr_of_out_edges.setdefault(edge.src.type, {})
# increase counter of edge.type by 1
edge_counters[edge.type] = edge_counters.get(edge.type, 0) + 1
def getCostLkp(self, type, is_vertex):
"""
Returns the cost of a lkp primitive operation (of a vertex or edge).
Returns None if vertex type or edge type not present in Host Graph
"""
if is_vertex:
cost = len(self.getVerticesOfType(type))
else:
cost = len(self.getEdgesOfType(type))
if cost == 0:
return None
# we use a logaritmic cost
return math.log(cost)
def getCostInc(self, vertex_type, edge_type):
"""
Returns the cost of an in primitive operation.
Returns None if vertex_type or edge_type not present in Host Graph
"""
cost = float(self.nr_of_inc_edges.get(vertex_type, {}).get(edge_type))
if cost != None:
nr_of_vertices_with_type = len(self.getVerticesOfType(vertex_type))
if nr_of_vertices_with_type != 0:
cost /= len(self.getVerticesOfType(vertex_type))
# we use a logaritmic cost
cost = math.log(cost)
return cost
def getCostOut(self, vertex_type, edge_type):
"""
Returns the cost of an out primitive operation.
Returns None if vertex_type or edge_type not present in Host Graph
"""
cost = float(self.nr_of_out_edges.get(vertex_type, {}).get(edge_type))
if cost != None:
nr_of_vertices_with_type = len(self.getVerticesOfType(vertex_type))
if nr_of_vertices_with_type != 0:
cost /= len(self.getVerticesOfType(vertex_type))
# we use a logaritmic cost
cost = math.log(cost)
return cost