202 lines
7 KiB
Python
202 lines
7 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.
|
|
"""
|
|
|
|
import graph
|
|
# import numpy as np
|
|
import math
|
|
import collections
|
|
import random
|
|
|
|
class GraphGenerator(object):
|
|
"""
|
|
Generates a random Graph with dv an array containing all vertices (there type),
|
|
de an array containing all edges (their type) and dc_inc an array representing
|
|
the incoming edges (analogue for dc_out)
|
|
"""
|
|
def __init__(self, dv, de, dc_inc, dc_out, debug=False):
|
|
if len(de) != len(dc_inc):
|
|
raise ValueError('de and dc_inc should be the same length.')
|
|
if len(de) != len(dc_out):
|
|
raise ValueError('de and dc_out should be the same length.')
|
|
|
|
self.dv = dv
|
|
self.de = de
|
|
self.dc_inc = dc_inc
|
|
self.dc_out = dc_out
|
|
|
|
# print for debugging, so you know the used values
|
|
if debug:
|
|
print('dv')
|
|
print('[',','.join(map(str,dv)),']')
|
|
print('_____')
|
|
print('de')
|
|
print('[',','.join(map(str,de)),']')
|
|
print('_____')
|
|
print('dc_inc')
|
|
print('[',','.join(map(str,dc_inc)),']')
|
|
print('_____')
|
|
print('dc_out')
|
|
print('[',','.join(map(str,dc_out)),']')
|
|
print('_____')
|
|
|
|
self.graph = graph.Graph()
|
|
self.vertices = []
|
|
# create all the vertices:
|
|
for v_type in self.dv:
|
|
# v_type represents the type of the vertex
|
|
self.vertices.append(self.graph.addCreateVertex('v' + str(v_type)))
|
|
|
|
index = 0
|
|
# create all edges
|
|
for e_type in self.de:
|
|
# e_type represents the type of the edge
|
|
src = self.vertices[self.dc_out[index]] # get src vertex
|
|
tgt = self.vertices[self.dc_inc[index]] # get tgt vertex
|
|
self.graph.addCreateEdge(src, tgt, 'e' + str(e_type)) # create edge
|
|
index += 1
|
|
|
|
def getRandomGraph(self):
|
|
return self.graph
|
|
|
|
def getRandomPattern(self, max_nr_of_v, max_nr_of_e, start=0, debug=False):
|
|
# create pattern
|
|
pattern = graph.Graph()
|
|
|
|
# map from graph to new pattern
|
|
graph_to_pattern = {}
|
|
|
|
# map of possible edges
|
|
# we don't need a dict, but python v2.7 does not have an OrderedSet
|
|
possible_edges = collections.OrderedDict()
|
|
|
|
# set of chosen edges
|
|
chosen_edges = set()
|
|
|
|
# start node from graph
|
|
g_node = self.vertices[start]
|
|
p_node = pattern.addCreateVertex(g_node.type)
|
|
# for debuging, print the order in which the pattern gets created and
|
|
# connects it edges
|
|
if debug:
|
|
print('v'+str(id(p_node))+'=pattern.addCreateVertex('+"'"+str(g_node.type)+"'"+')')
|
|
# save corrolation
|
|
graph_to_pattern[g_node] = p_node
|
|
|
|
def insertAllEdges(edges, possible_edges, chosen_edges):
|
|
for edge in edges:
|
|
# if we did not chose the edge
|
|
if edge not in chosen_edges:
|
|
# if inc_edge not in possible edges, add it with value 1
|
|
possible_edges[edge] = None
|
|
|
|
def insertEdges(g_vertex, possible_edges, chosen_edges):
|
|
insertAllEdges(g_vertex.incoming_edges, possible_edges, chosen_edges)
|
|
insertAllEdges(g_vertex.outgoing_edges, possible_edges, chosen_edges)
|
|
|
|
insertEdges(g_node, possible_edges, chosen_edges)
|
|
|
|
while max_nr_of_v > len(graph_to_pattern) and max_nr_of_e > len(chosen_edges):
|
|
candidate = None
|
|
if len(possible_edges) == 0:
|
|
break
|
|
# get a random number between 0 and len(possible_edges)
|
|
# We us a triangular distribution to approximate the fact that
|
|
# the first element is the longest in the possible_edges and
|
|
# already had the post chance of beeing choosen.
|
|
# (The approximation is because the first few ellements where
|
|
# added in the same itteration, but doing this exact is
|
|
# computationally expensive.)
|
|
if len(possible_edges) == 1:
|
|
randie = 0
|
|
else:
|
|
randie = int(round(random.triangular(1, len(possible_edges), len(possible_edges)))) - 1
|
|
candidate = list(possible_edges.keys())[randie]
|
|
del possible_edges[candidate]
|
|
chosen_edges.add(candidate)
|
|
|
|
src = graph_to_pattern.get(candidate.src)
|
|
tgt = graph_to_pattern.get(candidate.tgt)
|
|
src_is_new = True
|
|
if src != None and tgt != None:
|
|
# create edge between source and target
|
|
pattern.addCreateEdge(src, tgt, candidate.type)
|
|
if debug:
|
|
print('pattern.addCreateEdge('+'v'+str(id(src))+', '+'v'+str(id(tgt))+', '+"'"+str(candidate.type)+"'"+')')
|
|
# skip adding new edges
|
|
continue
|
|
elif src == None:
|
|
# create pattern vertex
|
|
src = pattern.addCreateVertex(candidate.src.type)
|
|
if debug:
|
|
print('v'+str(id(src))+'=pattern.addCreateVertex('+"'"+str(candidate.src.type)+"'"+')')
|
|
# map newly created pattern vertex
|
|
graph_to_pattern[candidate.src] = src
|
|
# create edge between source and target
|
|
pattern.addCreateEdge(src, tgt, candidate.type)
|
|
if debug:
|
|
print('pattern.addCreateEdge('+'v'+str(id(src))+', '+'v'+str(id(tgt))+', '+"'"+str(candidate.type)+"'"+')')
|
|
elif tgt == None:
|
|
src_is_new = False
|
|
# create pattern vertex
|
|
tgt = pattern.addCreateVertex(candidate.tgt.type)
|
|
if debug:
|
|
print('v'+str(id(tgt))+'=pattern.addCreateVertex('+"'"+str(candidate.tgt.type)+"'"+')')
|
|
# map newly created pattern vertex
|
|
graph_to_pattern[candidate.tgt] = tgt
|
|
# create edge between source and target
|
|
pattern.addCreateEdge(src, tgt, candidate.type)
|
|
if debug:
|
|
print('pattern.addCreateEdge('+'v'+str(id(src))+', '+'v'+str(id(tgt))+', '+"'"+str(candidate.type)+"'"+')')
|
|
else:
|
|
raise RuntimeError('Bug: src or tgt of edge should be in out pattern')
|
|
|
|
# select the vertex from the chosen edge that was not yet part of the pattern
|
|
if src_is_new:
|
|
new_vertex = candidate.src
|
|
else:
|
|
new_vertex = candidate.tgt
|
|
# insert all edges from the new vertex
|
|
insertEdges(new_vertex, possible_edges, chosen_edges)
|
|
|
|
return pattern
|
|
|
|
def createConstantPattern():
|
|
"""
|
|
Use this to create the same pattern over and over again.
|
|
"""
|
|
# create pattern
|
|
pattern = graph.Graph()
|
|
|
|
|
|
# copy and paste printed pattern from debug output or create a pattern
|
|
# below the following line:
|
|
# ----------------------------------------------------------------------
|
|
v4447242448=pattern.addCreateVertex('v4')
|
|
v4457323088=pattern.addCreateVertex('v6')
|
|
pattern.addCreateEdge(v4447242448, v4457323088, 'e4')
|
|
v4457323216=pattern.addCreateVertex('v8')
|
|
pattern.addCreateEdge(v4457323216, v4447242448, 'e4')
|
|
v4457323344=pattern.addCreateVertex('v7')
|
|
pattern.addCreateEdge(v4457323216, v4457323344, 'e3')
|
|
v4457323472=pattern.addCreateVertex('v7')
|
|
pattern.addCreateEdge(v4457323344, v4457323472, 'e1')
|
|
|
|
# ----------------------------------------------------------------------
|
|
return pattern
|