diff --git a/geosolver/clsolver.py b/geosolver/clsolver.py index fbb7bf6..af1b090 100644 --- a/geosolver/clsolver.py +++ b/geosolver/clsolver.py @@ -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,21 +77,75 @@ class PrototypeMethod(MultiMethod): assert len(conf.vars()) == 1 var = conf.vars()[0] selmap[var] = conf.map[var] - selconf = Configuration(selmap) - sat = True + if len(selmap) == 0: + selconf = {} + else: + selconf = Configuration(selmap) 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") + diag_print("constraint satisfied? "+str(satcon), "PrototypeMethod.multi_execute") + sat = sat and satcon + diag_print("prototype satisfied? "+str(sat), "PrototypeMethod.multi_execute") + if sat: + 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: - satcon = con.satisfied(inconf.map) != con.satisfied(selconf.map) - diag_print("constraint = "+str(con), "PrototypeMethod.multi_execute") - diag_print("constraint satisfied? "+str(satcon), "PrototypeMethod.multi_execute") + 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("prototype satisfied? "+str(sat), "PrototypeMethod.multi_execute") + 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 connected = Set() @@ -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,11 +182,15 @@ 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""" return self._graph.outgoing_vertices("_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,28 +484,67 @@ 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, 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_solution_selector(self, merge): - return - 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)) - # -------------- # search methods @@ -458,7 +553,7 @@ class ClusterSolver(Notifier): def _process_new(self): while len(self._new) > 0: newobject = self._new.pop() - diag_print ("search from "+str(newobject), "clsolver") + diag_print("search from "+str(newobject), "clsolver") succes = self._search(newobject) if succes and self.is_top_level(newobject): # maybe more rules applicable.... push back on stack @@ -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): diff --git a/geosolver/clsolver3D.py b/geosolver/clsolver3D.py index d8fb120..6d46553 100644 --- a/geosolver/clsolver3D.py +++ b/geosolver/clsolver3D.py @@ -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): diff --git a/geosolver/cluster.py b/geosolver/cluster.py index 4bdd6f5..9c1fb5f 100644 --- a/geosolver/cluster.py +++ b/geosolver/cluster.py @@ -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 ----- diff --git a/geosolver/geometric.py b/geosolver/geometric.py index baa9278..10d0768 100644 --- a/geosolver/geometric.py +++ b/geosolver/geometric.py @@ -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 @@ -247,6 +244,9 @@ class GeometricSolver (Listener): else: 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) @@ -256,19 +256,31 @@ 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): - self._add_constraint(con) + # 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""" @@ -336,14 +348,22 @@ class GeometricSolver (Listener): parent.subs.append(map[inp]) # combine clusters due to selection - for method in self.dr.methods(): - if isinstance(method, PrototypeMethod): - incluster = method.inputs()[0] - outcluster = method.outputs()[0] - geoin = map[incluster] - geoout = map[outcluster] - geoout.subs = list(geoin.subs) - + if False: + for method in self.dr.methods(): + if isinstance(method, PrototypeMethod): + incluster = method.inputs()[0] + outcluster = method.outputs()[0] + 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()) if len(rigids) == 0: @@ -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 = "" diff --git a/geosolver/intersections.py b/geosolver/intersections.py index 7c87e87..415e21a 100644 --- a/geosolver/intersections.py +++ b/geosolver/intersections.py @@ -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 ------- diff --git a/test/test.py b/test/test.py index 9157cad..29b9125 100644 --- a/test/test.py +++ b/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,7 +460,33 @@ 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()