Replaced references to depricated Set and ImmutableSet classes with set and frozenset types

This commit is contained in:
kwikrick 2009-11-17 10:27:29 +00:00
parent 74386c1710
commit fac2609def
7 changed files with 343 additions and 365 deletions

View File

@ -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

View File

@ -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]

View File

@ -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]

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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)