added support for SelectionConstraint

This commit is contained in:
kwikrick 2009-11-15 12:05:44 +00:00
parent 22e159a5ad
commit 99b304c882
6 changed files with 415 additions and 154 deletions

View File

@ -52,8 +52,8 @@ class PrototypeMethod(MultiMethod):
the protoype and the solution satisfy the same constraints. the protoype and the solution satisfy the same constraints.
""" """
def __init__(self, incluster, selclusters, outcluster, constraints): def __init__(self, incluster, selclusters, outcluster, constraints, enabled):
self._inputs = [incluster]+selclusters self._inputs = [incluster]+selclusters+[enabled]
self._outputs = [outcluster] self._outputs = [outcluster]
self._constraints = constraints self._constraints = constraints
MultiMethod.__init__(self) MultiMethod.__init__(self)
@ -62,11 +62,14 @@ class PrototypeMethod(MultiMethod):
diag_print("PrototypeMethod.multi_execute called","clmethods") diag_print("PrototypeMethod.multi_execute called","clmethods")
incluster = self._inputs[0] incluster = self._inputs[0]
selclusters = [] selclusters = []
for i in range(1,len(self._inputs)): for i in range(1,len(self._inputs)-1):
selclusters.append(self._inputs[i]) selclusters.append(self._inputs[i])
diag_print("input clusters"+str(incluster), "PrototypeMethod.multi_execute") enabledvar = self._inputs[-1]
diag_print("input cluster"+str(incluster), "PrototypeMethod.multi_execute")
diag_print("selection clusters"+str(selclusters), "PrototypeMethod.multi_execute") diag_print("selection clusters"+str(selclusters), "PrototypeMethod.multi_execute")
# get confs diag_print("enabledvar"+str(enabledvar), "PrototypeMethod.multi_execute")
# get confs/values
enabledval = inmap[enabledvar]
inconf = inmap[incluster] inconf = inmap[incluster]
selmap = {} selmap = {}
for cluster in selclusters: for cluster in selclusters:
@ -74,10 +77,16 @@ class PrototypeMethod(MultiMethod):
assert len(conf.vars()) == 1 assert len(conf.vars()) == 1
var = conf.vars()[0] var = conf.vars()[0]
selmap[var] = conf.map[var] selmap[var] = conf.map[var]
if len(selmap) == 0:
selconf = {}
else:
selconf = Configuration(selmap) selconf = Configuration(selmap)
sat = True
diag_print("input configuration = "+str(inconf), "PrototypeMethod.multi_execute") diag_print("input configuration = "+str(inconf), "PrototypeMethod.multi_execute")
diag_print("selection configuration = "+str(selconf), "PrototypeMethod.multi_execute") diag_print("selection configurations = "+str(selconf), "PrototypeMethod.multi_execute")
diag_print("enabled value = "+str(enabledval), "PrototypeMethod.multi_execute")
# do test
if enabledval == True:
sat = True
for con in self._constraints: for con in self._constraints:
satcon = con.satisfied(inconf.map) != con.satisfied(selconf.map) satcon = con.satisfied(inconf.map) != con.satisfied(selconf.map)
diag_print("constraint = "+str(con), "PrototypeMethod.multi_execute") diag_print("constraint = "+str(con), "PrototypeMethod.multi_execute")
@ -88,6 +97,54 @@ class PrototypeMethod(MultiMethod):
return [inconf] return [inconf]
else: else:
return [] return []
else:
return [inconf]
def __str__(self):
return "PrototypeMethod#%d(%s->%s)"%(id(self),str(self._inputs[0]), str(self._outputs[0]))
class SelectionMethod(MultiMethod):
"""A SelectionMethod selects those solutions of a cluster for which
all selectionconstraints are satisfied.
"""
def __init__(self, incluster, outcluster):
self._inputs = [incluster]
self._outputs = [outcluster]
self._constraints = []
MultiMethod.__init__(self)
def add_constraint(self, con):
self._constraints.append(con)
def rem_constraint(self, con):
self._constraints.remove(con)
def iter_constraints(self):
return iter(self._constraints)
def multi_execute(self, inmap):
diag_print("SelectionMethod.multi_execute called","SelectionMethod.multi_execute")
incluster = self._inputs[0]
inconf = inmap[incluster]
diag_print("input configuration = "+str(inconf), "SelectionMethod.multi_execute")
sat = True
for con in self._constraints:
diag_print("constraint = "+str(con), "SelectionMethod.multi_execute")
satcon = con.satisfied(inconf.map)
diag_print("satisfied = "+str(satcon), "SelectionMethod.multi_execute")
sat = sat and satcon
diag_print("all satisfied = "+str(sat), "SelectionMethod.multi_execute")
if sat:
return [inconf]
else:
return []
def __str__(self):
return "SelectionMethod#%d(%s & %s ->%s)"%(id(self),str(self._inputs[0]), str(self._constraints), str(self._outputs[0]))
def is_information_increasing(method): def is_information_increasing(method):
infinc = True infinc = True
@ -110,8 +167,11 @@ class ClusterSolver(Notifier):
def __init__(self, dimension): def __init__(self, dimension):
"""Create a new empty solver""" """Create a new empty solver"""
# init superclasses
Notifier.__init__(self) Notifier.__init__(self)
# store arguments
self.dimension = dimension self.dimension = dimension
# init instance vars
self._graph = Graph() self._graph = Graph()
self._graph.add_vertex("_root") self._graph.add_vertex("_root")
self._graph.add_vertex("_toplevel") self._graph.add_vertex("_toplevel")
@ -122,10 +182,14 @@ class ClusterSolver(Notifier):
self._graph.add_vertex("_hedgehogs") self._graph.add_vertex("_hedgehogs")
self._graph.add_vertex("_balloons") self._graph.add_vertex("_balloons")
self._graph.add_vertex("_methods") self._graph.add_vertex("_methods")
# queue of new objects to process
self._new = [] self._new = []
# methodgraph
self._mg = MethodGraph() self._mg = MethodGraph()
# add prototype_selection boolean var to method graph
self._prototype_selection_var = "_prototype_selection_enabled"
self._mg.add_variable(self._prototype_selection_var)
self._mg.set(self._prototype_selection_var, True)
# store map of selection_constraints to SelectionMethod (or None)
self._selection_method = {}
def variables(self): def variables(self):
"""get list of variables""" """get list of variables"""
@ -209,9 +273,29 @@ class ClusterSolver(Notifier):
def contains(self, obj): def contains(self, obj):
return self._graph.has_vertex(obj) return self._graph.has_vertex(obj)
def set_prototype_selection(self, enabled):
self._mg.set(self._prototype_selection_var, enabled)
def add_selection_constraint(self, con):
if con not in self._selection_method:
selector = self._find_selection_method(con)
if selector != None:
selector.add_constraint(con)
self._selection_method[con] = selector
self._mg.execute(selector)
self._selection_method[con] = None
def rem_selection_constraint(self, con):
if con in self._selection_method:
selector = self._selection_method[con]
if selector != None:
selector.rem_constraint(con)
self._mg.execute(selector)
del self._selection_method[con]
# ------------ INTERNALLY USED METHODS -------- # ------------ INTERNALLY USED METHODS --------
# -- general house hold # --- dependencies and groups
def _add_dependency(self, on, dependend): def _add_dependency(self, on, dependend):
"""Add a dependence for second object on first object""" """Add a dependence for second object on first object"""
@ -244,45 +328,6 @@ class ClusterSolver(Notifier):
if object in self._new: if object in self._new:
self._new.remove(object) self._new.remove(object)
def _remove(self, object):
# find all indirectly dependend objects
todelete = [object]+self._find_descendend(object)
torestore = Set()
# remove all objects
for item in todelete:
# if merge removed items from toplevel then add them back to top level
if hasattr(item, "restore_toplevel"):
for cluster in item.restore_toplevel:
torestore.add(cluster)
# delete it from graph
diag_print("deleting "+str(item),"clsolver.remove")
self._graph.rem_vertex(item)
# remove from _new list
if item in self._new:
self._new.remove(item)
# remove from methodgraph
if isinstance(item, Method):
# note: method may have been removed because variable removed
try:
self._mg.rem_method(item)
except:
pass
elif isinstance(item, MultiVariable):
self._mg.rem_variable(item)
# notify listeners
self.send_notify(("remove", item))
# restore toplevel (also added to _new)
for cluster in torestore:
if self._graph.has_vertex(cluster):
self._add_top_level(cluster)
# debug
# print "after remove, drplan:"
# print self
# print "after remove, toplevel:"
# print self.top_level()
# re-solve
self._process_new()
def _find_descendend(self,v): def _find_descendend(self,v):
"""find all descendend objects of v (dirdctly or indirectly dependend""" """find all descendend objects of v (dirdctly or indirectly dependend"""
front = [v] front = [v]
@ -406,16 +451,27 @@ class ClusterSolver(Notifier):
# remove inputs from toplevel # remove inputs from toplevel
for cluster in merge.inputs(): for cluster in merge.inputs():
self._rem_top_level(cluster) self._rem_top_level(cluster)
# add prototype selection method # add selection methods
self._add_prototype_selector(merge) output2 = self._add_prototype_selector(merge)
# add solution selection method output3 = self._add_solution_selector(output2)
self._add_solution_selector(merge) return output3
def _add_method(self, method):
diag_print("new "+str(method),"clsolver")
self._add_to_group("_methods", method)
for obj in method.inputs():
self._add_dependency(obj, method)
for obj in method.outputs():
self._add_dependency(method, obj)
self._add_dependency(obj, method)
self._mg.add_method(method)
self.send_notify(("add", method))
# ----- solution selection
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()
if len(constraints) == 0:
return
vars = Set() vars = Set()
for con in constraints: for con in constraints:
vars.union_update(con.variables()) vars.union_update(con.variables())
@ -428,27 +484,66 @@ class ClusterSolver(Notifier):
raise StandardError, "no prototype cluster for variable "+str(v) raise StandardError, "no prototype cluster for variable "+str(v)
selclusters.append(clusters[0]) selclusters.append(clusters[0])
outcluster = incluster.copy() outcluster = incluster.copy()
# Rick 20090519 - copy does not copy structural overconstrained flag? selector = PrototypeMethod(incluster, selclusters, outcluster, constraints, self._prototype_selection_var)
outcluster.overconstrained = incluster.overconstrained
selector = PrototypeMethod(incluster, selclusters, outcluster, constraints)
self._add_cluster(outcluster) self._add_cluster(outcluster)
self._add_method(selector) self._add_method(selector)
self._rem_top_level(incluster) self._rem_top_level(incluster)
return return outcluster
def _add_solution_selector(self, merge): def _add_solution_selector(self, incluster):
return outcluster = incluster.copy()
selector = SelectionMethod(incluster, outcluster)
constraints = self._find_selection_constraints(incluster)
for con in constraints:
selector.add_constraint(con)
self._selection_method[con] = selector
self._add_cluster(outcluster)
self._add_method(selector)
self._rem_top_level(incluster)
return selector
def _find_selection_method(self, con):
# find clusters containing all constraints vars
candidates = None
for var in con.variables():
# find clusters
clusters = set(self.find_dependend(var))
if candidates == None:
candidates = clusters
else:
candidates = candidates.intersection(clusters)
# get selection methods of clusters
methods = []
for cluster in candidates:
methods += filter(lambda c: isinstance(c,SelectionMethod), self.find_depends(cluster))
# get selection method with smallest cluster
if len(methods)>0:
method = min(methods, key=lambda m: len(m.inputs()[0].vars))
return method
else:
return None
##slow implementation, better would be to find method via clustering information in graph
#convars = set(con.variables())
#selmethods = filter(lambda x: isinstance(x,SelectionMethod), self.methods())
#for method in selmethods:
# incluster = method.inputs()[0]
# clvars = set(incluster.vars)
# if clvars.intersection(convars) == convars:
# return method
#return None
def _find_selection_constraints(self, incluster):
applicable = []
for con in self._selection_method:
selector = self._selection_method[con]
if selector == None:
convars = set(con.variables())
clvars = set(incluster.vars)
if convars.intersection(clvars) == convars:
applicable.append(con)
return applicable
def _add_method(self, method):
diag_print("new "+str(method),"clsolver")
self._add_to_group("_methods", method)
for obj in method.inputs():
self._add_dependency(obj, method)
for obj in method.outputs():
self._add_dependency(method, obj)
self._add_dependency(obj, method)
self._mg.add_method(method)
self.send_notify(("add", method))
# -------------- # --------------
@ -469,6 +564,49 @@ class ClusterSolver(Notifier):
def _search(self, newcluster): def _search(self, newcluster):
raise StandardError, "Not implemented. ClusterSolver is an abstract class, please use ClusterSolver2D or ClusterSolver3D" raise StandardError, "Not implemented. ClusterSolver is an abstract class, please use ClusterSolver2D or ClusterSolver3D"
# -- removing objects
def _remove(self, object):
# find all indirectly dependend objects
todelete = [object]+self._find_descendend(object)
torestore = Set()
# remove all objects
for item in todelete:
# if merge removed items from toplevel then add them back to top level
if hasattr(item, "restore_toplevel"):
for cluster in item.restore_toplevel:
torestore.add(cluster)
# delete it from graph
diag_print("deleting "+str(item),"clsolver.remove")
self._graph.rem_vertex(item)
# remove from _new list
if item in self._new:
self._new.remove(item)
# remove from methodgraph
if isinstance(item, Method):
# note: method may have been removed because variable removed
try:
self._mg.rem_method(item)
except:
pass
# restore SelectionConstraints
if isinstance(item, SelectionMethod):
for con in item.iter_constraints():
self._selection_method[con] = None
elif isinstance(item, MultiVariable):
self._mg.rem_variable(item)
# notify listeners
self.send_notify(("remove", item))
# restore toplevel (also added to _new)
for cluster in torestore:
if self._graph.has_vertex(cluster):
self._add_top_level(cluster)
# re-solve
self._process_new()
# --- root selection
def _contains_root(self, input_cluster): def _contains_root(self, input_cluster):
"""returns True iff input_cluster is root cluster or was determined by """returns True iff input_cluster is root cluster or was determined by
merging with the root cluster.""" merging with the root cluster."""
@ -501,6 +639,8 @@ class ClusterSolver(Notifier):
return False return False
#def #def
# ---- consistency
def _is_consistent_pair(self, object1, object2): def _is_consistent_pair(self, object1, object2):
diag_print("in is_consistent_pair "+str(object1)+" "+str(object2),"clsolver") diag_print("in is_consistent_pair "+str(object1)+" "+str(object2),"clsolver")
oc = over_constraints(object1, object2) oc = over_constraints(object1, object2)
@ -654,36 +794,36 @@ class ClusterSolver(Notifier):
## return None ## return None
## ##
def _known_angle(self,a,b,c): ## def _known_angle(self,a,b,c):
"""returns Balloon, Rigid or Hedgehog that contains angle(a, b, c)""" ## """returns Balloon, Rigid or Hedgehog that contains angle(a, b, c)"""
if a==b or a==c or b==c: ## if a==b or a==c or b==c:
raise StandardError, "all vars in angle must be different" ## raise StandardError, "all vars in angle must be different"
# get objects dependend on a, b and c ## # get objects dependend on a, b and c
dep_a = self._graph.outgoing_vertices(a) ## dep_a = self._graph.outgoing_vertices(a)
dep_b = self._graph.outgoing_vertices(b) ## dep_b = self._graph.outgoing_vertices(b)
dep_c = self._graph.outgoing_vertices(c) ## dep_c = self._graph.outgoing_vertices(c)
dependend = [] ## dependend = []
for obj in dep_a: ## for obj in dep_a:
if obj in dep_b and obj in dep_c: ## if obj in dep_b and obj in dep_c:
dependend.append(obj) ## dependend.append(obj)
# find a hedgehog ## # find a hedgehog
hogs = filter(lambda x: isinstance(x,Hedgehog), dependend) ## hogs = filter(lambda x: isinstance(x,Hedgehog), dependend)
hogs = filter(lambda hog: hog.cvar == b, hogs) ## hogs = filter(lambda hog: hog.cvar == b, hogs)
hogs = filter(lambda x: self.is_top_level(x), hogs) ## hogs = filter(lambda x: self.is_top_level(x), hogs)
if len(hogs) == 1: return hogs[0] ## if len(hogs) == 1: return hogs[0]
if len(hogs) > 1: raise "error: angle in more than one hedgehogs" ## if len(hogs) > 1: raise "error: angle in more than one hedgehogs"
# or find a cluster ## # or find a cluster
clusters = filter(lambda x: isinstance(x,Rigid), dependend) ## clusters = filter(lambda x: isinstance(x,Rigid), dependend)
clusters = filter(lambda x: self.is_top_level(x), clusters) ## clusters = filter(lambda x: self.is_top_level(x), clusters)
if len(clusters) == 1: return clusters[0] ## if len(clusters) == 1: return clusters[0]
if len(clusters) > 1: raise "error: angle in more than one Rigids" ## if len(clusters) > 1: raise "error: angle in more than one Rigids"
# or find a balloon ## # or find a balloon
balloons = filter(lambda x: isinstance(x,Balloon), dependend) ## balloons = filter(lambda x: isinstance(x,Balloon), dependend)
balloons = filter(lambda x: self.is_top_level(x), balloons) ## balloons = filter(lambda x: self.is_top_level(x), balloons)
if len(balloons) == 1: return balloons[0] ## if len(balloons) == 1: return balloons[0]
if len(balloons) > 1: raise "error: angle in more than one Balloons" ## if len(balloons) > 1: raise "error: angle in more than one Balloons"
# or return None ## # or return None
return None ## return None
##def _is_source(self, object, constraint): ##def _is_source(self, object, constraint):
## if not self._contains_constraint(object, constraint): ## if not self._contains_constraint(object, constraint):

View File

@ -76,14 +76,14 @@ class ClusterSolver3D(ClusterSolver):
For each Cluster a set of Configurations can be set using the For each Cluster a set of Configurations can be set using the
set method. Configurations are propagated via Methods and can set method. Configurations are propagated via Methods and can
be retrieved with the get method (also returning a set). 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, dimension=3) ClusterSolver.__init__(self, 3)
self.rootcluster = None self.rootcluster = None
# overriding ClusterSolver.set_root # overriding ClusterSolver.set_root
@ -220,14 +220,11 @@ class ClusterSolver3D(ClusterSolver):
merge.restore_toplevel.append(cluster) merge.restore_toplevel.append(cluster)
else: else:
diag_print("keep top-level: "+str(cluster),"clsolver3D") diag_print("keep top-level: "+str(cluster),"clsolver3D")
# endif
# add prototype selection method
self._add_prototype_selector(merge)
# add solution selection method
self._add_solution_selector(merge)
# add method to determine root-variable # add method to determine root-variable
self._add_root_method(merge.input_clusters(),merge.outputs()[0]) self._add_root_method(merge.input_clusters(),merge.outputs()[0])
# pause # add solution selection methods
output2 = self._add_prototype_selector(merge)
output3 = self._add_solution_selector(output2)
return True return True
def _add_root_method(self,inclusters,outcluster): def _add_root_method(self,inclusters,outcluster):

View File

@ -136,7 +136,9 @@ class Rigid(Cluster):
return s return s
def copy(self): def copy(self):
return Rigid(self.vars) new = Rigid(self.vars)
new.overconstrained = self.overconstrained
return new
@ -165,7 +167,9 @@ class Hedgehog(Cluster):
return s return s
def copy(self): def copy(self):
return Hedgehog(self.cvar, self.xvars) new = Hedgehog(self.cvar, self.xvars)
new.overconstrained = self.overconstrained
return new
class Balloon(Cluster): class Balloon(Cluster):
@ -190,8 +194,9 @@ class Balloon(Cluster):
return s return s
def copy(self): def copy(self):
return Balloon(self.vars) new = Balloon(self.vars)
new.overconstrained = self.overconstrained
return new
# ----- function to determine overconstraints ----- # ----- function to determine overconstraints -----

View File

@ -2,19 +2,17 @@
problems incrementally.""" problems incrementally."""
import vector import vector
from clsolver import PrototypeMethod import math
# depricated! from clsolver2D import ClusterSolver2D from clsolver import PrototypeMethod, SelectionMethod
from clsolver3D import ClusterSolver3D from clsolver3D import ClusterSolver3D
from cluster import Rigid, Hedgehog from cluster import Rigid, Hedgehog
from configuration import Configuration from configuration import Configuration
import math
from diagnostic import diag_print from diagnostic import diag_print
from constraint import Constraint, ConstraintGraph from constraint import Constraint, ConstraintGraph
from notify import Notifier, Listener from notify import Notifier, Listener
from tolerance import tol_eq from tolerance import tol_eq
from intersections import angle_3p, distance_2p from intersections import angle_3p, distance_2p
from selconstr import SelectionConstraint from selconstr import SelectionConstraint
from sets import Set
# ----------- GeometricProblem ------------- # ----------- GeometricProblem -------------
@ -226,8 +224,7 @@ class GeometricSolver (Listener):
""" """
# public methods # public methods
def __init__(self, problem, use_prototype=True):
def __init__(self, problem):
"""Create a new GeometricSolver instance """Create a new GeometricSolver instance
keyword args keyword args
@ -248,6 +245,9 @@ class GeometricSolver (Listener):
raise StandardError, "Do not know how to solve problems of dimension > 3." raise StandardError, "Do not know how to solve problems of dimension > 3."
self._map = {} self._map = {}
# enable prototype based selection by default
self.set_prototype_selection(use_prototype)
# register # register
self.cg.add_listener(self) self.cg.add_listener(self)
self.dr.add_listener(self) self.dr.add_listener(self)
@ -256,20 +256,32 @@ class GeometricSolver (Listener):
self.fixvars = [] self.fixvars = []
self.fixcluster = None self.fixcluster = None
# map current cg # add variables
for var in self.cg.variables(): for var in self.cg.variables():
self._add_variable(var) self._add_variable(var)
# add distances first? Nicer decomposition in Rigids # add constraints
for con in self.cg.constraints(): toadd = set(self.cg.constraints())
# add selection constraints first. Prevents re-evaluation
for con in list(toadd):
if isinstance(con, SelectionConstraint):
self._add_constraint(con)
toadd.remove(con)
# add distances first. Nicer decomposition in Rigids
for con in list(toadd):
if isinstance(con, DistanceConstraint): if isinstance(con, DistanceConstraint):
self._add_constraint(con) self._add_constraint(con)
toadd.remove(con)
# add angles and other constraints first? Better performance # add other constraints.
for con in self.cg.constraints(): for con in toadd:
if not isinstance(con, DistanceConstraint):
self._add_constraint(con) self._add_constraint(con)
def set_prototype_selection(self, enabled):
self.dr.set_prototype_selection(enabled)
def get_constrainedness(self): def get_constrainedness(self):
"""Depricated. Use get_statis instead""" """Depricated. Use get_statis instead"""
return self.get_status() return self.get_status()
@ -336,6 +348,7 @@ class GeometricSolver (Listener):
parent.subs.append(map[inp]) parent.subs.append(map[inp])
# combine clusters due to selection # combine clusters due to selection
if False:
for method in self.dr.methods(): for method in self.dr.methods():
if isinstance(method, PrototypeMethod): if isinstance(method, PrototypeMethod):
incluster = method.inputs()[0] incluster = method.inputs()[0]
@ -343,6 +356,13 @@ class GeometricSolver (Listener):
geoin = map[incluster] geoin = map[incluster]
geoout = map[outcluster] geoout = map[outcluster]
geoout.subs = list(geoin.subs) geoout.subs = list(geoin.subs)
for method in self.dr.methods():
if isinstance(method, SelectionMethod):
incluster = method.inputs()[0]
outcluster = method.outputs()[0]
geoin = map[incluster]
geoout = map[outcluster]
geoout.subs = list(geoin.subs)
# determine top-level result # determine top-level result
rigids = filter(lambda c: isinstance(c, Rigid), self.dr.top_level()) rigids = filter(lambda c: isinstance(c, Rigid), self.dr.top_level())
@ -366,12 +386,12 @@ class GeometricSolver (Listener):
def get_solutions(self): def get_solutions(self):
"""Returns a list of Configurations, which will be empty if the """Returns a list of Configurations, which will be empty if the
problem is not structurally well-constrained. Note: this method is problem has no solutions. Note: this method is
cheaper but less informative than get_cluster. The cheaper but less informative than get_cluster. The
list and the configurations should not be changed (since they are list and the configurations should not be changed (since they are
references to objects in the solver).""" references to objects in the solver)."""
rigids = filter(lambda c: isinstance(c, Rigid), self.dr.top_level()) rigids = filter(lambda c: isinstance(c, Rigid), self.dr.top_level())
if len(rigids) == 0: if len(rigids) != 0:
return self.dr.get(rigids[0]) return self.dr.get(rigids[0])
else: else:
return [] return []
@ -496,7 +516,7 @@ class GeometricSolver (Listener):
self._update_fix() self._update_fix()
elif isinstance(con, SelectionConstraint): elif isinstance(con, SelectionConstraint):
# add directly to clustersolver # add directly to clustersolver
self.dr.add(con) self.dr.add_selection_constraint(con)
else: else:
## raise StandardError, "unknown constraint type" ## raise StandardError, "unknown constraint type"
pass pass
@ -516,6 +536,9 @@ class GeometricSolver (Listener):
self.fixcluster = Rigid(self.fixvars) self.fixcluster = Rigid(self.fixvars)
self.dr.add(self.fixcluster) self.dr.add(self.fixcluster)
self.dr.set_root(self.fixcluster) self.dr.set_root(self.fixcluster)
elif isinstance(con, SelectionConstraint):
# remove directly from clustersolver
self.dr.rem_selection_constraint(con)
elif con in self._map: elif con in self._map:
self.dr.remove(self._map[con]) self.dr.remove(self._map[con])
del self._map[con] del self._map[con]
@ -641,8 +664,7 @@ class GeometricCluster:
# make done # make done
if done == None: if done == None:
done = Set() done = set()
# recurse # recurse
s = "" s = ""

View File

@ -247,7 +247,8 @@ def is_left_handed(p1,p2,p3,p4):
v = p3-p1 v = p3-p1
uv = vector.cross(u,v) uv = vector.cross(u,v)
w = p4-p1 w = p4-p1
return vector.dot(uv,w) < 0 #return vector.dot(uv,w) < 0
return tol_lt(vector.dot(uv,w), 0)
def is_right_handed(p1,p2,p3,p4): def is_right_handed(p1,p2,p3,p4):
"""return True if tetrahedron p1 p2 p3 p4 is right handed""" """return True if tetrahedron p1 p2 p3 p4 is right handed"""
@ -255,7 +256,18 @@ def is_right_handed(p1,p2,p3,p4):
v = p3-p1 v = p3-p1
uv = vector.cross(u,v) uv = vector.cross(u,v)
w = p4-p1 w = p4-p1
return vector.dot(uv,w) > 0 #return vector.dot(uv,w) > 0
return tol_gt(vector.dot(uv,w), 0)
def is_not_handed(p1,p2,p3,p4):
"""return True if tetrahedron p1 p2 p3 p4 is left handed"""
u = p2-p1
v = p3-p1
uv = vector.cross(u,v)
w = p4-p1
#return vector.dot(uv,w) == 0
return tol_eq(vector.dot(uv,w), 0)
# --------- coordinate tranformations ------- # --------- coordinate tranformations -------

View File

@ -5,6 +5,8 @@ from geosolver.geometric import *
from geosolver.vector import vector from geosolver.vector import vector
from geosolver.randomproblem import * from geosolver.randomproblem import *
from geosolver.diagnostic import diag_select, diag_print from geosolver.diagnostic import diag_select, diag_print
from geosolver.selconstr import fnot
from geosolver.intersections import is_left_handed, is_right_handed
import geosolver.tolerance as tolerance import geosolver.tolerance as tolerance
from time import time from time import time
@ -458,6 +460,32 @@ def twoscisors():
problem.add_constraint(DistanceConstraint('D', 'C', 6.0)) problem.add_constraint(DistanceConstraint('D', 'C', 6.0))
return problem return problem
def selection_problem():
"""The double tetrahedron problem with selection constraints"""
problem = GeometricProblem(dimension=3)
problem.add_point('v1', vector([0.0, 0.0, 0.0]))
problem.add_point('v2', vector([1.0, 0.0, 0.0]))
problem.add_point('v3', vector([0.0, 1.0, 0.0]))
problem.add_point('v4', vector([0.5, 0.5, 1.0]))
problem.add_point('v5', vector([0.5, 0.5,-1.0]))
problem.add_constraint(DistanceConstraint('v1', 'v2', 10.0))
problem.add_constraint(DistanceConstraint('v1', 'v3', 10.0))
problem.add_constraint(DistanceConstraint('v2', 'v3', 10.0))
problem.add_constraint(DistanceConstraint('v1', 'v4', 10.0))
problem.add_constraint(DistanceConstraint('v2', 'v4', 10.0))
problem.add_constraint(DistanceConstraint('v3', 'v4', 10.0))
problem.add_constraint(DistanceConstraint('v1', 'v5', 10.0))
problem.add_constraint(DistanceConstraint('v2', 'v5', 10.0))
problem.add_constraint(DistanceConstraint('v3', 'v5', 10.0))
problem.add_constraint(SelectionConstraint(is_left_handed, ['v1','v2','v3','v4']))
problem.add_constraint(SelectionConstraint(is_right_handed, ['v1','v2','v4','v5']))
return problem
# ------ 2D tests ------- # ------ 2D tests -------
@ -707,12 +735,13 @@ def test_ada_3d():
# ------- generic test ------- # ------- generic test -------
def test(problem): def test(problem, use_prototype=True):
"""Test solver on a given problem""" """Test solver on a given problem"""
#diag_select(".*") #diag_select(".*")
print "problem:" print "problem:"
print problem print problem
solver = GeometricSolver(problem) solver = GeometricSolver(problem, use_prototype)
#solver.set_prototype_selection(use_prototype)
print "drplan:" print "drplan:"
print solver.dr print solver.dr
print "number of top-level rigids:",len(solver.dr.top_level()) print "number of top-level rigids:",len(solver.dr.top_level())
@ -827,6 +856,57 @@ def runstats():
stats_parametric_incremental() stats_parametric_incremental()
stats_parametric() stats_parametric()
def selection_test():
problem = GeometricProblem(dimension=3)
problem.add_point('v1', vector([0.0, 0.0, 0.0]))
problem.add_point('v2', vector([1.0, 0.0, 0.0]))
problem.add_point('v3', vector([0.0, 1.0, 0.0]))
problem.add_point('v4', vector([0.5, 0.5, 1.0]))
problem.add_point('v5', vector([0.5, 0.5,-1.0]))
problem.add_constraint(DistanceConstraint('v1', 'v2', 10.0))
problem.add_constraint(DistanceConstraint('v1', 'v3', 10.0))
problem.add_constraint(DistanceConstraint('v2', 'v3', 10.0))
problem.add_constraint(DistanceConstraint('v1', 'v4', 10.0))
problem.add_constraint(DistanceConstraint('v2', 'v4', 10.0))
problem.add_constraint(DistanceConstraint('v3', 'v4', 10.0))
problem.add_constraint(DistanceConstraint('v1', 'v5', 10.0))
problem.add_constraint(DistanceConstraint('v2', 'v5', 10.0))
problem.add_constraint(DistanceConstraint('v3', 'v5', 10.0))
s1 = SelectionConstraint(is_right_handed, ['v1','v2','v4','v5'])
# add selection con
problem.add_constraint(s1)
# solve
solver = GeometricSolver(problem, False)
print len(solver.get_solutions()), "solutions"
# remove and add constraint
print "removing s1"
problem.rem_constraint(s1)
# solve again
print len(solver.get_solutions()), "solutions"
# remove and add constraint
print "adding s1"
problem.add_constraint(s1)
# solve again
print len(solver.get_solutions()), "solutions"
# remove distance
print "removing and re-adding d15"
problem.rem_constraint(problem.get_distance("v1","v5"))
problem.add_constraint(DistanceConstraint('v1', 'v5', 10.0))
# solve again
print len(solver.get_solutions()), "solutions"
def runtests(): def runtests():
#diag_select("clsolver3D") #diag_select("clsolver3D")
#test(double_banana_plus_one_problem()) #test(double_banana_plus_one_problem())
@ -838,6 +918,11 @@ def runtests():
#test(fix1_problem_3d()) #test(fix1_problem_3d())
#test(fix2_problem_3d()) #test(fix2_problem_3d())
#test(fix3_problem_3d()) #test(fix3_problem_3d())
test(block("BB", 4.0,2.5,5.0)) #test(block("BB", 4.0,2.5,5.0))
#diag_select("SelectionMethod.*")
#selection_test()
test(selection_problem(),False)
if __name__ == "__main__": runtests() if __name__ == "__main__": runtests()