(Re)Move some files

This commit is contained in:
Joeri Exelmans 2024-10-03 17:19:25 +02:00
parent 175edb64d9
commit 9faa5770a8
10 changed files with 4 additions and 1419 deletions

View file

@ -6,12 +6,11 @@ from uuid import UUID
from services.scd import SCD
from framework.conformance import Conformance
from services.od import OD
from transformation.matcher import mvs_adapter
from transformation.ramify import ramify
from transformation import rewriter
from services.bottom.V0 import Bottom
from services.primitives.integer_type import Integer
from pattern_matching import mvs_adapter
from pattern_matching.matcher import MatcherVF2
from concrete_syntax import plantuml
from concrete_syntax.textual_od import parser, renderer
@ -91,10 +90,8 @@ Bear_inh_Animal:Inheritance (Bear -> Animal)
dsl_m_cs = """
george :Man
weight = 80
bear1 :Bear
bear2 :Bear
bear1:Bear
bear2:Bear
:afraidOf (george -> bear1)
:afraidOf (george -> bear2)
"""

View file

@ -1,286 +0,0 @@
import time
import matcher as j # joeri's matcher
import graph as sgraph # sten's graph
import patternMatching as s # sten's matcher
import generator
def j_to_s(j):
s = sgraph.Graph()
m = {}
for jv in j.vtxs:
sv = s.addCreateVertex(jv.value) # value becomes type
m[jv] = sv
for je in j.edges:
s.addCreateEdge(m[je.src], m[je.tgt], "e") # only one type
return s
def s_to_j(s):
jg = j.Graph()
jg.vtxs = [ j.Vertex(typ) for (typ,svs) in s.vertices.items() for sv in svs ]
m = { sv : jg.vtxs[i] for svs in s.vertices.values() for i,sv in enumerate(svs) }
jg.edges = [j.Edge(m[se.src], m[se.tgt]) for ses in s.edges.values() for se in ses ]
return j
def run_benchmark(jhost, jguest, shost, sguest, expected=None):
j_durations = 0
s_durations = 0
# benchmark Joeri
m = j.MatcherVF2(host, guest,
lambda g_vtx, h_vtx: g_vtx.value == h_vtx.value) # all vertices can be matched
iterations = 50
print(" Patience (joeri)...")
for n in range(iterations):
time_start = time.perf_counter_ns()
matches = [mm for mm in m.match()]
time_end = time.perf_counter_ns()
duration = time_end - time_start
j_durations += duration
print(f' {iterations} iterations, took {j_durations/1000000:.3f} ms, {j_durations/iterations/1000000:.3f} ms per iteration')
if expected == None:
print(f" {len(matches)} matches")
else:
if len(matches) == expected:
print(" correct (probably)")
else:
print(f" WRONG! expected: {expected}, got: {len(matches)}")
# print([m.mapping_vtxs for m in matches])
# print([m.mapping_edges for m in matches])
# benchmark Sten
m = s.PatternMatching()
print(" Patience (sten)...")
for n in range(iterations):
time_start = time.perf_counter_ns()
matches = [mm for mm in m.matchVF2(sguest, shost)]
time_end = time.perf_counter_ns()
duration = time_end - time_start
s_durations += duration
print(f' {iterations} iterations, took {s_durations/1000000:.3f} ms, {s_durations/iterations/1000000:.3f} ms per iteration')
if expected == None:
print(f" {len(matches)} matches")
else:
if len(matches) == expected:
print(" correct (probably)")
else:
print(f" WRONG! expected: {expected}, got: {len(matches)}")
# print(matches)
print(f" joeri is {s_durations/j_durations:.2f} times faster")
if __name__ == "__main__":
print("\nBENCHMARK: small graph, simple pattern")
host = j.Graph()
host.vtxs = [j.Vertex(0), j.Vertex(0), j.Vertex(0), j.Vertex(0)]
host.edges = [
j.Edge(host.vtxs[0], host.vtxs[1]),
j.Edge(host.vtxs[1], host.vtxs[2]),
j.Edge(host.vtxs[2], host.vtxs[0]),
j.Edge(host.vtxs[2], host.vtxs[3]),
j.Edge(host.vtxs[3], host.vtxs[2]),
]
guest = j.Graph()
guest.vtxs = [
j.Vertex(0),
j.Vertex(0)]
guest.edges = [
# Look for a simple loop:
j.Edge(guest.vtxs[0], guest.vtxs[1]),
j.Edge(guest.vtxs[1], guest.vtxs[0]),
]
# because of the symmetry in our pattern, there will be 2 matches
run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=2)
#######################################################################
print("\nBENCHMARK: larger graph, simple pattern")
host = j.Graph()
host.vtxs = [
j.Vertex('triangle'), # 0
j.Vertex('square'), # 1
j.Vertex('square'), # 2
j.Vertex('circle'), # 3
j.Vertex('circle'), # 4
j.Vertex('circle'), # 5
]
host.edges = [
# not a match:
j.Edge(host.vtxs[0], host.vtxs[5]),
j.Edge(host.vtxs[5], host.vtxs[0]),
# will be a match:
j.Edge(host.vtxs[1], host.vtxs[5]),
j.Edge(host.vtxs[5], host.vtxs[1]),
# noise:
j.Edge(host.vtxs[1], host.vtxs[2]),
# will be a match:
j.Edge(host.vtxs[2], host.vtxs[4]),
j.Edge(host.vtxs[4], host.vtxs[2]),
# noise:
j.Edge(host.vtxs[0], host.vtxs[1]),
j.Edge(host.vtxs[0], host.vtxs[3]),
j.Edge(host.vtxs[0], host.vtxs[0]),
j.Edge(host.vtxs[1], host.vtxs[1]),
# will be a match:
j.Edge(host.vtxs[3], host.vtxs[2]),
j.Edge(host.vtxs[2], host.vtxs[3]),
]
guest = j.Graph()
guest.vtxs = [
j.Vertex('square'), # 0
j.Vertex('circle')] # 1
guest.edges = [
j.Edge(guest.vtxs[0], guest.vtxs[1]),
j.Edge(guest.vtxs[1], guest.vtxs[0]),
]
# should give 3 matches
run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=3)
#######################################################################
print("\nBENCHMARK: same as before, but with larger pattern")
host = j.Graph()
host.vtxs = [
j.Vertex('triangle'), # 0
j.Vertex('square'), # 1
j.Vertex('square'), # 2
j.Vertex('circle'), # 3
j.Vertex('circle'), # 4
j.Vertex('circle'), # 5
]
host.edges = [
# not a match:
j.Edge(host.vtxs[0], host.vtxs[5]),
j.Edge(host.vtxs[5], host.vtxs[0]),
# will be a match:
j.Edge(host.vtxs[1], host.vtxs[5]),
j.Edge(host.vtxs[5], host.vtxs[1]),
# noise:
j.Edge(host.vtxs[1], host.vtxs[2]),
# will be a match:
j.Edge(host.vtxs[2], host.vtxs[4]),
j.Edge(host.vtxs[4], host.vtxs[2]),
# noise:
j.Edge(host.vtxs[0], host.vtxs[1]),
j.Edge(host.vtxs[0], host.vtxs[3]),
j.Edge(host.vtxs[0], host.vtxs[0]),
j.Edge(host.vtxs[1], host.vtxs[1]),
# will be a match:
j.Edge(host.vtxs[3], host.vtxs[2]),
j.Edge(host.vtxs[2], host.vtxs[3]),
]
guest = j.Graph()
guest.vtxs = [
j.Vertex('square'), # 0
j.Vertex('circle'), # 1
j.Vertex('square')] # 2
guest.edges = [
j.Edge(guest.vtxs[0], guest.vtxs[1]),
j.Edge(guest.vtxs[1], guest.vtxs[0]),
j.Edge(guest.vtxs[2], guest.vtxs[0]),
]
# this time, only 2 matches
run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=2)
#######################################################################
print("\nBENCHMARK: disconnected pattern")
host = j.Graph()
host.vtxs = [
j.Vertex('triangle'), # 0
j.Vertex('square'), # 1
j.Vertex('square'), # 2
j.Vertex('circle'), # 3
j.Vertex('circle'), # 4
j.Vertex('circle'), # 5
j.Vertex('bear'),
j.Vertex('bear'),
]
host.edges = [
# not a match:
j.Edge(host.vtxs[0], host.vtxs[5]),
j.Edge(host.vtxs[5], host.vtxs[0]),
# will be a match:
j.Edge(host.vtxs[1], host.vtxs[5]),
j.Edge(host.vtxs[5], host.vtxs[1]),
# noise:
j.Edge(host.vtxs[1], host.vtxs[2]),
# will be a match:
j.Edge(host.vtxs[2], host.vtxs[4]),
j.Edge(host.vtxs[4], host.vtxs[2]),
# noise:
j.Edge(host.vtxs[0], host.vtxs[1]),
j.Edge(host.vtxs[0], host.vtxs[3]),
j.Edge(host.vtxs[0], host.vtxs[0]),
j.Edge(host.vtxs[1], host.vtxs[1]),
# will be a match:
j.Edge(host.vtxs[3], host.vtxs[2]),
j.Edge(host.vtxs[2], host.vtxs[3]),
]
guest = j.Graph()
guest.vtxs = [
j.Vertex('square'), # 0
j.Vertex('circle'), # 1
j.Vertex('bear')]
guest.edges = [
j.Edge(guest.vtxs[0], guest.vtxs[1]),
j.Edge(guest.vtxs[1], guest.vtxs[0]),
]
# the 'bear' in our pattern can be matched with any of the two bears in the graph, effectively doubling the number of matches
run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=6)
#######################################################################
print("\nBENCHMARK: larger graph")
shost, sguest = generator.get_large_host_and_guest()
run_benchmark(s_to_j(shost), s_to_j(sguest), shost, sguest)
#######################################################################
print("\nBENCHMARK: large random graph")
import random
random.seed(0)
shost, sguest = generator.get_random_host_and_guest(
nr_vtxs = 10,
nr_vtx_types = 0,
nr_edges = 20,
nr_edge_types = 0,
)
run_benchmark(s_to_j(shost), s_to_j(sguest), shost, sguest)

View file

@ -1,232 +0,0 @@
# 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
def get_random_host_and_guest(nr_vtxs, nr_vtx_types, nr_edges, nr_edge_types, pattern_nr_vtxs=3, pattern_nr_edges=15):
dv = [random.randint(0, nr_vtx_types) for _ in range(nr_vtxs)]
de = [random.randint(0, nr_edge_types) for _ in range(nr_edges)]
dc_inc = [random.randint(0, nr_vtxs-1) for _ in range(nr_edges)]
dc_out = [random.randint(0, nr_vtxs-1) for _ in range(nr_edges)]
return get_host_and_guest(dv, de, dc_inc, dc_out, pattern_nr_vtxs, pattern_nr_edges)
def get_host_and_guest(dv, de, dc_inc, dc_out, pattern_nr_vtxs=3, pattern_nr_edges=15):
gg = GraphGenerator(dv, de, dc_inc, dc_out)
graph = gg.getRandomGraph()
pattern = gg.getRandomPattern(pattern_nr_vtxs, pattern_nr_edges, debug=False)
return (graph, pattern)
def get_large_host_and_guest():
dv = [ 10,5,4,0,8,6,8,0,4,8,5,5,7,0,10,0,5,6,10,4,0,3,0,8,2,7,5,8,1,0,2,10,0,0,1,6,8,4,7,6,4,2,10,10,6,4,6,0,2,7 ]
de = [ 8,10,8,1,6,7,4,3,5,2,0,0,9,6,0,3,8,3,2,7,2,3,10,8,10,8,10,2,5,5,10,6,7,5,1,2,1,2,2,3,7,7,2,1,7,2,9,10,8,1,9,4,1,3,1,1,8,2,2,9,10,9,1,9,4,10,10,10,9,3,5,3,6,6,9,1,2,6,3,2,4,10,9,6,5,6,2,4,3,2,4,10,6,2,8,8,0,5,1,7,3,4,3,8,7,3,0,8,3,3,8,5,10,5,9,3,1,10,3,2,6,3,10,0,5,10,9,10,0,1,4,7,10,3,1,9,1,2,3,7,4,3,7,8,8,4,5,10,1,4 ]
dc_inc = [ 0,25,18,47,22,25,16,45,38,25,5,45,15,44,17,46,6,17,35,8,16,29,48,47,25,34,4,20,24,1,47,44,8,25,32,3,16,6,33,21,6,13,41,10,17,25,21,33,31,30,5,4,45,26,16,42,12,25,29,3,32,30,14,26,11,13,7,13,3,43,43,22,48,37,20,28,15,40,19,33,43,16,49,36,11,25,9,42,3,22,16,40,42,44,27,30,1,18,10,35,19,6,9,43,37,38,45,19,41,14,37,45,0,31,29,31,24,20,44,46,8,45,43,3,38,38,35,12,19,45,7,34,20,28,12,17,45,17,35,49,20,21,49,1,35,38,38,36,33,30 ]
dc_out = [ 9,2,49,49,37,33,16,21,5,46,4,15,9,6,14,22,16,33,23,21,15,31,37,23,47,3,30,26,35,9,29,21,39,32,22,43,5,9,41,30,31,30,37,33,31,34,23,22,34,26,44,36,38,33,48,5,9,34,13,7,48,41,43,26,26,7,12,6,12,28,22,8,29,22,24,27,16,4,31,41,32,15,19,20,38,0,26,18,43,46,40,17,29,14,34,14,32,17,32,47,16,45,7,4,35,22,42,11,38,2,0,29,4,38,17,44,9,23,5,10,31,17,1,11,16,5,37,27,35,32,45,16,18,1,14,4,42,24,43,31,21,38,6,34,39,46,20,1,38,47 ]
return get_host_and_guest(dv, de, dc_inc, dc_out)
def get_small_host_and_guest():
dv = [0, 1, 0, 1, 0]
de = [0, 0, 0]
dc_inc = [0, 2, 4]
dc_out = [1, 3, 3]
return get_host_and_guest(dv, de, dc_inc, dc_out)

View file

@ -1,160 +0,0 @@
# 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.
"""
class Properties(object):
"""
Holds all Properties.
"""
def __init__(self):
# member variables:
self.properties = {}
def addProperty(self, name, value):
"""
Adds property (overrides if name already exists).
"""
self.properties[name] = value
def getProperty(self, name):
"""
Returns property with given name or None if not found.
"""
return self.properties.get(name)
class Edge(Properties):
"""
Describes an Edge with source and target Node.
The Edge can have several properties, like a name, a weight, etc...
"""
def __init__(self, src, tgt, str_type=None):
# Call parent class constructor
Properties.__init__(self)
# member variables:
self.src = src
self.tgt = tgt
self.type = str_type
class Vertex(Properties):
"""
Describes a Vertex with incoming, outgoing and undirected (both ways) edges.
The vertex can have several properties, like a name, a weight, etc...
"""
def __init__(self, str_type):
# Call parent class constructor
Properties.__init__(self)
# member variables:
self.incoming_edges = set() # undirected edges should be stored both in
self.outgoing_edges = set() # incoming and outgoing edges
self.type = str_type
def addIncomingEdge(self, edge):
"""
Adds an incoming Edge.
"""
if not isinstance(edge, Edge):
raise TypeError('addIncomingEdge without it being an edge')
self.incoming_edges.add(edge)
def addOutgoingEdge(self, edge):
"""
Adds an outgoing Edge.
"""
if not isinstance(edge, Edge):
raise TypeError('addOutgoingEdge without it being an edge')
self.outgoing_edges.add(edge)
def addUndirectedEdge(self, edge):
"""
Adds an undirected (or bi-directed) Edge.
"""
self.addIncomingEdge(edge)
self.addOutgoingEdge(edge)
class Graph(object):
"""
Holds a Graph.
"""
def __init__(self):
# member variables:
# redundant type keeping, "needed" for fast iterating over specific type
self.vertices = {} # {type, set(v1, v2, ...)}
self.edges = {} # {type, set(e1, e2, ...)}
self.num_vertices = 0
def addCreateVertex(self, str_type):
"""
Creates a Vertex of str_type, stores it and returs it
(so that properties can be added to it).
"""
vertex = Vertex(str_type)
self.addVertex(vertex)
return vertex
def addVertex(self, vertex):
"""
Stores a Vertex into the Graph.
"""
if not isinstance(vertex, Vertex):
raise TypeError('addVertex expects a Vertex')
# add vertex, but it first creates a new set for the vertex type
# if the type does not exist in the dictionary
if vertex not in self.vertices.get(vertex.type, set()):
self.num_vertices += 1
self.vertices.setdefault(vertex.type, set()).add(vertex)
def getVerticesOfType(self, str_type):
"""
Returns all vertices of a specific type,
Return [] if there are no vertices with the given type
"""
return self.vertices.get(str_type, [])
def getEdgesOfType(self, str_type):
"""
Returns all edges of a specific type,
Return [] if there are no edges with the given type
"""
return self.edges.get(str_type, [])
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.
"""
if not isinstance(src, Vertex):
raise TypeError('addCreateEdge: src is not a Vertex')
if not isinstance(tgt, Vertex):
raise TypeError('addCreateEdge: tgt is not a Vertex')
edge = Edge(src, tgt, str_type)
# link vertices connected to this edge
edge.src.addOutgoingEdge(edge)
edge.tgt.addIncomingEdge(edge)
self.addEdge(edge)
return edge
def addEdge(self, edge):
"""
Stores an Edge into the Graph.
"""
if not isinstance(edge, Edge):
raise TypeError('addEdge expects an Edge')
# add edge, but it first creates a new set for the edge type
# if the type does not exist in the dictionary
self.edges.setdefault(edge.type, set()).add(edge)

View file

@ -1,44 +0,0 @@
# 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 as mg
def printGraph(fileName, graph, matched_v={}, matched_e={}):
if not isinstance(graph, mg.Graph):
raise TypeError('Can only print Graph Graphs')
with open(fileName, 'w') as f:
f.write('digraph randomGraph {\n\n')
for str_type, plan_vertices in graph.vertices.items():
for plan_vertex in plan_vertices:
vertex_str = str(id(plan_vertex)) + ' [label="'+str(str_type)+'"'
if plan_vertex in list(matched_v.values()):
vertex_str += ', style=dashed, style=filled]\n'
else:
vertex_str += ']\n'
f.write(vertex_str)
for out_edge in plan_vertex.outgoing_edges:
edge_str = str(id(plan_vertex)) + ' -> ' + str(id(out_edge.tgt)) + ' [label="'+str(out_edge.type)+'"'
if out_edge in list(matched_e.values()):
edge_str += ', style=dashed, penwidth = 4]\n'
else:
edge_str += ']\n'
f.write(edge_str)
f.write('\n}')

View file

@ -1,76 +0,0 @@
# 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 generator import *
from patternMatching import *
import graphToDot
import random
debug = False
if __name__ == '__main__':
"""
The main function called when running from the command line.
"""
random.seed(0)
graph, pattern = get_random_host_and_guest(
nr_vtxs = 10,
nr_vtx_types = 0,
nr_edges = 20,
nr_edge_types = 0,
)
# graph, pattern = get_large_host_and_guest()
# graph, pattern = get_small_host_and_guest()
# override random pattern by copy pasting output from terminal to create
# pattern, paste it in the createConstantPattern function in the generator.py
# pattern = gg.createConstantPattern()
# generate here to know pattern and graph before searching it
graphToDot.printGraph('randomPattern.dot', pattern)
graphToDot.printGraph('randomGraph.dot', graph)
#PM = PatternMatching('naive')
#PM = PatternMatching('SP')
# PM = PatternMatching('Ullmann')
PM = PatternMatching('VF2')
matches = [m for m in PM.matchVF2(pattern, graph)]
print("found", len(matches), "matches:", matches)
# regenerate graph, to show matched pattern
for i, (v,e) in enumerate(matches):
graphToDot.printGraph(f'randomGraph-{i}.dot', graph, v, e)
if debug:
print(len(v))
print('___')
print(v)
for key, value in v.items():
print(value.type)
print(len(e))
print(e)
print('___')
for key, value in e.items():
print(value.type)

View file

@ -1,603 +0,0 @@
# 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 collections
import itertools
class PatternMatching(object):
"""
Returns an occurrence of a given pattern from the given Graph
"""
def __init__(self, optimize=True):
self.optimize = optimize
def matchNaive(self, pattern, vertices, edges, pattern_vertices=None):
"""
Try to find an occurrence of the pattern in the Graph naively.
"""
# print('matchNaive...')
# print('pattern.vertices:', pattern.vertices)
# print('pattern.edges:', pattern.edges)
# print('vertices:', vertices)
# print('edges:', edges)
# print('pattern_vertices:', pattern_vertices)
# allow call with specific arguments
if pattern_vertices == None:
pattern_vertices = pattern.vertices
def visitEdge(pattern_vertices, p_edge, inc, g_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
# print('visitEdge')
"""
Visit a pattern edge, and try to bind it to a graph edge.
(If the first fails, try the second, and so on...)
"""
for g_edge in g_edges:
# only reckon the edge if its in edges and not visited
# (as the graph might be a subgraph of a more complex graph)
if g_edge not in edges.get(g_edge.type, []) or g_edge in visited_g_edges:
continue
if g_edge.type == p_edge.type and g_edge not in visited_g_edges:
visited_p_edges[p_edge] = g_edge
visited_g_edges.add(g_edge)
if inc:
p_vertex = p_edge.src
else:
p_vertex = p_edge.tgt
if visitVertices(pattern_vertices, p_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
return True
# remove added edges if they lead to no match, retry with others
del visited_p_edges[p_edge]
visited_g_edges.remove(g_edge)
# no edge leads to a possitive match
return False
def visitEdges(pattern_vertices, p_edges, inc, g_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
# print('visitEdges')
"""
Visit all edges of the pattern vertex (edges given as argument).
We need to try visiting them for all its permutations, as matching
v -e1-> first and v -e2-> second and v -e3-> third, might not result
in a matching an occurrence of the pattern, but matching v -e2->
first and v -e3-> second and v -e1-> third might.
"""
def removePrevEdge(visitedEdges, visited_p_edges, visited_g_edges):
"""
Undo the binding of the brevious edge, (the current bindinds do
not lead to an occurrence of the pattern in the graph).
"""
for wrong_edge in visitedEdges:
# remove binding (pattern edge to graph edge)
wrong_g_edge = visited_p_edges.get(wrong_edge)
del visited_p_edges[wrong_edge]
# remove visited graph edge
visited_g_edges.remove(wrong_g_edge)
for it in itertools.permutations(p_edges):
visitedEdges = []
foundallEdges = True
for edge in it:
if visited_p_edges.get(edge) == None:
if not visitEdge(pattern_vertices, edge, inc, g_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
# this did not work, so we have to undo all added edges
# (the current edge is not added, as it failed)
# we then can try a different permutation
removePrevEdge(visitedEdges, visited_p_edges, visited_g_edges)
foundallEdges = False
break # try other order
# add good visited (we know it succeeded)
visitedEdges.append(edge)
else:
# we visited this pattern edge, and have the coressponding graph edge
# if it is an incoming pattern edge, we need to make sure that
# the graph target that is map from the pattern target
# (of this incoming pattern edge, which has to be bound at this point)
# has the graph adge as an incoming edge,
# otherwise the graph is not properly connected
if inc:
if not visited_p_edges[edge] in visited_p_vertices[edge.tgt].incoming_edges:
# did not work
removePrevEdge(visitedEdges, visited_p_edges, visited_g_edges)
foundallEdges = False
break # try other order
else:
# analog for an outgoing edge
if not visited_p_edges[edge] in visited_p_vertices[edge.src].outgoing_edges:
# did not work
removePrevEdge(visitedEdges, visited_p_edges, visited_g_edges)
foundallEdges = False
break # try other order
# all edges are good, look no further
if foundallEdges:
break
return foundallEdges
def visitVertex(pattern_vertices, p_vertex, g_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
# print('visitVertex')
"""
Visit a pattern vertex, and try to bind it to the graph vertex
(both are given as argument). A binding is successful if all the
pattern vertex his incoming and outgoing edges can be bound
(to the graph vertex).
"""
if g_vertex in visited_g_vertices:
return False
# save visited graph vertex
visited_g_vertices.add(g_vertex)
# map pattern vertex to visited graph vertex
visited_p_vertices[p_vertex] = g_vertex
if visitEdges(pattern_vertices, p_vertex.incoming_edges, True, g_vertex.incoming_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
if visitEdges(pattern_vertices, p_vertex.outgoing_edges, False, g_vertex.outgoing_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
return True
# cleanup, remove from visited as this does not lead to
# an occurrence of the pttern in the graph
visited_g_vertices.remove(g_vertex)
del visited_p_vertices[p_vertex]
return False
def visitVertices(pattern_vertices, p_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
# print('visitVertices')
"""
Visit a pattern vertex and try to bind a graph vertex to it.
"""
# if already matched or if it is a vertex not in the pattern_vertices
# (second is for when you want to match the pattern partionally)
if visited_p_vertices.get(p_vertex) != None or p_vertex not in pattern_vertices.get(p_vertex.type, set()):
return True
# try visiting graph vertices of same type as pattern vertex
for g_vertex in vertices.get(p_vertex.type, []):
if g_vertex not in visited_g_vertices:
if visitVertex(pattern_vertices, p_vertex, g_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
return True
return False
visited_p_vertices = {}
visited_p_edges = {}
visited_g_vertices = set()
visited_g_edges = set()
# for loop is need for when pattern consists of multiple not connected structures
allVertices = []
for _, p_vertices in pattern_vertices.items():
allVertices.extend(p_vertices)
foundIt = False
for it_p_vertices in itertools.permutations(allVertices):
foundIt = True
for p_vertex in it_p_vertices:
if not visitVertices(pattern_vertices, p_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
foundIt = False
# reset visited
visited_p_vertices = {}
visited_p_edges = {}
visited_g_vertices = set()
visited_g_edges = set()
break
if foundIt:
break
if foundIt:
return (visited_p_vertices, visited_p_edges)
else:
return None
def createAdjacencyMatrixMap(self, graph, pattern):
"""
Return adjacency matrix and the order of the vertices.
"""
# print('createAdjacencyMatrixMap...')
# print('graph:', graph)
# print('pattern:', pattern)
matrix = collections.OrderedDict() # { vertex, (index, [has edge from index to pos?]) }
# contains all vertices we'll use for the AdjacencyMatrix
allVertices = []
if self.optimize:
# insert only the vertices from the graph which have a type
# that is present in the pattern
for vertex_type, _ in pattern.vertices.items():
graph_vertices = graph.vertices.get(vertex_type)
if graph_vertices != None:
allVertices.extend(graph_vertices)
else:
# we will not be able to find the pattern
# as the pattern contains a vertex of a certain type
# that is not present in the host graph
return False
else:
# insert all vertices from the graph
for _, vertices in graph.vertices.items():
allVertices.extend(vertices)
# create squared zero matrix
index = 0
for vertex in allVertices:
matrix[vertex] = (index, [False] * len(allVertices))
index += 1
for _, edges in graph.edges.items():
for edge in edges:
if self.optimize:
if edge.tgt not in matrix or edge.src not in matrix:
# skip adding edge if the target or source type
# is not present in the pattern
# (and therefor not added to the matrix)
continue
index = matrix[edge.tgt][0]
matrix[edge.src][1][index] = True
AM = []
vertices_order = []
for vertex, row in matrix.items():
AM.append(row[1])
vertices_order.append(vertex)
return AM, vertices_order
def matchVF2(self, pattern, graph):
# print('matchVF2...')
# print('pattern:', pattern)
# print('graph:', graph)
class VF2_Obj(object):
"""
Structor for keeping the VF2 data.
"""
def __init__(self, len_graph_vertices, len_pattern_vertices):
# represents if n-the element (h[n] or p[n]) matched
self.host_vtx_is_matched = [False]*len_graph_vertices
self.pattern_vtx_is_matched = [False]*len_pattern_vertices
# save mapping from pattern to graph
self.mapping = {}
self.edge_mapping = {}
# preference lvl 1
# ordered set of vertices adjecent to M_graph connected via an outgoing edge
self.N_out_graph = [-1]*len_graph_vertices
# ordered set of vertices adjecent to M_pattern connected via an outgoing edge
self.N_out_pattern = [-1]*len_pattern_vertices
# preference lvl 2
# ordered set of vertices adjecent to M_graph connected via an incoming edge
self.N_inc_graph = [-1]*len_graph_vertices
# ordered set of vertices adjecent to M_pattern connected via an incoming edge
self.N_inc_pattern = [-1]*len_pattern_vertices
# preference lvl 3
# not in the above
def findM(H, P, h, p, VF2_obj, index_M=0):
"""
Find an isomorphic mapping for the vertices of P to H.
This mapping is represented by a matrix M if,
and only if M(MH)^T = P^T.
This operates in a simular way as Ullmann. Ullmann has a predefind
order for matching (sorted on most edges first). VF2's order is to
first try to match the adjacency vertices connected via outgoing
edges, then thos connected via incoming edges and then those that
not connected to the currently mathed vertices.
"""
def addOutNeighbours(neighbours, N, index_M):
"""
Given outgoing neighbours (a row from an adjacency matrix),
label them as added by saving when they got added (index_M
represents this, otherwise it is -1)
"""
for neighbour_index in range(0, len(neighbours)):
if neighbours[neighbour_index]:
if N[neighbour_index] == -1:
N[neighbour_index] = index_M
def addIncNeighbours(G, j, N, index_M):
"""
Given the adjacency matrix, and the colum j, representing that
we want to add the incoming edges to vertex j,
label them as added by saving when they got added (index_M
represents this, otherwise it is -1)
"""
for i in range(0, len(G)):
if G[i][j]:
if N[i] == -1:
N[i] = index_M
def delNeighbours(N, index_M):
"""
Remove neighbours that where added at index_M.
If we call this function, we are backtracking and we want to
remove the added neighbours from the just tried matching (n, m)
pair (whiched failed).
"""
for n in range(0, len(N)):
if N[n] == index_M:
N[n] = -1
def feasibilityTest(H, P, h, p, VF2_obj, n, m):
"""
Examine all the nodes connected to n and m; if such nodes are
in the current partial mapping, check if each branch from or to
n has a corresponding branch from or to m and vice versa.
If the nodes and the branches of the graphs being matched also
carry semantic attributes, another condition must also hold for
F(s, n, m) to be true; namely the attributes of the nodes and of
the branches being paired must be compatible.
Another pruning step is to check if the nr of ext_edges between
the matched_vertices from the pattern and its adjecent vertices
are less than or equal to the nr of ext_edges between
matched_vertices from the graph and its adjecent vertices.
And if the nr of ext_edges between those adjecent vertices from
the pattern and the not connected vertices are less than or
equal to the nr of ext_edges between those adjecent vertices from
the graph and its adjecent vertices.
"""
# Get all neighbours from graph node n and pattern node m
# (including n and m)
neighbours_graph = {}
neighbours_graph[h[n].type] = set([h[n]])
neighbours_pattern = {}
neighbours_pattern[p[m].type] = set([p[m]])
# add all neihgbours of pattern vertex m
for i in range(0, len(P)): # P is a nxn-matrix
if (P[m][i] or P[i][m]) and VF2_obj.pattern_vtx_is_matched[i]:
neighbours_pattern.setdefault(p[i].type, set()).add(p[i])
# add all neihgbours of graph vertex n
for i in range(0, len(H)): # P is a nxn-matrix
if (H[n][i] or H[i][n]) and VF2_obj.host_vtx_is_matched[i]:
neighbours_graph.setdefault(h[i].type, set()).add(h[i])
# take a coding shortcut,
# use self.matchNaive function to see if it is feasable.
# this way, we immidiatly test the semantic attributes
# print('pattern.vertices', pattern.vertices)
matched = self.matchNaive(pattern, pattern_vertices=neighbours_pattern, vertices=neighbours_graph, edges=graph.edges)
if matched == None:
return False
# count ext_edges from host_vtx_is_matched to a adjecent vertices and
# cuotn ext_edges for adjecent vertices and not matched vertices
# connected via the ext_edges
ext_edges_graph_ca = 0
ext_edges_graph_an = 0
# for all core vertices
for x in range(0, len(VF2_obj.host_vtx_is_matched)):
# for all its neighbours
for y in range(0, len(H)):
if H[x][y]:
# if it is a neighbor and not yet matched
if (VF2_obj.N_out_graph[y] != -1 or VF2_obj.N_inc_graph[y] != -1) and VF2_obj.host_vtx_is_matched[y]:
# if we matched it
if VF2_obj.host_vtx_is_matched[x] != -1:
ext_edges_graph_ca += 1
else:
ext_edges_graph_an += 1
# count ext_edges from pattern_vtx_is_matched to a adjecent vertices
# connected via the ext_edges
ext_edges_pattern_ca = 0
ext_edges_pattern_an = 0
# for all core vertices
for x in range(0, len(VF2_obj.pattern_vtx_is_matched)):
# for all its neighbours
for y in range(0, len(P)):
if P[x][y]:
# if it is a neighbor and not yet matched
if (VF2_obj.N_out_pattern[y] != -1 or VF2_obj.N_inc_pattern[y] != -1) and VF2_obj.pattern_vtx_is_matched[y]:
# if we matched it
if VF2_obj.pattern_vtx_is_matched[x] != -1:
ext_edges_pattern_ca += 1
else:
ext_edges_pattern_an += 1
# The nr of ext_edges between matched_vertices from the pattern
# and its adjecent vertices must be less than or equal to the nr
# of ext_edges between matched_vertices from the graph and its
# adjecent vertices, otherwise we wont find an occurrence
if ext_edges_pattern_ca > ext_edges_graph_ca:
return False
# The nr of ext_edges between those adjancent vertices from the
# pattern and its not connected vertices must be less than or
# equal to the nr of ext_edges between those adjacent vertices
# from the graph and its not connected vertices,
# otherwise we wont find an occurrence
if ext_edges_pattern_an > ext_edges_graph_an:
return False
return matched
def matchPhase(index_M, VF2_obj, n, m):
"""
The matching fase of the VF2 algorithm. If the chosen n, m pair
passes the feasibilityTest, the pair gets added and we start
to search for the next matching pair.
"""
# all candidate pair (n, m) represent graph x pattern
candidate = frozenset(itertools.chain(
((i, j) for i,j in VF2_obj.mapping.items()),
# ((self.reverseMapH[i], self.reverseMapP[j]) for i,j in VF2_obj.mapping.items()),
[(h[n],p[m])],
))
if candidate in self.alreadyVisited:
# print(self.indent*" ", "candidate:", candidate)
# for match in self.alreadyVisited.get(index_M, []):
# if match == candidate:
return False # already visited this (partial) match -> skip
matched = feasibilityTest(H, P, h, p, VF2_obj, n, m)
if matched != False:
# print(self.indent*" ","adding to match:", n, "->", m)
# adapt VF2_obj
VF2_obj.host_vtx_is_matched[n] = True
VF2_obj.pattern_vtx_is_matched[m] = True
VF2_obj.mapping[h[n]] = p[m]
# VF2_obj.edge_mapping
addOutNeighbours(H[n], VF2_obj.N_out_graph, index_M)
addIncNeighbours(H, n, VF2_obj.N_inc_graph, index_M)
addOutNeighbours(P[m], VF2_obj.N_out_pattern, index_M)
addIncNeighbours(P, m, VF2_obj.N_inc_pattern, index_M)
if index_M > 0:
# remember our partial match (shallow copy) so we don't visit it again
self.alreadyVisited.add(frozenset([ (i, j) for i,j in VF2_obj.mapping.items()]))
# self.alreadyVisited.setdefault(index_M, set()).add(frozenset([ (self.reverseMapH[i], self.reverseMapP[j]) for i,j in VF2_obj.mapping.items()]))
# print(self.alreadyVisited)
self.indent += 1
matched = yield from findM(H, P, h, p, VF2_obj, index_M + 1)
if matched:
# return True
# print(self.indent*" ","found match", len(self.results), ", continuing...")
pass
self.indent -= 1
if True:
# else:
# print(self.indent*" ","backtracking... remove", n, "->", m)
# else, backtrack, adapt VF2_obj
VF2_obj.host_vtx_is_matched[n] = False
VF2_obj.pattern_vtx_is_matched[m] = False
del VF2_obj.mapping[h[n]]
delNeighbours(VF2_obj.N_out_graph, index_M)
delNeighbours(VF2_obj.N_inc_graph, index_M)
delNeighbours(VF2_obj.N_out_pattern, index_M)
delNeighbours(VF2_obj.N_inc_pattern, index_M)
return False
def preferred(index_M, VF2_obj, N_graph, N_pattern):
"""
Try to match the adjacency vertices connected via outgoing
or incoming edges. (Depending on what is given for N_graph and
N_pattern.)
"""
for n in range(0, len(N_graph)):
# skip graph vertices that are not in VF2_obj.N_out_graph
# (or already matched)
if N_graph[n] == -1 or VF2_obj.host_vtx_is_matched[n]:
# print(self.indent*" "," skipping")
continue
# print(self.indent*" "," n:", n)
for m in range(0, len(N_pattern)):
# skip graph vertices that are not in VF2_obj.N_out_pattern
# (or already matched)
if N_pattern[m] == -1 or VF2_obj.pattern_vtx_is_matched[m]:
continue
# print(self.indent*" "," m:", m)
matched = yield from matchPhase(index_M, VF2_obj, n, m)
if matched:
return True
return False
def leastPreferred(index_M, VF2_obj):
"""
Try to match the vertices that are not connected to the curretly
matched vertices.
"""
for n in range(0, len(VF2_obj.N_out_graph)):
# skip vertices that are connected to the graph
# (or already matched)
if not (VF2_obj.N_out_graph[n] == -1 and VF2_obj.N_inc_graph[n] == -1) or VF2_obj.host_vtx_is_matched[n]:
# print(self.indent*" "," skipping")
continue
# print(" n:", n)
for m in range(0, len(VF2_obj.N_out_pattern)):
# skip vertices that are connected to the graph
# (or already matched)
if not (VF2_obj.N_out_pattern[m] == -1 and VF2_obj.N_inc_pattern[m] == -1) or VF2_obj.pattern_vtx_is_matched[m]:
# print(self.indent*" "," skipping")
continue
# print(self.indent*" "," m:", m)
matched = yield from matchPhase(index_M, VF2_obj, n, m)
if matched:
return True
return False
# print(self.indent*" ","index_M:", index_M)
# We are at the end, we found an candidate.
if index_M == len(p):
# print(self.indent*" ","end...")
bound_graph_vertices = {}
for vertex_bound, _ in VF2_obj.mapping.items():
bound_graph_vertices.setdefault(vertex_bound.type, set()).add(vertex_bound)
result = self.matchNaive(pattern, vertices=bound_graph_vertices, edges=graph.edges)
if result != None:
yield result
return result != None
if index_M > 0:
# try the candidates is the preffered order
# first try the adjacent vertices connected via the outgoing edges.
# print(self.indent*" ","preferred L1")
matched = yield from preferred(index_M, VF2_obj, VF2_obj.N_out_graph, VF2_obj.N_out_pattern)
if matched:
return True
# print(self.indent*" ","preferred L2")
# then try the adjacent vertices connected via the incoming edges.
matched = yield from preferred(index_M, VF2_obj, VF2_obj.N_inc_graph, VF2_obj.N_inc_pattern)
if matched:
return True
# print(self.indent*" ","leastPreferred")
# and lastly, try the vertices not connected to the currently matched vertices
matched = yield from leastPreferred(index_M, VF2_obj)
if matched:
return True
return False
# create adjacency matrix of the graph
H, h = self.createAdjacencyMatrixMap(graph, pattern)
# create adjacency matrix of the pattern
P, p = self.createAdjacencyMatrixMap(pattern, pattern)
VF2_obj = VF2_Obj(len(h), len(p))
# Only for debugging:
self.indent = 0
self.reverseMapH = { h[i] : i for i in range(len(h))}
self.reverseMapP = { p[i] : i for i in range(len(p))}
# Set of partial matches already explored - prevents us from producing the same match multiple times
# Encoded as a mapping from match size to the partial match
self.alreadyVisited = set()
yield from findM(H, P, h, p, VF2_obj)

View file

@ -1,11 +0,0 @@
#!/bin/sh
rm *.svg
rm *.dot
python main.py
# dot randomGraph.dot -Tsvg > randomGraph.svg
for filename in randomGraph-*.dot; do
dot $filename -Tsvg > $filename.svg
done
firefox *.svg

View file

@ -3,7 +3,7 @@ from uuid import UUID
from services.bottom.V0 import Bottom
from services.scd import SCD
from services.od import OD
from pattern_matching.matcher import Graph, Edge, Vertex, MatcherVF2
from transformation.matcher.matcher import Graph, Edge, Vertex, MatcherVF2
from transformation import ramify
import itertools
import re