115 lines
No EOL
4.1 KiB
Python
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 |