added support for SelectionConstraint
This commit is contained in:
parent
22e159a5ad
commit
99b304c882
|
@ -52,8 +52,8 @@ class PrototypeMethod(MultiMethod):
|
|||
the protoype and the solution satisfy the same constraints.
|
||||
"""
|
||||
|
||||
def __init__(self, incluster, selclusters, outcluster, constraints):
|
||||
self._inputs = [incluster]+selclusters
|
||||
def __init__(self, incluster, selclusters, outcluster, constraints, enabled):
|
||||
self._inputs = [incluster]+selclusters+[enabled]
|
||||
self._outputs = [outcluster]
|
||||
self._constraints = constraints
|
||||
MultiMethod.__init__(self)
|
||||
|
@ -62,11 +62,14 @@ class PrototypeMethod(MultiMethod):
|
|||
diag_print("PrototypeMethod.multi_execute called","clmethods")
|
||||
incluster = self._inputs[0]
|
||||
selclusters = []
|
||||
for i in range(1,len(self._inputs)):
|
||||
for i in range(1,len(self._inputs)-1):
|
||||
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")
|
||||
# get confs
|
||||
diag_print("enabledvar"+str(enabledvar), "PrototypeMethod.multi_execute")
|
||||
# get confs/values
|
||||
enabledval = inmap[enabledvar]
|
||||
inconf = inmap[incluster]
|
||||
selmap = {}
|
||||
for cluster in selclusters:
|
||||
|
@ -74,10 +77,16 @@ class PrototypeMethod(MultiMethod):
|
|||
assert len(conf.vars()) == 1
|
||||
var = conf.vars()[0]
|
||||
selmap[var] = conf.map[var]
|
||||
if len(selmap) == 0:
|
||||
selconf = {}
|
||||
else:
|
||||
selconf = Configuration(selmap)
|
||||
sat = True
|
||||
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:
|
||||
satcon = con.satisfied(inconf.map) != con.satisfied(selconf.map)
|
||||
diag_print("constraint = "+str(con), "PrototypeMethod.multi_execute")
|
||||
|
@ -88,6 +97,54 @@ class PrototypeMethod(MultiMethod):
|
|||
return [inconf]
|
||||
else:
|
||||
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):
|
||||
infinc = True
|
||||
|
@ -110,8 +167,11 @@ class ClusterSolver(Notifier):
|
|||
|
||||
def __init__(self, dimension):
|
||||
"""Create a new empty solver"""
|
||||
# init superclasses
|
||||
Notifier.__init__(self)
|
||||
# store arguments
|
||||
self.dimension = dimension
|
||||
# init instance vars
|
||||
self._graph = Graph()
|
||||
self._graph.add_vertex("_root")
|
||||
self._graph.add_vertex("_toplevel")
|
||||
|
@ -122,10 +182,14 @@ class ClusterSolver(Notifier):
|
|||
self._graph.add_vertex("_hedgehogs")
|
||||
self._graph.add_vertex("_balloons")
|
||||
self._graph.add_vertex("_methods")
|
||||
# queue of new objects to process
|
||||
self._new = []
|
||||
# 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):
|
||||
"""get list of variables"""
|
||||
|
@ -209,9 +273,29 @@ class ClusterSolver(Notifier):
|
|||
def contains(self, 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 --------
|
||||
|
||||
# -- general house hold
|
||||
# --- dependencies and groups
|
||||
|
||||
def _add_dependency(self, on, dependend):
|
||||
"""Add a dependence for second object on first object"""
|
||||
|
@ -244,45 +328,6 @@ class ClusterSolver(Notifier):
|
|||
if object in self._new:
|
||||
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):
|
||||
"""find all descendend objects of v (dirdctly or indirectly dependend"""
|
||||
front = [v]
|
||||
|
@ -406,16 +451,27 @@ class ClusterSolver(Notifier):
|
|||
# remove inputs from toplevel
|
||||
for cluster in merge.inputs():
|
||||
self._rem_top_level(cluster)
|
||||
# add prototype selection method
|
||||
self._add_prototype_selector(merge)
|
||||
# add solution selection method
|
||||
self._add_solution_selector(merge)
|
||||
# add selection methods
|
||||
output2 = self._add_prototype_selector(merge)
|
||||
output3 = self._add_solution_selector(output2)
|
||||
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):
|
||||
incluster = merge.outputs()[0]
|
||||
constraints = merge.prototype_constraints()
|
||||
if len(constraints) == 0:
|
||||
return
|
||||
vars = Set()
|
||||
for con in constraints:
|
||||
vars.union_update(con.variables())
|
||||
|
@ -428,27 +484,66 @@ class ClusterSolver(Notifier):
|
|||
raise StandardError, "no prototype cluster for variable "+str(v)
|
||||
selclusters.append(clusters[0])
|
||||
outcluster = incluster.copy()
|
||||
# Rick 20090519 - copy does not copy structural overconstrained flag?
|
||||
outcluster.overconstrained = incluster.overconstrained
|
||||
selector = PrototypeMethod(incluster, selclusters, outcluster, constraints)
|
||||
selector = PrototypeMethod(incluster, selclusters, outcluster, constraints, self._prototype_selection_var)
|
||||
self._add_cluster(outcluster)
|
||||
self._add_method(selector)
|
||||
self._rem_top_level(incluster)
|
||||
return
|
||||
return outcluster
|
||||
|
||||
def _add_solution_selector(self, merge):
|
||||
return
|
||||
def _add_solution_selector(self, incluster):
|
||||
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):
|
||||
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):
|
||||
"""returns True iff input_cluster is root cluster or was determined by
|
||||
merging with the root cluster."""
|
||||
|
@ -501,6 +639,8 @@ class ClusterSolver(Notifier):
|
|||
return False
|
||||
#def
|
||||
|
||||
# ---- consistency
|
||||
|
||||
def _is_consistent_pair(self, object1, object2):
|
||||
diag_print("in is_consistent_pair "+str(object1)+" "+str(object2),"clsolver")
|
||||
oc = over_constraints(object1, object2)
|
||||
|
@ -654,36 +794,36 @@ class ClusterSolver(Notifier):
|
|||
## return None
|
||||
##
|
||||
|
||||
def _known_angle(self,a,b,c):
|
||||
"""returns Balloon, Rigid or Hedgehog that contains angle(a, b, c)"""
|
||||
if a==b or a==c or b==c:
|
||||
raise StandardError, "all vars in angle must be different"
|
||||
# get objects dependend on a, b and c
|
||||
dep_a = self._graph.outgoing_vertices(a)
|
||||
dep_b = self._graph.outgoing_vertices(b)
|
||||
dep_c = self._graph.outgoing_vertices(c)
|
||||
dependend = []
|
||||
for obj in dep_a:
|
||||
if obj in dep_b and obj in dep_c:
|
||||
dependend.append(obj)
|
||||
# find a hedgehog
|
||||
hogs = filter(lambda x: isinstance(x,Hedgehog), dependend)
|
||||
hogs = filter(lambda hog: hog.cvar == b, hogs)
|
||||
hogs = filter(lambda x: self.is_top_level(x), hogs)
|
||||
if len(hogs) == 1: return hogs[0]
|
||||
if len(hogs) > 1: raise "error: angle in more than one hedgehogs"
|
||||
# or find a cluster
|
||||
clusters = filter(lambda x: isinstance(x,Rigid), dependend)
|
||||
clusters = filter(lambda x: self.is_top_level(x), clusters)
|
||||
if len(clusters) == 1: return clusters[0]
|
||||
if len(clusters) > 1: raise "error: angle in more than one Rigids"
|
||||
# or find a balloon
|
||||
balloons = filter(lambda x: isinstance(x,Balloon), dependend)
|
||||
balloons = filter(lambda x: self.is_top_level(x), balloons)
|
||||
if len(balloons) == 1: return balloons[0]
|
||||
if len(balloons) > 1: raise "error: angle in more than one Balloons"
|
||||
# or return None
|
||||
return None
|
||||
## def _known_angle(self,a,b,c):
|
||||
## """returns Balloon, Rigid or Hedgehog that contains angle(a, b, c)"""
|
||||
## if a==b or a==c or b==c:
|
||||
## raise StandardError, "all vars in angle must be different"
|
||||
## # get objects dependend on a, b and c
|
||||
## dep_a = self._graph.outgoing_vertices(a)
|
||||
## dep_b = self._graph.outgoing_vertices(b)
|
||||
## dep_c = self._graph.outgoing_vertices(c)
|
||||
## dependend = []
|
||||
## for obj in dep_a:
|
||||
## if obj in dep_b and obj in dep_c:
|
||||
## dependend.append(obj)
|
||||
## # find a hedgehog
|
||||
## hogs = filter(lambda x: isinstance(x,Hedgehog), dependend)
|
||||
## hogs = filter(lambda hog: hog.cvar == b, hogs)
|
||||
## hogs = filter(lambda x: self.is_top_level(x), hogs)
|
||||
## if len(hogs) == 1: return hogs[0]
|
||||
## if len(hogs) > 1: raise "error: angle in more than one hedgehogs"
|
||||
## # or find a cluster
|
||||
## clusters = filter(lambda x: isinstance(x,Rigid), dependend)
|
||||
## clusters = filter(lambda x: self.is_top_level(x), clusters)
|
||||
## if len(clusters) == 1: return clusters[0]
|
||||
## if len(clusters) > 1: raise "error: angle in more than one Rigids"
|
||||
## # or find a balloon
|
||||
## balloons = filter(lambda x: isinstance(x,Balloon), dependend)
|
||||
## balloons = filter(lambda x: self.is_top_level(x), balloons)
|
||||
## if len(balloons) == 1: return balloons[0]
|
||||
## if len(balloons) > 1: raise "error: angle in more than one Balloons"
|
||||
## # or return None
|
||||
## return None
|
||||
|
||||
##def _is_source(self, object, constraint):
|
||||
## if not self._contains_constraint(object, constraint):
|
||||
|
|
|
@ -76,14 +76,14 @@ class ClusterSolver3D(ClusterSolver):
|
|||
|
||||
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 (also returning a set).
|
||||
be retrieved with the get method.
|
||||
"""
|
||||
|
||||
# ------- PUBLIC METHODS --------
|
||||
|
||||
def __init__(self):
|
||||
"""Instantiate a ClusterSolver3D"""
|
||||
ClusterSolver.__init__(self, dimension=3)
|
||||
ClusterSolver.__init__(self, 3)
|
||||
self.rootcluster = None
|
||||
|
||||
# overriding ClusterSolver.set_root
|
||||
|
@ -220,14 +220,11 @@ class ClusterSolver3D(ClusterSolver):
|
|||
merge.restore_toplevel.append(cluster)
|
||||
else:
|
||||
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
|
||||
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
|
||||
|
||||
def _add_root_method(self,inclusters,outcluster):
|
||||
|
|
|
@ -136,7 +136,9 @@ class Rigid(Cluster):
|
|||
return s
|
||||
|
||||
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
|
||||
|
||||
def copy(self):
|
||||
return Hedgehog(self.cvar, self.xvars)
|
||||
new = Hedgehog(self.cvar, self.xvars)
|
||||
new.overconstrained = self.overconstrained
|
||||
return new
|
||||
|
||||
|
||||
class Balloon(Cluster):
|
||||
|
@ -190,8 +194,9 @@ class Balloon(Cluster):
|
|||
return s
|
||||
|
||||
def copy(self):
|
||||
return Balloon(self.vars)
|
||||
|
||||
new = Balloon(self.vars)
|
||||
new.overconstrained = self.overconstrained
|
||||
return new
|
||||
|
||||
# ----- function to determine overconstraints -----
|
||||
|
||||
|
|
|
@ -2,19 +2,17 @@
|
|||
problems incrementally."""
|
||||
|
||||
import vector
|
||||
from clsolver import PrototypeMethod
|
||||
# depricated! from clsolver2D import ClusterSolver2D
|
||||
import math
|
||||
from clsolver import PrototypeMethod, SelectionMethod
|
||||
from clsolver3D import ClusterSolver3D
|
||||
from cluster import Rigid, Hedgehog
|
||||
from configuration import Configuration
|
||||
import math
|
||||
from diagnostic import diag_print
|
||||
from constraint import Constraint, ConstraintGraph
|
||||
from notify import Notifier, Listener
|
||||
from tolerance import tol_eq
|
||||
from intersections import angle_3p, distance_2p
|
||||
from selconstr import SelectionConstraint
|
||||
from sets import Set
|
||||
|
||||
# ----------- GeometricProblem -------------
|
||||
|
||||
|
@ -226,8 +224,7 @@ class GeometricSolver (Listener):
|
|||
"""
|
||||
|
||||
# public methods
|
||||
|
||||
def __init__(self, problem):
|
||||
def __init__(self, problem, use_prototype=True):
|
||||
"""Create a new GeometricSolver instance
|
||||
|
||||
keyword args
|
||||
|
@ -248,6 +245,9 @@ class GeometricSolver (Listener):
|
|||
raise StandardError, "Do not know how to solve problems of dimension > 3."
|
||||
self._map = {}
|
||||
|
||||
# enable prototype based selection by default
|
||||
self.set_prototype_selection(use_prototype)
|
||||
|
||||
# register
|
||||
self.cg.add_listener(self)
|
||||
self.dr.add_listener(self)
|
||||
|
@ -256,20 +256,32 @@ class GeometricSolver (Listener):
|
|||
self.fixvars = []
|
||||
self.fixcluster = None
|
||||
|
||||
# map current cg
|
||||
# add variables
|
||||
for var in self.cg.variables():
|
||||
self._add_variable(var)
|
||||
|
||||
# add distances first? Nicer decomposition in Rigids
|
||||
for con in self.cg.constraints():
|
||||
# add 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):
|
||||
self._add_constraint(con)
|
||||
toadd.remove(con)
|
||||
|
||||
# add angles and other constraints first? Better performance
|
||||
for con in self.cg.constraints():
|
||||
if not isinstance(con, DistanceConstraint):
|
||||
# add other constraints.
|
||||
for con in toadd:
|
||||
self._add_constraint(con)
|
||||
|
||||
def set_prototype_selection(self, enabled):
|
||||
self.dr.set_prototype_selection(enabled)
|
||||
|
||||
def get_constrainedness(self):
|
||||
"""Depricated. Use get_statis instead"""
|
||||
return self.get_status()
|
||||
|
@ -336,6 +348,7 @@ class GeometricSolver (Listener):
|
|||
parent.subs.append(map[inp])
|
||||
|
||||
# combine clusters due to selection
|
||||
if False:
|
||||
for method in self.dr.methods():
|
||||
if isinstance(method, PrototypeMethod):
|
||||
incluster = method.inputs()[0]
|
||||
|
@ -343,6 +356,13 @@ class GeometricSolver (Listener):
|
|||
geoin = map[incluster]
|
||||
geoout = map[outcluster]
|
||||
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
|
||||
rigids = filter(lambda c: isinstance(c, Rigid), self.dr.top_level())
|
||||
|
@ -366,12 +386,12 @@ class GeometricSolver (Listener):
|
|||
|
||||
def get_solutions(self):
|
||||
"""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
|
||||
list and the configurations should not be changed (since they are
|
||||
references to objects in the solver)."""
|
||||
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])
|
||||
else:
|
||||
return []
|
||||
|
@ -496,7 +516,7 @@ class GeometricSolver (Listener):
|
|||
self._update_fix()
|
||||
elif isinstance(con, SelectionConstraint):
|
||||
# add directly to clustersolver
|
||||
self.dr.add(con)
|
||||
self.dr.add_selection_constraint(con)
|
||||
else:
|
||||
## raise StandardError, "unknown constraint type"
|
||||
pass
|
||||
|
@ -516,6 +536,9 @@ class GeometricSolver (Listener):
|
|||
self.fixcluster = Rigid(self.fixvars)
|
||||
self.dr.add(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:
|
||||
self.dr.remove(self._map[con])
|
||||
del self._map[con]
|
||||
|
@ -641,8 +664,7 @@ class GeometricCluster:
|
|||
|
||||
# make done
|
||||
if done == None:
|
||||
done = Set()
|
||||
|
||||
done = set()
|
||||
|
||||
# recurse
|
||||
s = ""
|
||||
|
|
|
@ -247,7 +247,8 @@ def is_left_handed(p1,p2,p3,p4):
|
|||
v = p3-p1
|
||||
uv = vector.cross(u,v)
|
||||
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):
|
||||
"""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
|
||||
uv = vector.cross(u,v)
|
||||
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 -------
|
||||
|
||||
|
|
91
test/test.py
91
test/test.py
|
@ -5,6 +5,8 @@ from geosolver.geometric import *
|
|||
from geosolver.vector import vector
|
||||
from geosolver.randomproblem import *
|
||||
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
|
||||
from time import time
|
||||
|
||||
|
@ -458,6 +460,32 @@ def twoscisors():
|
|||
problem.add_constraint(DistanceConstraint('D', 'C', 6.0))
|
||||
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 -------
|
||||
|
@ -707,12 +735,13 @@ def test_ada_3d():
|
|||
|
||||
# ------- generic test -------
|
||||
|
||||
def test(problem):
|
||||
def test(problem, use_prototype=True):
|
||||
"""Test solver on a given problem"""
|
||||
#diag_select(".*")
|
||||
print "problem:"
|
||||
print problem
|
||||
solver = GeometricSolver(problem)
|
||||
solver = GeometricSolver(problem, use_prototype)
|
||||
#solver.set_prototype_selection(use_prototype)
|
||||
print "drplan:"
|
||||
print solver.dr
|
||||
print "number of top-level rigids:",len(solver.dr.top_level())
|
||||
|
@ -827,6 +856,57 @@ def runstats():
|
|||
stats_parametric_incremental()
|
||||
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():
|
||||
#diag_select("clsolver3D")
|
||||
#test(double_banana_plus_one_problem())
|
||||
|
@ -838,6 +918,11 @@ def runtests():
|
|||
#test(fix1_problem_3d())
|
||||
#test(fix2_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()
|
||||
|
|
Loading…
Reference in New Issue
Block a user