Replaced references to depricated Set and ImmutableSet classes with set and frozenset types
This commit is contained in:
parent
74386c1710
commit
fac2609def
|
@ -13,16 +13,22 @@ from graph import Graph
|
||||||
from method import Method, MethodGraph
|
from method import Method, MethodGraph
|
||||||
from diagnostic import diag_print
|
from diagnostic import diag_print
|
||||||
from notify import Notifier
|
from notify import Notifier
|
||||||
from sets import Set, ImmutableSet
|
|
||||||
from multimethod import MultiVariable, MultiMethod
|
from multimethod import MultiVariable, MultiMethod
|
||||||
from cluster import *
|
from cluster import *
|
||||||
from configuration import Configuration
|
from configuration import Configuration
|
||||||
|
from gmatch import gmatch
|
||||||
|
from method import OrMethod
|
||||||
|
|
||||||
# Basic methods
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
# ----------Method classes used by ClusterSolver -------------
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
class ClusterMethod(MultiMethod):
|
class ClusterMethod(MultiMethod):
|
||||||
"""A derive is a method such that a single ouput cluster is a
|
"""A method that determines a single output cluster from a ser of input clusters.
|
||||||
subconsraint of a single input cluster."""
|
Subclasses should ensure that the output cluster satisfies all the constraints
|
||||||
|
in the input clusters.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.consistent = None
|
self.consistent = None
|
||||||
|
@ -144,33 +150,89 @@ class SelectionMethod(MultiMethod):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "SelectionMethod#%d(%s & %s ->%s)"%(id(self),str(self._inputs[0]), str(self._constraints), str(self._outputs[0]))
|
return "SelectionMethod#%d(%s & %s ->%s)"%(id(self),str(self._inputs[0]), str(self._constraints), str(self._outputs[0]))
|
||||||
|
|
||||||
|
# --------------------------------------
|
||||||
|
# helper functions for pattern matching
|
||||||
|
# --------------------------------------
|
||||||
|
|
||||||
|
def pattern2graph(pattern):
|
||||||
|
"""convert pattern to pattern graph"""
|
||||||
|
pgraph = Graph()
|
||||||
|
pgraph.add_vertex("point")
|
||||||
|
pgraph.add_vertex("distance")
|
||||||
|
pgraph.add_vertex("rigid")
|
||||||
|
pgraph.add_vertex("balloon")
|
||||||
|
pgraph.add_vertex("hedgehog")
|
||||||
|
for clpattern in pattern:
|
||||||
|
(pattype, patname, patvars) = clpattern
|
||||||
|
pgraph.add_edge(pattype, patname)
|
||||||
|
for var in patvars:
|
||||||
|
pgraph.add_edge(patname, var)
|
||||||
|
if pattype == "hedgehog":
|
||||||
|
pgraph.add_edge("cvar"+"#"+patname, patvars[0])
|
||||||
|
pgraph.add_edge(patname, "cvar"+"#"+patname)
|
||||||
|
#diag_print("pattern graph:"+str(pgraph),"match");
|
||||||
|
return pgraph
|
||||||
|
|
||||||
|
def reference2graph(nlet):
|
||||||
|
"""convert nlet to reference graph"""
|
||||||
|
rgraph = Graph()
|
||||||
|
rgraph.add_vertex("point")
|
||||||
|
rgraph.add_vertex("distance")
|
||||||
|
rgraph.add_vertex("rigid")
|
||||||
|
rgraph.add_vertex("balloon")
|
||||||
|
rgraph.add_vertex("hedgehog")
|
||||||
|
for cluster in nlet:
|
||||||
|
for var in cluster.vars:
|
||||||
|
rgraph.add_edge(cluster, var)
|
||||||
|
if isinstance(cluster, Rigid):
|
||||||
|
rgraph.add_edge("rigid", cluster)
|
||||||
|
if len(cluster.vars) == 1:
|
||||||
|
rgraph.add_edge("point", cluster)
|
||||||
|
elif len(cluster.vars) == 2:
|
||||||
|
rgraph.add_edge("distance", cluster)
|
||||||
|
if isinstance(cluster, Balloon):
|
||||||
|
rgraph.add_edge("balloon", cluster)
|
||||||
|
if isinstance(cluster, Hedgehog):
|
||||||
|
rgraph.add_edge("hedgehog", cluster)
|
||||||
|
rgraph.add_edge("cvar"+"#"+str(id(cluster)), cluster.cvar)
|
||||||
|
rgraph.add_edge(cluster, "cvar"+"#"+str(id(cluster)))
|
||||||
|
#diag_print("reference graph:"+str(rgraph),"match");
|
||||||
|
return rgraph
|
||||||
|
|
||||||
|
def rootname(cluster):
|
||||||
|
return "root#"+str(id(cluster))
|
||||||
|
|
||||||
|
|
||||||
def is_information_increasing(method):
|
# --------------------------------------------------
|
||||||
infinc = True
|
# ---------- ClusterSolver main class --------------
|
||||||
connected = Set()
|
# --------------------------------------------------
|
||||||
output = method.outputs()[0]
|
|
||||||
for cluster in method.input_clusters():
|
|
||||||
if num_constraints(cluster.intersection(output)) >= num_constraints(output):
|
|
||||||
infinc = False
|
|
||||||
break
|
|
||||||
return infinc
|
|
||||||
|
|
||||||
# ---------- main class --------------
|
|
||||||
|
|
||||||
class ClusterSolver(Notifier):
|
class ClusterSolver(Notifier):
|
||||||
"""Constraints are Clusers: Rigids, Hedgehogs and Balloons.
|
|
||||||
After adding each cluster, the solver tries to merge
|
|
||||||
clusters, adding new clusters and methods between clusters.
|
|
||||||
"""
|
"""
|
||||||
|
Finds a generic solution for problems formulated by Clusters.
|
||||||
|
|
||||||
|
Cluster are added and removed using the add and remove methods.
|
||||||
|
After adding each Cluster, the solver tries to merge it with
|
||||||
|
others, resulting in new Clusters.
|
||||||
|
|
||||||
|
The generic solution is a directed acyclic graph of Clusters and Methods.
|
||||||
|
Particilar problems and solutions are represented by a Configuration
|
||||||
|
for each cluster.
|
||||||
|
|
||||||
|
For each Cluster a set of Configurations can be set using the
|
||||||
|
set method. Configurations are propagated via Methods and can
|
||||||
|
be retrieved with the get method.
|
||||||
|
"""
|
||||||
|
|
||||||
# ------- PUBLIC METHODS --------
|
# ------- PUBLIC METHODS --------
|
||||||
|
|
||||||
def __init__(self, dimension):
|
def __init__(self, dimension, methodclasses):
|
||||||
"""Create a new empty solver"""
|
"""Create a new empty solver"""
|
||||||
# init superclasses
|
# init superclasses
|
||||||
Notifier.__init__(self)
|
Notifier.__init__(self)
|
||||||
# store arguments
|
# store arguments
|
||||||
self.dimension = dimension
|
self.dimension = dimension
|
||||||
|
self.methodclasses = methodclasses
|
||||||
# init instance vars
|
# init instance vars
|
||||||
self._graph = Graph()
|
self._graph = Graph()
|
||||||
self._graph.add_vertex("_root")
|
self._graph.add_vertex("_root")
|
||||||
|
@ -190,6 +252,9 @@ class ClusterSolver(Notifier):
|
||||||
self._mg.set(self._prototype_selection_var, True)
|
self._mg.set(self._prototype_selection_var, True)
|
||||||
# store map of selection_constraints to SelectionMethod (or None)
|
# store map of selection_constraints to SelectionMethod (or None)
|
||||||
self._selection_method = {}
|
self._selection_method = {}
|
||||||
|
# store root cluster (will be assigned when first cluster added)
|
||||||
|
self.rootcluster = None
|
||||||
|
|
||||||
|
|
||||||
def variables(self):
|
def variables(self):
|
||||||
"""get list of variables"""
|
"""get list of variables"""
|
||||||
|
@ -251,14 +316,28 @@ class ClusterSolver(Notifier):
|
||||||
"""Return a set of configurations associated with a cluster"""
|
"""Return a set of configurations associated with a cluster"""
|
||||||
return self._mg.get(cluster)
|
return self._mg.get(cluster)
|
||||||
|
|
||||||
def set_root(self, rigid):
|
def set_root(self, cluster):
|
||||||
"""Make given rigid cluster the root cluster
|
"""Set root cluster, used for positionig and orienting the solutions"""
|
||||||
|
diag_print("set root "+str(self.rootcluster), "clsolver")
|
||||||
|
if self.rootcluster != None:
|
||||||
|
oldrootvar = rootname(self.rootcluster)
|
||||||
|
self._mg.set(oldrootvar, False)
|
||||||
|
newrootvar = rootname(cluster)
|
||||||
|
self._mg.set(newrootvar, True)
|
||||||
|
self.rootcluster = cluster
|
||||||
|
|
||||||
arguments:
|
def get_root(self):
|
||||||
cluster: A Rigid
|
"""returns current root cluster or None"""
|
||||||
"""
|
return self.rootcluster
|
||||||
self._graph.rem_vertex("_root")
|
|
||||||
self._graph.add_edge("_root", rigid)
|
##def set_root(self, rigid):
|
||||||
|
## """Make given rigid cluster the root cluster
|
||||||
|
##
|
||||||
|
## arguments:
|
||||||
|
## cluster: A Rigid
|
||||||
|
## """
|
||||||
|
## self._graph.rem_vertex("_root")
|
||||||
|
## self._graph.add_edge("_root", rigid)
|
||||||
|
|
||||||
def find_dependend(self, object):
|
def find_dependend(self, object):
|
||||||
"""Return a list of objects that depend on given object directly."""
|
"""Return a list of objects that depend on given object directly."""
|
||||||
|
@ -354,6 +433,7 @@ class ClusterSolver(Notifier):
|
||||||
self._add_to_group("_variables", var)
|
self._add_to_group("_variables", var)
|
||||||
|
|
||||||
def _add_cluster(self, cluster):
|
def _add_cluster(self, cluster):
|
||||||
|
# add in appriate way for type
|
||||||
if isinstance(cluster, Rigid):
|
if isinstance(cluster, Rigid):
|
||||||
self._add_rigid(cluster)
|
self._add_rigid(cluster)
|
||||||
elif isinstance(cluster, Hedgehog):
|
elif isinstance(cluster, Hedgehog):
|
||||||
|
@ -362,6 +442,16 @@ class ClusterSolver(Notifier):
|
||||||
self._add_balloon(cluster)
|
self._add_balloon(cluster)
|
||||||
else:
|
else:
|
||||||
raise StandardError, "unsupported type", type(cluster)
|
raise StandardError, "unsupported type", type(cluster)
|
||||||
|
# add root-variable if needed with default value False
|
||||||
|
root = rootname(cluster)
|
||||||
|
if not self._mg.contains(root):
|
||||||
|
self._mg.add_variable(root, False)
|
||||||
|
self._mg.set(root, False)
|
||||||
|
# add root-variable to dependency graph
|
||||||
|
self._add_dependency(cluster, root)
|
||||||
|
# if there is no root cluster, this one will be it
|
||||||
|
if self.get_root() == None:
|
||||||
|
self.set_root(cluster)
|
||||||
|
|
||||||
def _add_rigid(self, newcluster):
|
def _add_rigid(self, newcluster):
|
||||||
"""add a rigid cluster if not already in system"""
|
"""add a rigid cluster if not already in system"""
|
||||||
|
@ -374,9 +464,6 @@ class ClusterSolver(Notifier):
|
||||||
for var in newcluster.vars:
|
for var in newcluster.vars:
|
||||||
self._add_variable(var)
|
self._add_variable(var)
|
||||||
self._add_dependency(var, newcluster)
|
self._add_dependency(var, newcluster)
|
||||||
# if there is no root cluster, this one will be it
|
|
||||||
if len(self._graph.outgoing_vertices("_root")) == 0:
|
|
||||||
self._graph.add_edge("_root", newcluster)
|
|
||||||
# add to top level
|
# add to top level
|
||||||
self._add_top_level(newcluster)
|
self._add_top_level(newcluster)
|
||||||
# add to methodgraph
|
# add to methodgraph
|
||||||
|
@ -472,9 +559,9 @@ class ClusterSolver(Notifier):
|
||||||
def _add_prototype_selector(self, merge):
|
def _add_prototype_selector(self, merge):
|
||||||
incluster = merge.outputs()[0]
|
incluster = merge.outputs()[0]
|
||||||
constraints = merge.prototype_constraints()
|
constraints = merge.prototype_constraints()
|
||||||
vars = Set()
|
vars = set()
|
||||||
for con in constraints:
|
for con in constraints:
|
||||||
vars.union_update(con.variables())
|
vars.update(con.variables())
|
||||||
selclusters = []
|
selclusters = []
|
||||||
for var in vars:
|
for var in vars:
|
||||||
clusters = self._graph.outgoing_vertices(var)
|
clusters = self._graph.outgoing_vertices(var)
|
||||||
|
@ -562,14 +649,140 @@ class ClusterSolver(Notifier):
|
||||||
#end def
|
#end def
|
||||||
|
|
||||||
def _search(self, newcluster):
|
def _search(self, newcluster):
|
||||||
raise StandardError, "Not implemented. ClusterSolver is an abstract class, please use ClusterSolver2D or ClusterSolver3D"
|
diag_print("search from: "+str(newcluster),"clsolver")
|
||||||
|
# find all toplevel clusters connected to newcluster via one or more variables
|
||||||
|
connected = set()
|
||||||
|
for var in newcluster.vars:
|
||||||
|
dependend = self.find_dependend(var)
|
||||||
|
dependend = filter(lambda x: self.is_top_level(x), dependend)
|
||||||
|
connected.update(dependend)
|
||||||
|
diag_print("search: connected clusters="+str(connected),"clsolver")
|
||||||
|
# try applying methods
|
||||||
|
if self._try_method(connected):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _try_method(self, nlet):
|
||||||
|
"""finds a possible rewrite rule applications on given set of clusters, applies it
|
||||||
|
and returns True iff successfull
|
||||||
|
"""
|
||||||
|
refgraph = reference2graph(nlet)
|
||||||
|
for methodclass in self.methodclasses:
|
||||||
|
matches = gmatch(methodclass.patterngraph, refgraph)
|
||||||
|
if len(matches) > 0:
|
||||||
|
diag_print("number of matches = "+str(len(matches)), "clsolver")
|
||||||
|
for s in matches:
|
||||||
|
# diag_print("try match: "+str(s),"clsolver")
|
||||||
|
method = apply(methodclass, [s])
|
||||||
|
succes = self._add_method_complete(method)
|
||||||
|
if succes:
|
||||||
|
#raw_input()
|
||||||
|
#print "press key"
|
||||||
|
return True
|
||||||
|
# end for match
|
||||||
|
# end for method
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _add_method_complete(self, merge):
|
||||||
|
# diag_print("add_method_complete "+str(merge), "clsolver")
|
||||||
|
# check that method has one output
|
||||||
|
if len(merge.outputs()) != 1:
|
||||||
|
raise StandardError, "merge number of outputs != 1"
|
||||||
|
output = merge.outputs()[0]
|
||||||
|
|
||||||
|
# check that the method is information increasing (infinc)
|
||||||
|
infinc = True
|
||||||
|
connected = set()
|
||||||
|
for var in output.vars:
|
||||||
|
dependend = self.find_dependend(var)
|
||||||
|
dependend = filter(lambda x: self.is_top_level(x), dependend)
|
||||||
|
connected.update(dependend)
|
||||||
|
#for cluster in merge.input_clusters():
|
||||||
|
# if cluster in connected:
|
||||||
|
# connected.remove(cluster)
|
||||||
|
|
||||||
|
# NOTE 07-11-2007 (while writing the paper): this implementation of information increasing may not be correct. We may need to check that the total sum of the information in the overlapping clusters is equal to the information in the output.
|
||||||
|
|
||||||
|
for cluster in connected:
|
||||||
|
if num_constraints(cluster.intersection(output)) >= num_constraints(output):
|
||||||
|
infinc = False
|
||||||
|
break
|
||||||
|
diag_print("information increasing:"+str(infinc),"clsolver")
|
||||||
|
|
||||||
|
# check if method reduces number of clusters (reduc)
|
||||||
|
nremove = 0
|
||||||
|
for cluster in merge.input_clusters():
|
||||||
|
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
|
||||||
|
# will be removed from toplevel
|
||||||
|
nremove += 1
|
||||||
|
reduc = (nremove > 1)
|
||||||
|
diag_print("reduce # clusters:"+str(reduc),"clsolver")
|
||||||
|
|
||||||
|
# check if the method is redundant
|
||||||
|
if not infinc and not reduc:
|
||||||
|
diag_print("method is redundant","clsolver")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# check consistency and local/global overconstrained
|
||||||
|
consistent = True
|
||||||
|
local_oc = False
|
||||||
|
for i1 in range(0, len(merge.input_clusters())):
|
||||||
|
for i2 in range(i1+1, len(merge.input_clusters())):
|
||||||
|
c1 = merge.input_clusters()[i1]
|
||||||
|
c2 = merge.input_clusters()[i2]
|
||||||
|
if num_constraints(c1.intersection(c2)) != 0:
|
||||||
|
local_oc = True
|
||||||
|
consistent = consistent and self._is_consistent_pair(c1, c2)
|
||||||
|
merge.consistent = consistent
|
||||||
|
merge.overconstrained = local_oc
|
||||||
|
# global overconstrained? (store in output cluster)
|
||||||
|
overconstrained = not consistent
|
||||||
|
for cluster in merge.input_clusters():
|
||||||
|
overconstrained = overconstrained or cluster.overconstrained
|
||||||
|
output.overconstrained = overconstrained
|
||||||
|
# add to graph
|
||||||
|
self._add_cluster(output)
|
||||||
|
self._add_method(merge)
|
||||||
|
# remove input clusters from top_level
|
||||||
|
merge.restore_toplevel = [] # make restore list in method
|
||||||
|
for cluster in merge.input_clusters():
|
||||||
|
# do not remove rigids from toplevel if method does not consider root
|
||||||
|
if isinstance(cluster, Rigid):
|
||||||
|
if hasattr(merge,"noremove") and merge.noremove == True:
|
||||||
|
continue
|
||||||
|
# remove input clusters when all its constraints are in output cluster
|
||||||
|
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
|
||||||
|
diag_print("remove from top-level: "+str(cluster),"clsolver")
|
||||||
|
self._rem_top_level(cluster)
|
||||||
|
merge.restore_toplevel.append(cluster)
|
||||||
|
else:
|
||||||
|
diag_print("keep top-level: "+str(cluster),"clsolver")
|
||||||
|
# add method to determine root-variable
|
||||||
|
self._add_root_method(merge.input_clusters(),merge.outputs()[0])
|
||||||
|
# add solution selection methods
|
||||||
|
output2 = self._add_prototype_selector(merge)
|
||||||
|
output3 = self._add_solution_selector(output2)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _add_root_method(self,inclusters,outcluster):
|
||||||
|
inroots = []
|
||||||
|
for cluster in inclusters:
|
||||||
|
inroots.append(rootname(cluster))
|
||||||
|
outroot = rootname(outcluster)
|
||||||
|
method = OrMethod(inroots, outroot)
|
||||||
|
# add method
|
||||||
|
self._add_method(method)
|
||||||
|
# make sure its deleted when cluster is deleted
|
||||||
|
self._add_dependency(outcluster, method)
|
||||||
|
|
||||||
|
|
||||||
# -- removing objects
|
# -- removing objects
|
||||||
|
|
||||||
def _remove(self, object):
|
def _remove(self, object):
|
||||||
# find all indirectly dependend objects
|
# find all indirectly dependend objects
|
||||||
todelete = [object]+self._find_descendend(object)
|
todelete = [object]+self._find_descendend(object)
|
||||||
torestore = Set()
|
torestore = set()
|
||||||
# remove all objects
|
# remove all objects
|
||||||
for item in todelete:
|
for item in todelete:
|
||||||
# if merge removed items from toplevel then add them back to top level
|
# if merge removed items from toplevel then add them back to top level
|
||||||
|
@ -605,38 +818,36 @@ class ClusterSolver(Notifier):
|
||||||
self._process_new()
|
self._process_new()
|
||||||
|
|
||||||
|
|
||||||
# --- root selection
|
##def _contains_root(self, input_cluster):
|
||||||
|
## """returns True iff input_cluster is root cluster or was determined by
|
||||||
def _contains_root(self, input_cluster):
|
## merging with the root cluster."""
|
||||||
"""returns True iff input_cluster is root cluster or was determined by
|
##
|
||||||
merging with the root cluster."""
|
## # start from root cluster. Follow merges upwards until:
|
||||||
|
## # - input cluster found -> True
|
||||||
# start from root cluster. Follow merges upwards until:
|
## # - no more merges -> False
|
||||||
# - input cluster found -> True
|
##
|
||||||
# - no more merges -> False
|
## if len(self._graph.outgoing_vertices("_root")) > 1:
|
||||||
|
## raise StandardError, "more than one root cluster"
|
||||||
if len(self._graph.outgoing_vertices("_root")) > 1:
|
## if len(self._graph.outgoing_vertices("_root")) == 1:
|
||||||
raise StandardError, "more than one root cluster"
|
## cluster = self._graph.outgoing_vertices("_root")[0]
|
||||||
if len(self._graph.outgoing_vertices("_root")) == 1:
|
## else:
|
||||||
cluster = self._graph.outgoing_vertices("_root")[0]
|
## cluster = None
|
||||||
else:
|
## while (cluster != None):
|
||||||
cluster = None
|
## if cluster is input_cluster:
|
||||||
while (cluster != None):
|
## return True
|
||||||
if cluster is input_cluster:
|
## fr = self._graph.outgoing_vertices(cluster)
|
||||||
return True
|
## me = filter(lambda x: isinstance(x, Merge), fr)
|
||||||
fr = self._graph.outgoing_vertices(cluster)
|
## me = filter(lambda x: cluster in x.outputs(), me)
|
||||||
me = filter(lambda x: isinstance(x, Merge), fr)
|
## if len(me) > 1:
|
||||||
me = filter(lambda x: cluster in x.outputs(), me)
|
## raise StandardError, "root cluster merged more than once"
|
||||||
if len(me) > 1:
|
## elif len(me) == 0:
|
||||||
raise StandardError, "root cluster merged more than once"
|
## cluster = None
|
||||||
elif len(me) == 0:
|
## elif len(me[0].outputs()) != 1:
|
||||||
cluster = None
|
## raise StandardError, "a merge with number of outputs != 1"
|
||||||
elif len(me[0].outputs()) != 1:
|
## else:
|
||||||
raise StandardError, "a merge with number of outputs != 1"
|
## cluster = me[0].outputs()[0]
|
||||||
else:
|
## #while
|
||||||
cluster = me[0].outputs()[0]
|
## return False
|
||||||
#while
|
|
||||||
return False
|
|
||||||
#def
|
#def
|
||||||
|
|
||||||
# ---- consistency
|
# ---- consistency
|
||||||
|
@ -848,7 +1059,7 @@ class ClusterSolver(Notifier):
|
||||||
## dependend.append(obj)
|
## dependend.append(obj)
|
||||||
## candidates = filter(lambda x: self._contains_distance(x, distance), dependend)
|
## candidates = filter(lambda x: self._contains_distance(x, distance), dependend)
|
||||||
## # determine sources, i.e. clusters created from clusters that do not contain the distance
|
## # determine sources, i.e. clusters created from clusters that do not contain the distance
|
||||||
## sources = Set()
|
## sources = set()
|
||||||
## for c1 in candidates:
|
## for c1 in candidates:
|
||||||
## methods = filter(lambda v: isinstance(v, Method), self._graph.ingoing_vertices(c1))
|
## methods = filter(lambda v: isinstance(v, Method), self._graph.ingoing_vertices(c1))
|
||||||
## if len(methods) == 0:
|
## if len(methods) == 0:
|
||||||
|
@ -868,7 +1079,7 @@ class ClusterSolver(Notifier):
|
||||||
## for source in sources:
|
## for source in sources:
|
||||||
## diag_print(str(source), "clsolver")
|
## diag_print(str(source), "clsolver")
|
||||||
## # filter sources for dependencies
|
## # filter sources for dependencies
|
||||||
## #unfiltered = Set(sources)
|
## #unfiltered = set(sources)
|
||||||
## #for s1 in unfiltered:
|
## #for s1 in unfiltered:
|
||||||
## # if s1 not in sources: continue
|
## # if s1 not in sources: continue
|
||||||
## # descendants = self._find_descendants(s1)
|
## # descendants = self._find_descendants(s1)
|
||||||
|
@ -889,7 +1100,7 @@ class ClusterSolver(Notifier):
|
||||||
## dependend.append(obj)
|
## dependend.append(obj)
|
||||||
## candidates = filter(lambda x: self._contains_angle(x, angle), dependend)
|
## candidates = filter(lambda x: self._contains_angle(x, angle), dependend)
|
||||||
## # determine sources, i.e. clusters created from clusters that do not contain the angle
|
## # determine sources, i.e. clusters created from clusters that do not contain the angle
|
||||||
## sources = Set()
|
## sources = set()
|
||||||
## for c1 in candidates:
|
## for c1 in candidates:
|
||||||
## methods = filter(lambda v: isinstance(v, Method), self._graph.ingoing_vertices(c1))
|
## methods = filter(lambda v: isinstance(v, Method), self._graph.ingoing_vertices(c1))
|
||||||
## if len(methods) == 0:
|
## if len(methods) == 0:
|
||||||
|
@ -912,8 +1123,8 @@ class ClusterSolver(Notifier):
|
||||||
|
|
||||||
##def _roots(self,object):
|
##def _roots(self,object):
|
||||||
## front = [object]
|
## front = [object]
|
||||||
## result = Set()
|
## result = set()
|
||||||
## done = Set()
|
## done = set()
|
||||||
## while len(front) > 0:
|
## while len(front) > 0:
|
||||||
## x = front.pop()
|
## x = front.pop()
|
||||||
## if x not in done:
|
## if x not in done:
|
||||||
|
@ -930,14 +1141,14 @@ class ClusterSolver(Notifier):
|
||||||
|
|
||||||
##def _all_sources_constraint_in_cluster(self, constraint, cluster):
|
##def _all_sources_constraint_in_cluster(self, constraint, cluster):
|
||||||
## if not self._contains_constraint(cluster, constraint):
|
## if not self._contains_constraint(cluster, constraint):
|
||||||
## return Set()
|
## return set()
|
||||||
## elif self._is_atomic(cluster):
|
## elif self._is_atomic(cluster):
|
||||||
## return Set([cluster])
|
## return set([cluster])
|
||||||
## else:
|
## else:
|
||||||
## method = self._determining_method(cluster)
|
## method = self._determining_method(cluster)
|
||||||
## sources = Set()
|
## sources = set()
|
||||||
## for inp in method.input_clusters():
|
## for inp in method.input_clusters():
|
||||||
## sources.union_update(self._all_sources_constraint_in_cluster(constraint, inp))
|
## sources.update(self._all_sources_constraint_in_cluster(constraint, inp))
|
||||||
## return sources
|
## return sources
|
||||||
|
|
||||||
# class ClusterSolver
|
# class ClusterSolver
|
||||||
|
|
|
@ -1,251 +1,21 @@
|
||||||
"""A generic 3D geometric constraint solver"""
|
"""A generic 3D geometric constraint solver"""
|
||||||
|
|
||||||
from clsolver import *
|
from clsolver import *
|
||||||
from sets import Set
|
|
||||||
from diagnostic import diag_print, diag_select
|
from diagnostic import diag_print, diag_select
|
||||||
from selconstr import *
|
from selconstr import *
|
||||||
from intersections import *
|
from intersections import *
|
||||||
from configuration import Configuration
|
from configuration import Configuration
|
||||||
from cluster import *
|
from cluster import *
|
||||||
from map import Map
|
from map import Map
|
||||||
from gmatch import gmatch
|
|
||||||
from method import OrMethod
|
|
||||||
|
|
||||||
def pattern2graph(pattern):
|
|
||||||
"""convert pattern to pattern graph"""
|
|
||||||
pgraph = Graph()
|
|
||||||
pgraph.add_vertex("point")
|
|
||||||
pgraph.add_vertex("distance")
|
|
||||||
pgraph.add_vertex("rigid")
|
|
||||||
pgraph.add_vertex("balloon")
|
|
||||||
pgraph.add_vertex("hedgehog")
|
|
||||||
for clpattern in pattern:
|
|
||||||
(pattype, patname, patvars) = clpattern
|
|
||||||
pgraph.add_edge(pattype, patname)
|
|
||||||
for var in patvars:
|
|
||||||
pgraph.add_edge(patname, var)
|
|
||||||
if pattype == "hedgehog":
|
|
||||||
pgraph.add_edge("cvar"+"#"+patname, patvars[0])
|
|
||||||
pgraph.add_edge(patname, "cvar"+"#"+patname)
|
|
||||||
#diag_print("pattern graph:"+str(pgraph),"match");
|
|
||||||
return pgraph
|
|
||||||
|
|
||||||
def reference2graph(nlet):
|
|
||||||
"""convert nlet to reference graph"""
|
|
||||||
rgraph = Graph()
|
|
||||||
rgraph.add_vertex("point")
|
|
||||||
rgraph.add_vertex("distance")
|
|
||||||
rgraph.add_vertex("rigid")
|
|
||||||
rgraph.add_vertex("balloon")
|
|
||||||
rgraph.add_vertex("hedgehog")
|
|
||||||
for cluster in nlet:
|
|
||||||
for var in cluster.vars:
|
|
||||||
rgraph.add_edge(cluster, var)
|
|
||||||
if isinstance(cluster, Rigid):
|
|
||||||
rgraph.add_edge("rigid", cluster)
|
|
||||||
if len(cluster.vars) == 1:
|
|
||||||
rgraph.add_edge("point", cluster)
|
|
||||||
elif len(cluster.vars) == 2:
|
|
||||||
rgraph.add_edge("distance", cluster)
|
|
||||||
if isinstance(cluster, Balloon):
|
|
||||||
rgraph.add_edge("balloon", cluster)
|
|
||||||
if isinstance(cluster, Hedgehog):
|
|
||||||
rgraph.add_edge("hedgehog", cluster)
|
|
||||||
rgraph.add_edge("cvar"+"#"+str(id(cluster)), cluster.cvar)
|
|
||||||
rgraph.add_edge(cluster, "cvar"+"#"+str(id(cluster)))
|
|
||||||
#diag_print("reference graph:"+str(rgraph),"match");
|
|
||||||
return rgraph
|
|
||||||
|
|
||||||
def rootname(cluster):
|
|
||||||
return "root#"+str(id(cluster))
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterSolver3D(ClusterSolver):
|
class ClusterSolver3D(ClusterSolver):
|
||||||
"""A generic 3D geometric constraint solver.
|
"""A generic 3D geometric constraint solver. See ClusterSolver for details."""
|
||||||
|
|
||||||
Finds a generic solution for problems formulated by cluster-constraints.
|
|
||||||
|
|
||||||
Constraints are Clusers: Rigids, Hedgehogs and Balloons.
|
|
||||||
Cluster are added and removed using the add and remove methods.
|
|
||||||
After adding each Cluster, the solver tries to merge it with
|
|
||||||
other clusters, resulting in new Clusters and Methods.
|
|
||||||
|
|
||||||
The generic solution is a directed acyclic graph of Clusters and Methods.
|
|
||||||
Particilar problems and solutions are represented by a Configuration
|
|
||||||
for each cluster.
|
|
||||||
|
|
||||||
For each Cluster a set of Configurations can be set using the
|
|
||||||
set method. Configurations are propagated via Methods and can
|
|
||||||
be retrieved with the get method.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ------- PUBLIC METHODS --------
|
# ------- PUBLIC METHODS --------
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Instantiate a ClusterSolver3D"""
|
"""Instantiate a ClusterSolver3D"""
|
||||||
ClusterSolver.__init__(self, 3)
|
ClusterSolver.__init__(self, 3, [MergePR, MergeDR, MergeRR, MergeSR, DeriveTTD, DeriveDDD, DeriveADD, DeriveDAD, DeriveAA])
|
||||||
self.rootcluster = None
|
|
||||||
|
|
||||||
# overriding ClusterSolver.set_root
|
|
||||||
def set_root(self, cluster):
|
|
||||||
"""Set root cluster, used for positionig and orienting the solutions"""
|
|
||||||
diag_print("set root "+str(self.rootcluster), "clsolver3D")
|
|
||||||
if self.rootcluster != None:
|
|
||||||
oldrootvar = rootname(self.rootcluster)
|
|
||||||
self._mg.set(oldrootvar, False)
|
|
||||||
newrootvar = rootname(cluster)
|
|
||||||
self._mg.set(newrootvar, True)
|
|
||||||
self.rootcluster = cluster
|
|
||||||
|
|
||||||
|
|
||||||
# ------------ INTERNALLY USED METHODS --------
|
|
||||||
|
|
||||||
|
|
||||||
# --------------
|
|
||||||
# search methods
|
|
||||||
# --------------
|
|
||||||
|
|
||||||
def _search(self, newcluster):
|
|
||||||
diag_print("search from: "+str(newcluster),"clsolver3D")
|
|
||||||
# find all toplevel clusters connected to newcluster via one or more variables
|
|
||||||
connected = Set()
|
|
||||||
for var in newcluster.vars:
|
|
||||||
dependend = self.find_dependend(var)
|
|
||||||
dependend = filter(lambda x: self.is_top_level(x), dependend)
|
|
||||||
connected.union_update(dependend)
|
|
||||||
diag_print("search: connected clusters="+str(connected),"clsolver3D")
|
|
||||||
# try applying methods
|
|
||||||
if self._try_method(connected):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _try_method(self, nlet):
|
|
||||||
"""finds a possible rewrite rule applications on given set of clusters, applies it
|
|
||||||
and returns True iff successfull
|
|
||||||
"""
|
|
||||||
refgraph = reference2graph(nlet)
|
|
||||||
for methodclass in [MergePR, MergeDR, MergeRR, MergeSR, DeriveTTD, DeriveDDD, DeriveADD, DeriveDAD, DeriveAA]:
|
|
||||||
matches = gmatch(methodclass.patterngraph, refgraph)
|
|
||||||
if len(matches) > 0:
|
|
||||||
diag_print("number of matches = "+str(len(matches)), "clsolver3D")
|
|
||||||
for s in matches:
|
|
||||||
# diag_print("try match: "+str(s),"clsolver3D")
|
|
||||||
method = apply(methodclass, [s])
|
|
||||||
succes = self._add_method_complete(method)
|
|
||||||
if succes:
|
|
||||||
#raw_input()
|
|
||||||
#print "press key"
|
|
||||||
return True
|
|
||||||
# end for match
|
|
||||||
# end for method
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _add_method_complete(self, merge):
|
|
||||||
# diag_print("add_method_complete "+str(merge), "clsolver3D")
|
|
||||||
# check that method has one output
|
|
||||||
if len(merge.outputs()) != 1:
|
|
||||||
raise StandardError, "merge number of outputs != 1"
|
|
||||||
output = merge.outputs()[0]
|
|
||||||
|
|
||||||
# check that the method is information increasing (infinc)
|
|
||||||
infinc = True
|
|
||||||
connected = Set()
|
|
||||||
for var in output.vars:
|
|
||||||
dependend = self.find_dependend(var)
|
|
||||||
dependend = filter(lambda x: self.is_top_level(x), dependend)
|
|
||||||
connected.union_update(dependend)
|
|
||||||
#for cluster in merge.input_clusters():
|
|
||||||
# if cluster in connected:
|
|
||||||
# connected.remove(cluster)
|
|
||||||
|
|
||||||
# NOTE 07-11-2007 (while writing the paper): this implementation of information increasing may not be correct. We may need to check that the total sum of the information in the overlapping clusters is equal to the information in the output.
|
|
||||||
|
|
||||||
for cluster in connected:
|
|
||||||
if num_constraints(cluster.intersection(output)) >= num_constraints(output):
|
|
||||||
infinc = False
|
|
||||||
break
|
|
||||||
diag_print("information increasing:"+str(infinc),"clsolver3D")
|
|
||||||
|
|
||||||
# check if method reduces number of clusters (reduc)
|
|
||||||
nremove = 0
|
|
||||||
for cluster in merge.input_clusters():
|
|
||||||
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
|
|
||||||
# will be removed from toplevel
|
|
||||||
nremove += 1
|
|
||||||
reduc = (nremove > 1)
|
|
||||||
diag_print("reduce # clusters:"+str(reduc),"clsolver3D")
|
|
||||||
|
|
||||||
# check if the method is redundant
|
|
||||||
if not infinc and not reduc:
|
|
||||||
diag_print("method is redundant","clsolver3D")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# check consistency and local/global overconstrained
|
|
||||||
consistent = True
|
|
||||||
local_oc = False
|
|
||||||
for i1 in range(0, len(merge.input_clusters())):
|
|
||||||
for i2 in range(i1+1, len(merge.input_clusters())):
|
|
||||||
c1 = merge.input_clusters()[i1]
|
|
||||||
c2 = merge.input_clusters()[i2]
|
|
||||||
if num_constraints(c1.intersection(c2)) != 0:
|
|
||||||
local_oc = True
|
|
||||||
consistent = consistent and self._is_consistent_pair(c1, c2)
|
|
||||||
merge.consistent = consistent
|
|
||||||
merge.overconstrained = local_oc
|
|
||||||
# global overconstrained? (store in output cluster)
|
|
||||||
overconstrained = not consistent
|
|
||||||
for cluster in merge.input_clusters():
|
|
||||||
overconstrained = overconstrained or cluster.overconstrained
|
|
||||||
output.overconstrained = overconstrained
|
|
||||||
# add to graph
|
|
||||||
self._add_cluster(output)
|
|
||||||
self._add_method(merge)
|
|
||||||
# remove input clusters from top_level
|
|
||||||
merge.restore_toplevel = [] # make restore list in method
|
|
||||||
for cluster in merge.input_clusters():
|
|
||||||
# do not remove rigids from toplevel if method does not consider root
|
|
||||||
if isinstance(cluster, Rigid):
|
|
||||||
if hasattr(merge,"noremove") and merge.noremove == True:
|
|
||||||
continue
|
|
||||||
# remove input clusters when all its constraints are in output cluster
|
|
||||||
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
|
|
||||||
diag_print("remove from top-level: "+str(cluster),"clsolver3D")
|
|
||||||
self._rem_top_level(cluster)
|
|
||||||
merge.restore_toplevel.append(cluster)
|
|
||||||
else:
|
|
||||||
diag_print("keep top-level: "+str(cluster),"clsolver3D")
|
|
||||||
# add method to determine root-variable
|
|
||||||
self._add_root_method(merge.input_clusters(),merge.outputs()[0])
|
|
||||||
# add solution selection methods
|
|
||||||
output2 = self._add_prototype_selector(merge)
|
|
||||||
output3 = self._add_solution_selector(output2)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _add_root_method(self,inclusters,outcluster):
|
|
||||||
inroots = []
|
|
||||||
for cluster in inclusters:
|
|
||||||
inroots.append(rootname(cluster))
|
|
||||||
outroot = rootname(outcluster)
|
|
||||||
method = OrMethod(inroots, outroot)
|
|
||||||
# add method
|
|
||||||
self._add_method(method)
|
|
||||||
# make sure its deleted when cluster is deleted
|
|
||||||
self._add_dependency(outcluster, method)
|
|
||||||
|
|
||||||
# overriding superclass function
|
|
||||||
def _add_cluster(self, cluster):
|
|
||||||
# call superclass function
|
|
||||||
ClusterSolver._add_cluster(self, cluster)
|
|
||||||
# add root-variable if needed with default value False
|
|
||||||
root = rootname(cluster)
|
|
||||||
if not self._mg.contains(root):
|
|
||||||
self._mg.add_variable(root, False)
|
|
||||||
self._mg.set(root, False)
|
|
||||||
# add root-variable to dependency graph
|
|
||||||
self._add_dependency(cluster, root)
|
|
||||||
|
|
||||||
# class ClusterSolver3D
|
|
||||||
|
|
||||||
# ----------------------------------------------
|
# ----------------------------------------------
|
||||||
# ---------- Methods for 3D solving -------------
|
# ---------- Methods for 3D solving -------------
|
||||||
|
@ -261,7 +31,7 @@ class MergePR(ClusterMethod):
|
||||||
in1 = map["$p"]
|
in1 = map["$p"]
|
||||||
in2 = map["$r"]
|
in2 = map["$r"]
|
||||||
# create ouput
|
# create ouput
|
||||||
outvars = Set(in1.vars).union(in2.vars)
|
outvars = set(in1.vars).union(in2.vars)
|
||||||
out = Rigid(outvars)
|
out = Rigid(outvars)
|
||||||
# set method properties
|
# set method properties
|
||||||
in1root = rootname(in1)
|
in1root = rootname(in1)
|
||||||
|
@ -304,7 +74,7 @@ class MergeDR(ClusterMethod):
|
||||||
in1 = map["$d"]
|
in1 = map["$d"]
|
||||||
in2 = map["$r"]
|
in2 = map["$r"]
|
||||||
# create ouput
|
# create ouput
|
||||||
outvars = Set(in1.vars).union(in2.vars)
|
outvars = set(in1.vars).union(in2.vars)
|
||||||
out = Rigid(outvars)
|
out = Rigid(outvars)
|
||||||
# set method properties
|
# set method properties
|
||||||
in1root = rootname(in1)
|
in1root = rootname(in1)
|
||||||
|
@ -347,7 +117,7 @@ class MergeRR(ClusterMethod):
|
||||||
in1 = map["$r1"]
|
in1 = map["$r1"]
|
||||||
in2 = map["$r2"]
|
in2 = map["$r2"]
|
||||||
# create output
|
# create output
|
||||||
out = Rigid(Set(in1.vars).union(in2.vars))
|
out = Rigid(set(in1.vars).union(in2.vars))
|
||||||
# set method parameters
|
# set method parameters
|
||||||
in1root = rootname(in1)
|
in1root = rootname(in1)
|
||||||
in2root = rootname(in2)
|
in2root = rootname(in2)
|
||||||
|
@ -636,7 +406,7 @@ class MergeSR(ClusterMethod):
|
||||||
in1 = map["$r"]
|
in1 = map["$r"]
|
||||||
in2 = map["$s"]
|
in2 = map["$s"]
|
||||||
# create output
|
# create output
|
||||||
out = Rigid(Set(in2.vars))
|
out = Rigid(set(in2.vars))
|
||||||
# set method parameters
|
# set method parameters
|
||||||
self._inputs = [in1, in2]
|
self._inputs = [in1, in2]
|
||||||
self._outputs = [out]
|
self._outputs = [out]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Clusters are generalised constraints on sets of points in R^n. Cluster
|
"""Clusters are generalised constraints on sets of points in R^n. Cluster
|
||||||
types are Rigids, Hedgehogs and Balloons. """
|
types are Rigids, Hedgehogs and Balloons. """
|
||||||
|
|
||||||
from sets import Set, ImmutableSet
|
|
||||||
from multimethod import MultiVariable
|
from multimethod import MultiVariable
|
||||||
|
|
||||||
class Distance:
|
class Distance:
|
||||||
|
@ -23,11 +22,11 @@ class Distance:
|
||||||
+str(self.vars[1])+")"
|
+str(self.vars[1])+")"
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(ImmutableSet(self.vars))
|
return hash(frozenset(self.vars))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, Distance):
|
if isinstance(other, Distance):
|
||||||
return ImmutableSet(self.vars) == ImmutableSet(other.vars)
|
return frozenset(self.vars) == frozenset(other.vars)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -47,13 +46,13 @@ class Angle:
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, Angle):
|
if isinstance(other, Angle):
|
||||||
return self.vars[2] == other.vars[2] and ImmutableSet(self.vars) == ImmutableSet(other.vars)
|
return self.vars[2] == other.vars[2] and frozenset(self.vars) == frozenset(other.vars)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(ImmutableSet(self.vars))
|
return hash(frozenset(self.vars))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "ang("\
|
return "ang("\
|
||||||
|
@ -72,7 +71,7 @@ class Cluster(MultiVariable):
|
||||||
self.creationtime = Cluster.staticcounter
|
self.creationtime = Cluster.staticcounter
|
||||||
|
|
||||||
def intersection(self, other):
|
def intersection(self, other):
|
||||||
shared = Set(self.vars).intersection(other.vars)
|
shared = set(self.vars).intersection(other.vars)
|
||||||
# note, a one point cluster is never returned
|
# note, a one point cluster is never returned
|
||||||
#because it is not a constraint
|
#because it is not a constraint
|
||||||
if len(shared) < 2:
|
if len(shared) < 2:
|
||||||
|
@ -89,7 +88,7 @@ class Cluster(MultiVariable):
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
elif isinstance(other, Hedgehog):
|
elif isinstance(other, Hedgehog):
|
||||||
xvars = Set(shared) - Set([other.cvar])
|
xvars = set(shared) - set([other.cvar])
|
||||||
if other.cvar in self.vars and len(xvars) >= 2:
|
if other.cvar in self.vars and len(xvars) >= 2:
|
||||||
return Hedgehog(other.cvar,xvars)
|
return Hedgehog(other.cvar,xvars)
|
||||||
else:
|
else:
|
||||||
|
@ -101,20 +100,20 @@ class Cluster(MultiVariable):
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
elif isinstance(other, Hedgehog):
|
elif isinstance(other, Hedgehog):
|
||||||
xvars = Set(shared) - Set([other.cvar])
|
xvars = set(shared) - set([other.cvar])
|
||||||
if other.cvar in self.vars and len(xvars) >= 2:
|
if other.cvar in self.vars and len(xvars) >= 2:
|
||||||
return Hedgehog(other.cvar,xvars)
|
return Hedgehog(other.cvar,xvars)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
elif isinstance(self, Hedgehog):
|
elif isinstance(self, Hedgehog):
|
||||||
if isinstance(other, Rigid) or isinstance(other, Balloon):
|
if isinstance(other, Rigid) or isinstance(other, Balloon):
|
||||||
xvars = Set(shared) - Set([self.cvar])
|
xvars = set(shared) - set([self.cvar])
|
||||||
if self.cvar in other.vars and len(xvars) >= 2:
|
if self.cvar in other.vars and len(xvars) >= 2:
|
||||||
return Hedgehog(self.cvar,xvars)
|
return Hedgehog(self.cvar,xvars)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
elif isinstance(other, Hedgehog):
|
elif isinstance(other, Hedgehog):
|
||||||
xvars = Set(self.xvars).intersection(other.xvars)
|
xvars = set(self.xvars).intersection(other.xvars)
|
||||||
if self.cvar == other.cvar and len(xvars) >= 2:
|
if self.cvar == other.cvar and len(xvars) >= 2:
|
||||||
return Hedgehog(self.cvar,xvars)
|
return Hedgehog(self.cvar,xvars)
|
||||||
else:
|
else:
|
||||||
|
@ -134,7 +133,7 @@ class Rigid(Cluster):
|
||||||
vars - list of variables
|
vars - list of variables
|
||||||
"""
|
"""
|
||||||
Cluster.__init__(self)
|
Cluster.__init__(self)
|
||||||
self.vars = ImmutableSet(vars)
|
self.vars = frozenset(vars)
|
||||||
self.overconstrained = False
|
self.overconstrained = False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -165,7 +164,7 @@ class Hedgehog(Cluster):
|
||||||
self.cvar = cvar
|
self.cvar = cvar
|
||||||
if len(xvars) < 2:
|
if len(xvars) < 2:
|
||||||
raise StandardError, "hedgehog must have at least three variables"
|
raise StandardError, "hedgehog must have at least three variables"
|
||||||
self.xvars = ImmutableSet(xvars)
|
self.xvars = frozenset(xvars)
|
||||||
self.vars = self.xvars.union([self.cvar])
|
self.vars = self.xvars.union([self.cvar])
|
||||||
self.overconstrained = False
|
self.overconstrained = False
|
||||||
|
|
||||||
|
@ -194,7 +193,7 @@ class Balloon(Cluster):
|
||||||
Cluster.__init__(self)
|
Cluster.__init__(self)
|
||||||
if len(variables) < 3:
|
if len(variables) < 3:
|
||||||
raise StandardError, "balloon must have at least three variables"
|
raise StandardError, "balloon must have at least three variables"
|
||||||
self.vars = ImmutableSet(variables)
|
self.vars = frozenset(variables)
|
||||||
self.overconstrained = False
|
self.overconstrained = False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -240,10 +239,10 @@ def over_angles(c1, c2):
|
||||||
def over_distances(c1, c2):
|
def over_distances(c1, c2):
|
||||||
"""determine set of distances in c1 and c2"""
|
"""determine set of distances in c1 and c2"""
|
||||||
if not (isinstance(c1, Rigid) and isinstance(c2, Rigid)):
|
if not (isinstance(c1, Rigid) and isinstance(c2, Rigid)):
|
||||||
return Set()
|
return set()
|
||||||
else:
|
else:
|
||||||
shared = list(Set(c1.vars).intersection(c2.vars))
|
shared = list(set(c1.vars).intersection(c2.vars))
|
||||||
overdists = Set()
|
overdists = set()
|
||||||
for i in range(len(shared)):
|
for i in range(len(shared)):
|
||||||
for j in range(i):
|
for j in range(i):
|
||||||
v1 = shared[i]
|
v1 = shared[i]
|
||||||
|
@ -253,10 +252,10 @@ def over_distances(c1, c2):
|
||||||
|
|
||||||
def over_angles_hh(hog1, hog2):
|
def over_angles_hh(hog1, hog2):
|
||||||
# determine duplicate angles
|
# determine duplicate angles
|
||||||
shared = list(Set(hog1.xvars).intersection(hog2.xvars))
|
shared = list(set(hog1.xvars).intersection(hog2.xvars))
|
||||||
if not hog1.cvar == hog2.cvar:
|
if not hog1.cvar == hog2.cvar:
|
||||||
return Set()
|
return set()
|
||||||
overangles = Set()
|
overangles = set()
|
||||||
for i in range(len(shared)):
|
for i in range(len(shared)):
|
||||||
for j in range(i):
|
for j in range(i):
|
||||||
v1 = shared[i]
|
v1 = shared[i]
|
||||||
|
@ -266,8 +265,8 @@ def over_angles_hh(hog1, hog2):
|
||||||
|
|
||||||
def over_angles_bb(b1, b2):
|
def over_angles_bb(b1, b2):
|
||||||
# determine duplicate angles
|
# determine duplicate angles
|
||||||
shared = list(Set(b1.vars).intersection(b2.vars))
|
shared = list(set(b1.vars).intersection(b2.vars))
|
||||||
overangles = Set()
|
overangles = set()
|
||||||
for i in range(len(shared)):
|
for i in range(len(shared)):
|
||||||
for j in range(i+1, len(shared)):
|
for j in range(i+1, len(shared)):
|
||||||
for k in range(j+1, len(shared)):
|
for k in range(j+1, len(shared)):
|
||||||
|
@ -282,8 +281,8 @@ def over_angles_bb(b1, b2):
|
||||||
def over_angles_cb(cluster, balloon):
|
def over_angles_cb(cluster, balloon):
|
||||||
# determine duplicate angles
|
# determine duplicate angles
|
||||||
# note: identical to over_angles_bb and (non-existent) over_angles_cc
|
# note: identical to over_angles_bb and (non-existent) over_angles_cc
|
||||||
shared = list(Set(cluster.vars).intersection(balloon.vars))
|
shared = list(set(cluster.vars).intersection(balloon.vars))
|
||||||
overangles = Set()
|
overangles = set()
|
||||||
for i in range(len(shared)):
|
for i in range(len(shared)):
|
||||||
for j in range(i+1, len(shared)):
|
for j in range(i+1, len(shared)):
|
||||||
for k in range(j+1, len(shared)):
|
for k in range(j+1, len(shared)):
|
||||||
|
@ -297,10 +296,10 @@ def over_angles_cb(cluster, balloon):
|
||||||
|
|
||||||
def over_angles_bh(balloon, hog):
|
def over_angles_bh(balloon, hog):
|
||||||
# determine duplicate angles
|
# determine duplicate angles
|
||||||
shared = list(Set(balloon.vars).intersection(hog.xvars))
|
shared = list(set(balloon.vars).intersection(hog.xvars))
|
||||||
if hog.cvar not in balloon.vars:
|
if hog.cvar not in balloon.vars:
|
||||||
return Set()
|
return set()
|
||||||
overangles = Set()
|
overangles = set()
|
||||||
for i in range(len(shared)):
|
for i in range(len(shared)):
|
||||||
for j in range(i+1,len(shared)):
|
for j in range(i+1,len(shared)):
|
||||||
v1 = shared[i]
|
v1 = shared[i]
|
||||||
|
@ -310,10 +309,10 @@ def over_angles_bh(balloon, hog):
|
||||||
|
|
||||||
def over_angles_ch(cluster, hog):
|
def over_angles_ch(cluster, hog):
|
||||||
# determine duplicate angles
|
# determine duplicate angles
|
||||||
shared = list(Set(cluster.vars).intersection(hog.xvars))
|
shared = list(set(cluster.vars).intersection(hog.xvars))
|
||||||
if hog.cvar not in cluster.vars:
|
if hog.cvar not in cluster.vars:
|
||||||
return Set()
|
return set()
|
||||||
overangles = Set()
|
overangles = set()
|
||||||
for i in range(len(shared)):
|
for i in range(len(shared)):
|
||||||
for j in range(i+1,len(shared)):
|
for j in range(i+1,len(shared)):
|
||||||
v1 = shared[i]
|
v1 = shared[i]
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
A configuration is a set of named points with coordinates."""
|
A configuration is a set of named points with coordinates."""
|
||||||
|
|
||||||
from sets import Set
|
|
||||||
from matfunc import Vec, Mat
|
from matfunc import Vec, Mat
|
||||||
from intersections import *
|
from intersections import *
|
||||||
from tolerance import *
|
from tolerance import *
|
||||||
|
@ -113,7 +112,7 @@ class Configuration:
|
||||||
|
|
||||||
def _merge_transform_2D(self, other):
|
def _merge_transform_2D(self, other):
|
||||||
"""returns a new configurations which is this one plus the given other configuration transformed, such that common points will overlap (if possible)."""
|
"""returns a new configurations which is this one plus the given other configuration transformed, such that common points will overlap (if possible)."""
|
||||||
shared = Set(self.vars()).intersection(other.vars())
|
shared = set(self.vars()).intersection(other.vars())
|
||||||
underconstrained = self.underconstrained or other.underconstrained
|
underconstrained = self.underconstrained or other.underconstrained
|
||||||
if len(shared) == 0:
|
if len(shared) == 0:
|
||||||
underconstrained = True
|
underconstrained = True
|
||||||
|
@ -152,7 +151,7 @@ class Configuration:
|
||||||
def merge_scale_2D(self, other, vars=[]):
|
def merge_scale_2D(self, other, vars=[]):
|
||||||
"""returns a new configurations which is this one plus the given other configuration transformed, such that common points will overlap (if possible)."""
|
"""returns a new configurations which is this one plus the given other configuration transformed, such that common points will overlap (if possible)."""
|
||||||
if len(vars) == 0:
|
if len(vars) == 0:
|
||||||
shared = Set(self.vars()).intersection(other.vars())
|
shared = set(self.vars()).intersection(other.vars())
|
||||||
else:
|
else:
|
||||||
shared = vars
|
shared = vars
|
||||||
underconstrained = self.underconstrained or other.underconstrained
|
underconstrained = self.underconstrained or other.underconstrained
|
||||||
|
@ -186,7 +185,7 @@ class Configuration:
|
||||||
"""returns a matrix for a rigid transformation
|
"""returns a matrix for a rigid transformation
|
||||||
such that points in other are mapped onto points in self
|
such that points in other are mapped onto points in self
|
||||||
"""
|
"""
|
||||||
shared = Set(self.vars()).intersection(other.vars())
|
shared = set(self.vars()).intersection(other.vars())
|
||||||
underconstrained = self.underconstrained or other.underconstrained
|
underconstrained = self.underconstrained or other.underconstrained
|
||||||
if len(shared) == 0:
|
if len(shared) == 0:
|
||||||
underconstrained = True
|
underconstrained = True
|
||||||
|
@ -253,7 +252,7 @@ class Configuration:
|
||||||
return t
|
return t
|
||||||
|
|
||||||
def _merge_scale_transform_3D(self, other):
|
def _merge_scale_transform_3D(self, other):
|
||||||
shared = Set(self.vars()).intersection(other.vars())
|
shared = set(self.vars()).intersection(other.vars())
|
||||||
if len(shared) == 0:
|
if len(shared) == 0:
|
||||||
return self._merge_transform_3D(other)
|
return self._merge_transform_3D(other)
|
||||||
elif len(shared) == 1:
|
elif len(shared) == 1:
|
||||||
|
|
|
@ -41,7 +41,7 @@ def gmatch(pattern, reference):
|
||||||
outmatches = []
|
outmatches = []
|
||||||
for n in onumbers:
|
for n in onumbers:
|
||||||
outmatches += reference.outfan(n)
|
outmatches += reference.outfan(n)
|
||||||
matches = Set(inmatches).intersection(outmatches)
|
matches = set(inmatches).intersection(outmatches)
|
||||||
newsolutions = []
|
newsolutions = []
|
||||||
if solutions == None:
|
if solutions == None:
|
||||||
for refvar in matches:
|
for refvar in matches:
|
||||||
|
|
|
@ -31,7 +31,6 @@ Copyright Rick van der Meiden - 2003, 2004, 2005
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import random
|
import random
|
||||||
from sets import Set,ImmutableSet
|
|
||||||
from notify import Notifier
|
from notify import Notifier
|
||||||
|
|
||||||
class Graph (Notifier):
|
class Graph (Notifier):
|
||||||
|
@ -199,9 +198,8 @@ class Graph (Notifier):
|
||||||
|
|
||||||
def adjacent_vertices(self, v):
|
def adjacent_vertices(self, v):
|
||||||
"""list of adjacent (ingoing or outgoing) vertices"""
|
"""list of adjacent (ingoing or outgoing) vertices"""
|
||||||
from sets import Set
|
iset = set(self.ingoing_vertices(v))
|
||||||
iset = Set(self.ingoing_vertices(v))
|
oset = set(self.outgoing_vertices(v))
|
||||||
oset = Set(self.outgoing_vertices(v))
|
|
||||||
vset = iset.union(oset)
|
vset = iset.union(oset)
|
||||||
return list(vset)
|
return list(vset)
|
||||||
|
|
||||||
|
@ -300,11 +298,11 @@ class Graph (Notifier):
|
||||||
|
|
||||||
def connected_subsets(self):
|
def connected_subsets(self):
|
||||||
"""returns a set of (undirectionally) connected subsets of vertices"""
|
"""returns a set of (undirectionally) connected subsets of vertices"""
|
||||||
todo = Set(self.vertices())
|
todo = set(self.vertices())
|
||||||
subsets = Set()
|
subsets = set()
|
||||||
while (todo):
|
while (todo):
|
||||||
v = todo.pop()
|
v = todo.pop()
|
||||||
s = Set(self.connected(v))
|
s = set(self.connected(v))
|
||||||
for x in s:
|
for x in s:
|
||||||
todo.remove(x)
|
todo.remove(x)
|
||||||
s.add(v)
|
s.add(v)
|
||||||
|
@ -324,13 +322,13 @@ class Graph (Notifier):
|
||||||
graph = Graph()
|
graph = Graph()
|
||||||
for edge in self.edges():
|
for edge in self.edges():
|
||||||
(v1,v2) = edge
|
(v1,v2) = edge
|
||||||
g1 = ImmutableSet([v1])
|
g1 = frozenset([v1])
|
||||||
g2 = ImmutableSet([v2])
|
g2 = frozenset([v2])
|
||||||
graph.add_edge(g1,g2)
|
graph.add_edge(g1,g2)
|
||||||
|
|
||||||
# Stoer/Wagner algorithm
|
# Stoer/Wagner algorithm
|
||||||
mincutvalue = None
|
mincutvalue = None
|
||||||
mincut = ImmutableSet()
|
mincut = frozenset()
|
||||||
while len(graph.vertices()) > 1:
|
while len(graph.vertices()) > 1:
|
||||||
(phasecut,phasecutvalue) = self._mincutphase(graph)
|
(phasecut,phasecutvalue) = self._mincutphase(graph)
|
||||||
if mincutvalue == None or phasecutvalue < mincutvalue:
|
if mincutvalue == None or phasecutvalue < mincutvalue:
|
||||||
|
@ -339,8 +337,8 @@ class Graph (Notifier):
|
||||||
|
|
||||||
# rewrite output
|
# rewrite output
|
||||||
g1 = mincut
|
g1 = mincut
|
||||||
g2 = ImmutableSet(self.vertices()).difference(g1)
|
g2 = frozenset(self.vertices()).difference(g1)
|
||||||
edges = Set()
|
edges = set()
|
||||||
for v in g1:
|
for v in g1:
|
||||||
for k in self.adjacent_vertices(v):
|
for k in self.adjacent_vertices(v):
|
||||||
if k in g2:
|
if k in g2:
|
||||||
|
@ -356,7 +354,7 @@ class Graph (Notifier):
|
||||||
if self.has_edge(k,v):
|
if self.has_edge(k,v):
|
||||||
edges.add((k,v))
|
edges.add((k,v))
|
||||||
|
|
||||||
return (mincutvalue, ImmutableSet(edges), g1, g2)
|
return (mincutvalue, frozenset(edges), g1, g2)
|
||||||
|
|
||||||
def _mincutphase(self, graph):
|
def _mincutphase(self, graph):
|
||||||
# returns the last vertex (group) added and the cut value
|
# returns the last vertex (group) added and the cut value
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Base classes for multi-valued assignments in methodgraphs"""
|
"""Base classes for multi-valued assignments in methodgraphs"""
|
||||||
|
|
||||||
from method import Method, MethodGraph
|
from method import Method, MethodGraph
|
||||||
from sets import Set
|
|
||||||
|
|
||||||
class MultiVariable:
|
class MultiVariable:
|
||||||
"""For representing multi-valued variables
|
"""For representing multi-valued variables
|
||||||
|
@ -34,6 +33,8 @@ class MultiMethod(Method):
|
||||||
The 'multi_execute' method must return a list of possible values for the output variable.
|
The 'multi_execute' method must return a list of possible values for the output variable.
|
||||||
The output values returned by subsequent calls multi-execute are collected and stored in the
|
The output values returned by subsequent calls multi-execute are collected and stored in the
|
||||||
output MultiVariable.
|
output MultiVariable.
|
||||||
|
|
||||||
|
Note that a set of values for the outputvariable is stored, so that equivalent values are only stored once.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -65,10 +66,10 @@ class MultiMethod(Method):
|
||||||
if len(multi_inputs) > 0:
|
if len(multi_inputs) > 0:
|
||||||
mvar = multi_inputs[0]
|
mvar = multi_inputs[0]
|
||||||
values = inmap[mvar]
|
values = inmap[mvar]
|
||||||
output = Set()
|
output = set()
|
||||||
for value in values:
|
for value in values:
|
||||||
base_inmap[mvar] = value
|
base_inmap[mvar] = value
|
||||||
output.union_update(self._recurse_execute(inmap, base_inmap, multi_inputs[1:]))
|
output.update(self._recurse_execute(inmap, base_inmap, multi_inputs[1:]))
|
||||||
return output
|
return output
|
||||||
else:
|
else:
|
||||||
return self.multi_execute(base_inmap)
|
return self.multi_execute(base_inmap)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user