Bugfix: Fix-constraint now works.

Implemented a mechanism for setting the root cluster so that cluster-merging
takes place in the correct order. For each cluster, a root variable is 
created, which is used in cluster-merging methods to choose which 
cluster to transform, and which too keep fixed. The root-variable 
of the output cluster is related to root-variables of the input
clusters by adding a logical OR method (OrMethod) to the 
method graph.

Rick
This commit is contained in:
kwikrick 2009-10-06 15:26:25 +00:00
parent 9fd7fdb797
commit 4fe301a5e6
8 changed files with 267 additions and 186 deletions

View File

@ -44,6 +44,8 @@ class ClusterMethod(MultiMethod):
s += "wellconstrained" s += "wellconstrained"
return s return s
def input_clusters(self):
return filter(lambda var: isinstance(var, Cluster), self.inputs())
class PrototypeMethod(MultiMethod): class PrototypeMethod(MultiMethod):
"""A PrototypeMethod selects those solutions of a cluster for which """A PrototypeMethod selects those solutions of a cluster for which
@ -91,7 +93,7 @@ def is_information_increasing(method):
infinc = True infinc = True
connected = Set() connected = Set()
output = method.outputs()[0] output = method.outputs()[0]
for cluster in method.inputs(): for cluster in method.input_clusters():
if num_constraints(cluster.intersection(output)) >= num_constraints(output): if num_constraints(cluster.intersection(output)) >= num_constraints(output):
infinc = False infinc = False
break break

View File

@ -9,6 +9,7 @@ from configuration import Configuration
from cluster import * from cluster import *
from map import Map from map import Map
from gmatch import gmatch from gmatch import gmatch
from method import OrMethod
def pattern2graph(pattern): def pattern2graph(pattern):
"""convert pattern to pattern graph""" """convert pattern to pattern graph"""
@ -55,6 +56,10 @@ def reference2graph(nlet):
#diag_print("reference graph:"+str(rgraph),"match"); #diag_print("reference graph:"+str(rgraph),"match");
return rgraph 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.
@ -79,6 +84,18 @@ class ClusterSolver3D(ClusterSolver):
def __init__(self): def __init__(self):
"""Instantiate a ClusterSolver3D""" """Instantiate a ClusterSolver3D"""
ClusterSolver.__init__(self, dimension=3) ClusterSolver.__init__(self, dimension=3)
self.rootcluster = None
# overriding ClusterSolver.set_root
def set_root(self, cluster):
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 -------- # ------------ INTERNALLY USED METHODS --------
@ -90,7 +107,7 @@ class ClusterSolver3D(ClusterSolver):
else: else:
method = self._determining_method(cluster) method = self._determining_method(cluster)
sources = Set() sources = Set()
for inp in method.inputs(): for inp in method.input_clusters():
sources.union_update(self._all_sources_constraint_in_cluster(constraint, inp)) sources.union_update(self._all_sources_constraint_in_cluster(constraint, inp))
return sources return sources
@ -99,7 +116,7 @@ class ClusterSolver3D(ClusterSolver):
# -------------- # --------------
def _search(self, newcluster): def _search(self, newcluster):
print "search from:", newcluster diag_print("search from: "+str(newcluster),"clsolver3D")
# find all toplevel clusters connected to newcluster via one or more variables # find all toplevel clusters connected to newcluster via one or more variables
connected = Set() connected = Set()
for var in newcluster.vars: for var in newcluster.vars:
@ -112,80 +129,12 @@ class ClusterSolver3D(ClusterSolver):
return True return True
return False return False
# end _search
def _search_old(self, newcluster):
#print "search:", newcluster
# 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)
connected.remove(newcluster)
#print "connected:", connected
# make pairs
pairs = Set()
for cluster in connected:
pair = Set([newcluster, cluster])
pairs.add(pair)
#print "pairs:",pairs
# try merging pairs
for pair in pairs:
if self._try_method(pair):
return True
# make triplets
triplets = Set()
for pair in pairs:
for cluster in connected:
allconnected = True
for c in pair:
if c is cluster:
allconnected = False
break
shared = Set(cluster.vars).intersection(c.vars)
if len(shared) == 0:
allconnected = False
break
if allconnected:
triplet = pair.union([cluster])
triplets.add(triplet)
#print "triplets:",triplets
# try merging triplets
for triplet in triplets:
if self._try_method(triplet):
return True
## make quadlets
#quadlets = Set()
#for triplet in triplets:
# for cluster in connected:
# allconnected = True
# for c in triplet:
# if c is cluster:
# allconnected = False
# break
# shared = Set(cluster.vars).intersection(c.vars)
# if len(shared) == 0:
# allconnected = False
# break
# if allconnected:
# quadlet = triplet.union([cluster])
# quadlets.add(quadlet)
#diag_print("quadlets:"+str(quadlets),"clsolver3D")
## try merging quadlets
#for quadlet in quadlets:
# if self._try_method(quadlet):
# return True
return False
# end _search
def _try_method(self, nlet): def _try_method(self, nlet):
"""finds a possible rewrite rule applications on given set of clusters, applies it """finds a possible rewrite rule applications on given set of clusters, applies it
and returns True iff successfull and returns True iff successfull
""" """
refgraph = reference2graph(nlet) refgraph = reference2graph(nlet)
for methodclass in reversed([MergePR, MergeDR, MergeDDD, MergeADD, MergeDAD, MergeAA, MergeSD, MergeTTD, MergeRR]): for methodclass in [MergePR, MergeDR, MergeRR, MergeSR, DeriveTTD, DeriveDDD, DeriveADD, DeriveDAD, DeriveAA]:
matches = gmatch(methodclass.patterngraph, refgraph) matches = gmatch(methodclass.patterngraph, refgraph)
if len(matches) > 0: if len(matches) > 0:
diag_print("number of matches = "+str(len(matches)), "clsolver3D") diag_print("number of matches = "+str(len(matches)), "clsolver3D")
@ -215,7 +164,7 @@ class ClusterSolver3D(ClusterSolver):
dependend = self.find_dependend(var) dependend = self.find_dependend(var)
dependend = filter(lambda x: self.is_top_level(x), dependend) dependend = filter(lambda x: self.is_top_level(x), dependend)
connected.union_update(dependend) connected.union_update(dependend)
#for cluster in merge.inputs(): #for cluster in merge.input_clusters():
# if cluster in connected: # if cluster in connected:
# connected.remove(cluster) # connected.remove(cluster)
@ -223,15 +172,13 @@ class ClusterSolver3D(ClusterSolver):
for cluster in connected: for cluster in connected:
if num_constraints(cluster.intersection(output)) >= num_constraints(output): if num_constraints(cluster.intersection(output)) >= num_constraints(output):
# if self._is_consistent_pair(cluster, output):
# print "#overconstraint=",len(oc), "#constraints"=num_constraints(cluster)
infinc = False infinc = False
break break
diag_print("information increasing:"+str(infinc),"clsolver3D") diag_print("information increasing:"+str(infinc),"clsolver3D")
# check if method reduces number of clusters (reduc) # check if method reduces number of clusters (reduc)
nremove = 0 nremove = 0
for cluster in merge.inputs(): for cluster in merge.input_clusters():
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster): if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
# will be removed from toplevel # will be removed from toplevel
nremove += 1 nremove += 1
@ -246,10 +193,10 @@ class ClusterSolver3D(ClusterSolver):
# check consistency and local/global overconstrained # check consistency and local/global overconstrained
consistent = True consistent = True
local_oc = False local_oc = False
for i1 in range(0, len(merge.inputs())): for i1 in range(0, len(merge.input_clusters())):
for i2 in range(i1+1, len(merge.inputs())): for i2 in range(i1+1, len(merge.input_clusters())):
c1 = merge.inputs()[i1] c1 = merge.input_clusters()[i1]
c2 = merge.inputs()[i2] c2 = merge.input_clusters()[i2]
if num_constraints(c1.intersection(c2)) != 0: if num_constraints(c1.intersection(c2)) != 0:
local_oc = True local_oc = True
consistent = consistent and self._is_consistent_pair(c1, c2) consistent = consistent and self._is_consistent_pair(c1, c2)
@ -257,38 +204,66 @@ class ClusterSolver3D(ClusterSolver):
merge.overconstrained = local_oc merge.overconstrained = local_oc
# global overconstrained? (store in output cluster) # global overconstrained? (store in output cluster)
overconstrained = not consistent overconstrained = not consistent
for cluster in merge.inputs(): for cluster in merge.input_clusters():
overconstrained = overconstrained or cluster.overconstrained overconstrained = overconstrained or cluster.overconstrained
output.overconstrained = overconstrained output.overconstrained = overconstrained
# add to graph # add to graph
self._add_cluster(output) self._add_cluster(output)
self._add_method(merge) self._add_method(merge)
# remove inputs from top_level # remove input clusters from top_level
if not (hasattr(merge,"noremove") and merge.noremove == True):
merge.restore_toplevel = [] # make restore list in method merge.restore_toplevel = [] # make restore list in method
for cluster in merge.inputs(): for cluster in merge.input_clusters():
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster): if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
diag_print("remove from top-level: "+str(cluster),"clsolver3D") diag_print("remove from top-level: "+str(cluster),"clsolver3D")
self._rem_top_level(cluster) self._rem_top_level(cluster)
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 # add prototype selection method
self._add_prototype_selector(merge) self._add_prototype_selector(merge)
# add solution selection method # add solution selection method
self._add_solution_selector(merge) self._add_solution_selector(merge)
# add method to determine root-variable
self._add_root_method(merge.input_clusters(),merge.outputs()[0])
# pause # pause
return True 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 # class ClusterSolver3D
# ---------------------------------------------- # ----------------------------------------------
# ---------- Methods for 3D solving ------------- # ---------- Methods for 3D solving -------------
# ---------------------------------------------- # ----------------------------------------------
# Merge<X> methods take root-cluster in considerations
# Derive<X> methods do not take root cluster in consideration
class MergePR(ClusterMethod): class MergePR(ClusterMethod):
"""Represents a merging of a one-point cluster with any other rigid """Represents a merging of a one-point cluster with any other rigid."""
The first cluster determines the orientation of the resulting cluster
"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
in1 = map["$p"] in1 = map["$p"]
@ -297,7 +272,9 @@ class MergePR(ClusterMethod):
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
self._inputs = [in1, in2] in1root = rootname(in1)
in2root = rootname(in2)
self._inputs = [in1, in2, in1root, in2root]
self._outputs = [out] self._outputs = [out]
ClusterMethod.__init__(self) ClusterMethod.__init__(self)
@ -314,21 +291,22 @@ class MergePR(ClusterMethod):
def multi_execute(self, inmap): def multi_execute(self, inmap):
diag_print("MergePR.multi_execute called","clmethods") diag_print("MergePR.multi_execute called","clmethods")
c1 = self._inputs[0] #c1 = self._inputs[0]
c2 = self._inputs[1] #c2 = self._inputs[1]
conf1 = inmap[c1] conf1 = inmap[self._inputs[0]]
conf2 = inmap[c2] conf2 = inmap[self._inputs[1]]
#res = conf1.merge2D(conf2) isroot1 = inmap[self._inputs[2]]
#return [res] isroot2 = inmap[self._inputs[3]]
if len(c1.vars) == 1: if isroot1:
return [conf2.copy()] res = conf1.merge(conf2)
else: elif isroot2:
return [conf1.copy()] res = conf2.merge(conf1)
else: # cheapest
res = conf2.merge(conf1)
return [res]
class MergeDR(ClusterMethod): class MergeDR(ClusterMethod):
"""Represents a merging of a distance (two-point cluster) with a rigid """Represents a merging of a distance (two-point cluster) with a rigid."""
The first cluster determines the orientation of the resulting cluster
"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
in1 = map["$d"] in1 = map["$d"]
@ -337,7 +315,9 @@ class MergeDR(ClusterMethod):
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
self._inputs = [in1, in2] in1root = rootname(in1)
in2root = rootname(in2)
self._inputs = [in1, in2, in1root, in2root]
self._outputs = [out] self._outputs = [out]
ClusterMethod.__init__(self) ClusterMethod.__init__(self)
@ -358,17 +338,18 @@ class MergeDR(ClusterMethod):
c2 = self._inputs[1] c2 = self._inputs[1]
conf1 = inmap[c1] conf1 = inmap[c1]
conf2 = inmap[c2] conf2 = inmap[c2]
#res = conf1.merge2D(conf2) isroot1 = inmap[self._inputs[2]]
#return [res] isroot2 = inmap[self._inputs[3]]
if len(c1.vars) == 2: if isroot1:
return [conf2.copy()] res = conf1.merge(conf2)
else: elif isroot2:
return [conf1.copy()] res = conf2.merge(conf1)
else: # cheapest
res = conf2.merge(conf1)
return [res]
class MergeRR(ClusterMethod): class MergeRR(ClusterMethod):
"""Represents a merging of two rigids sharing three points (overconstrained). """Represents a merging of two rigids sharing three points."""
The first cluster determines the orientation of the resulting cluster
"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
in1 = map["$r1"] in1 = map["$r1"]
@ -376,7 +357,9 @@ class MergeRR(ClusterMethod):
# 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
self._inputs = [in1, in2] in1root = rootname(in1)
in2root = rootname(in2)
self._inputs = [in1, in2, in1root, in2root]
self._outputs = [out] self._outputs = [out]
ClusterMethod.__init__(self) ClusterMethod.__init__(self)
@ -397,9 +380,19 @@ class MergeRR(ClusterMethod):
c2 = self._inputs[1] c2 = self._inputs[1]
conf1 = inmap[c1] conf1 = inmap[c1]
conf2 = inmap[c2] conf2 = inmap[c2]
return [conf1.merge(conf2)] isroot1 = inmap[self._inputs[2]]
isroot2 = inmap[self._inputs[3]]
if isroot1 and not isroot2:
res = conf1.merge(conf2)
elif isroot2 and not isroot1:
res = conf2.merge(conf1)
elif len(c1.vars) < len(c2.vars): # cheapest
res = conf2.merge(conf1)
else:
res = conf1.merge(conf2)
return [res]
class MergeDDD(ClusterMethod): class DeriveDDD(ClusterMethod):
"""Represents a merging of three distances""" """Represents a merging of three distances"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
@ -415,6 +408,8 @@ class MergeDDD(ClusterMethod):
self._inputs = [self.d_ab, self.d_ac, self.d_bc] self._inputs = [self.d_ab, self.d_ac, self.d_bc]
self._outputs = [out] self._outputs = [out]
ClusterMethod.__init__(self) ClusterMethod.__init__(self)
# do not remove input clusters (because root not considered here)
self.noremove = True
def _pattern(): def _pattern():
pattern = [["rigid","$d_ab",["$a", "$b"]], pattern = [["rigid","$d_ab",["$a", "$b"]],
@ -426,12 +421,12 @@ class MergeDDD(ClusterMethod):
def __str__(self): def __str__(self):
s = "MergeDDD("+str(self._inputs[0])+"+"+str(self._inputs[1])+"+"+str(self._inputs[2])+"->"+str(self._outputs[0])+")" s = "DeriveDDD("+str(self._inputs[0])+"+"+str(self._inputs[1])+"+"+str(self._inputs[2])+"->"+str(self._outputs[0])+")"
s += "[" + self.status_str()+"]" s += "[" + self.status_str()+"]"
return s return s
def multi_execute(self, inmap): def multi_execute(self, inmap):
diag_print("MergeDDD.multi_execute called","clmethods") diag_print("DeriveDDD.multi_execute called","clmethods")
c12 = inmap[self.d_ab] c12 = inmap[self.d_ab]
c13 = inmap[self.d_ac] c13 = inmap[self.d_ac]
c23 = inmap[self.d_bc] c23 = inmap[self.d_bc]
@ -444,7 +439,7 @@ class MergeDDD(ClusterMethod):
solutions = solve_ddd_3D(v1,v2,v3,d12,d23,d31) solutions = solve_ddd_3D(v1,v2,v3,d12,d23,d31)
return solutions return solutions
class MergeTTD(ClusterMethod): class DeriveTTD(ClusterMethod):
"""Represents a derive of a tetra from six distances""" """Represents a derive of a tetra from six distances"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
@ -461,9 +456,11 @@ class MergeTTD(ClusterMethod):
self._inputs = [self.t_abc, self.t_abd, self.d_cd] self._inputs = [self.t_abc, self.t_abd, self.d_cd]
self._outputs = [out] self._outputs = [out]
ClusterMethod.__init__(self) ClusterMethod.__init__(self)
# do not remove input clusters (because root not considered here)
self.noremove = True
def __str__(self): def __str__(self):
s = "MergeTTD("+str(self._inputs[0])+\ s = "DeriveTTD("+str(self._inputs[0])+\
str(self._inputs[1])+\ str(self._inputs[1])+\
str(self._inputs[2])+\ str(self._inputs[2])+\
", ... -> "+\ ", ... -> "+\
@ -480,7 +477,7 @@ class MergeTTD(ClusterMethod):
patterngraph = _pattern() patterngraph = _pattern()
def multi_execute(self, inmap): def multi_execute(self, inmap):
diag_print("MergeTTD.multi_execute called","clmethods") diag_print("DeriveTTD.multi_execute called","clmethods")
c123 = inmap[self.t_abc] c123 = inmap[self.t_abc]
c124 = inmap[self.t_abd] c124 = inmap[self.t_abd]
c34 = inmap[self.d_cd] c34 = inmap[self.d_cd]
@ -502,8 +499,8 @@ class MergeTTD(ClusterMethod):
constraints.append(FunctionConstraint(fnot(is_right_handed),[self.a,self.b,self.c,self.d])) constraints.append(FunctionConstraint(fnot(is_right_handed),[self.a,self.b,self.c,self.d]))
return constraints return constraints
class MergeDAD(ClusterMethod): class DeriveDAD(ClusterMethod):
"""Represents a merging of three distances""" """Represents a merging of two distances and an angle"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
self.d_ab = map["$d_ab"] self.d_ab = map["$d_ab"]
@ -518,6 +515,8 @@ class MergeDAD(ClusterMethod):
self._inputs = [self.d_ab, self.a_abc, self.d_bc] self._inputs = [self.d_ab, self.a_abc, self.d_bc]
self._outputs = [out] self._outputs = [out]
ClusterMethod.__init__(self) ClusterMethod.__init__(self)
# do not remove input clusters (because root not considered here)
self.noremove = True
def _pattern(): def _pattern():
pattern = [["rigid","$d_ab",["$a", "$b"]], pattern = [["rigid","$d_ab",["$a", "$b"]],
@ -528,12 +527,12 @@ class MergeDAD(ClusterMethod):
patterngraph = _pattern() patterngraph = _pattern()
def __str__(self): def __str__(self):
s = "MergeDAD("+str(self._inputs[0])+"+"+str(self._inputs[1])+"+"+str(self._inputs[2])+"->"+str(self._outputs[0])+")" s = "DeriveDAD("+str(self._inputs[0])+"+"+str(self._inputs[1])+"+"+str(self._inputs[2])+"->"+str(self._outputs[0])+")"
s += "[" + self.status_str()+"]" s += "[" + self.status_str()+"]"
return s return s
def multi_execute(self, inmap): def multi_execute(self, inmap):
diag_print("MergeDDD.multi_execute called","clmethods") diag_print("DeriveDAD.multi_execute called","clmethods")
c12 = inmap[self.d_ab] c12 = inmap[self.d_ab]
c123 = inmap[self.a_abc] c123 = inmap[self.a_abc]
c23 = inmap[self.d_bc] c23 = inmap[self.d_bc]
@ -546,8 +545,8 @@ class MergeDAD(ClusterMethod):
solutions = solve_dad_3D(v1,v2,v3,d12,a123,d23) solutions = solve_dad_3D(v1,v2,v3,d12,a123,d23)
return solutions return solutions
class MergeADD(ClusterMethod): class DeriveADD(ClusterMethod):
"""Represents a merging of three distances""" """Represents a merging of one distance and to distances"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
self.a_cab = map["$a_cab"] self.a_cab = map["$a_cab"]
@ -562,6 +561,9 @@ class MergeADD(ClusterMethod):
self._inputs = [self.a_cab, self.d_ab, self.d_bc] self._inputs = [self.a_cab, self.d_ab, self.d_bc]
self._outputs = [out] self._outputs = [out]
ClusterMethod.__init__(self) ClusterMethod.__init__(self)
# do not remove input clusters (because root not considered here)
self.noremove = True
def _pattern(): def _pattern():
pattern = [["hedgehog","$a_cab",["$a", "$c", "$b"]], pattern = [["hedgehog","$a_cab",["$a", "$c", "$b"]],
@ -572,12 +574,12 @@ class MergeADD(ClusterMethod):
patterngraph = _pattern() patterngraph = _pattern()
def __str__(self): def __str__(self):
s = "MergeADD("+str(self._inputs[0])+"+"+str(self._inputs[1])+"+"+str(self._inputs[2])+"->"+str(self._outputs[0])+")" s = "DeriveADD("+str(self._inputs[0])+"+"+str(self._inputs[1])+"+"+str(self._inputs[2])+"->"+str(self._outputs[0])+")"
s += "[" + self.status_str()+"]" s += "[" + self.status_str()+"]"
return s return s
def multi_execute(self, inmap): def multi_execute(self, inmap):
diag_print("MergeADD.multi_execute called","clmethods") diag_print("DeriveADD.multi_execute called","clmethods")
c312 = inmap[self.a_cab] c312 = inmap[self.a_cab]
c12 = inmap[self.d_ab] c12 = inmap[self.d_ab]
c23 = inmap[self.d_bc] c23 = inmap[self.d_bc]
@ -590,7 +592,7 @@ class MergeADD(ClusterMethod):
solutions = solve_add_3D(v1,v2,v3,a312,d12,d23) solutions = solve_add_3D(v1,v2,v3,a312,d12,d23)
return solutions return solutions
class MergeAA(ClusterMethod): class DeriveAA(ClusterMethod):
"""Derive a scalable from two angles""" """Derive a scalable from two angles"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
@ -605,6 +607,10 @@ class MergeAA(ClusterMethod):
self._inputs = [self.a_cab, self.a_abc] self._inputs = [self.a_cab, self.a_abc]
self._outputs = [out] self._outputs = [out]
ClusterMethod.__init__(self) ClusterMethod.__init__(self)
# do not remove input clusters (because root not considered here)
self.noremove = True
def _pattern(): def _pattern():
pattern = [["hedgehog","$a_cab",["$a", "$c", "$b"]], pattern = [["hedgehog","$a_cab",["$a", "$c", "$b"]],
@ -614,12 +620,12 @@ class MergeAA(ClusterMethod):
patterngraph = _pattern() patterngraph = _pattern()
def __str__(self): def __str__(self):
s = "MergeAA("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")" s = "DeriveAA("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")"
s += "[" + self.status_str()+"]" s += "[" + self.status_str()+"]"
return s return s
def multi_execute(self, inmap): def multi_execute(self, inmap):
diag_print("MergeAA.multi_execute called","clmethods") diag_print("DeriveAA.multi_execute called","clmethods")
c312 = inmap[self.a_cab] c312 = inmap[self.a_cab]
c123 = inmap[self.a_abc] c123 = inmap[self.a_abc]
v1 = self.a v1 = self.a
@ -631,8 +637,8 @@ class MergeAA(ClusterMethod):
solutions = solve_ada_3D(v1,v2,v3,a312,d12,a123) solutions = solve_ada_3D(v1,v2,v3,a312,d12,a123)
return solutions return solutions
class MergeSD(ClusterMethod): class MergeSR(ClusterMethod):
"""Derive a Rigid from a Scalabe and a Rigid sharing two points""" """Merge a Rigid from a Scalabe and a Rigid sharing two points"""
def __init__(self, map): def __init__(self, map):
# check inputs # check inputs
in1 = map["$r"] in1 = map["$r"]
@ -651,12 +657,12 @@ class MergeSD(ClusterMethod):
patterngraph = _pattern() patterngraph = _pattern()
def __str__(self): def __str__(self):
s = "MergeSD("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")" s = "MergeSR("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")"
s += "[" + self.status_str()+"]" s += "[" + self.status_str()+"]"
return s return s
def multi_execute(self, inmap): def multi_execute(self, inmap):
diag_print("MergeSD.multi_execute called","clmethods") diag_print("MergeSR.multi_execute called","clmethods")
c1 = self._inputs[0] c1 = self._inputs[0]
c2 = self._inputs[1] c2 = self._inputs[1]
conf1 = inmap[c1] conf1 = inmap[c1]

View File

@ -319,18 +319,32 @@ class Configuration:
return self.hashvalue return self.hashvalue
def __str__(self): def __str__(self):
if self.underconstrained:
return "Configuration("+str(self.map)+" underconstrained)"
else:
return "Configuration("+str(self.map)+")" return "Configuration("+str(self.map)+")"
def testeq():
p1 = vector.vector([0.0,0.0,0.0])
p2 = vector.vector([1.0,0.0,0.0])
p3 = vector.vector([0.0,1.0,0.0])
c1 = Configuration({1:p1,2:p2,3:p3})
q1 = vector.vector([0.0,0.0,0.0])
q2 = vector.vector([1.0,0.0,0.0])
q3 = vector.vector([0.0,-1.0,0.0])
c2 = Configuration({1:q1,2:q2,3:q3})
print c1 == c2
def test(): def test():
p1 = vector.vector([0.0,0.0,0.0]) p1 = vector.vector([0.0,0.0,0.0])
p2 = vector.vector([1.0,0.0,0.0]) p2 = vector.vector([1.0,0.0,0.0])
p3 = vector.vector([0.0,1.0,0.0]) p3 = vector.vector([0.0,1.0,0.0])
c1 = Configuration({1:p1,2:p2}) p = Configuration({1:p1,2:p2,3:p3})
q1 = vector.vector([0.0,0.0,0.0]) q1 = vector.vector([0.0,0.0,3.0])
q2 = vector.vector([1.0,0.0,0.0]) q = Configuration({1:q1})
q3 = vector.vector([0.0,-1.0,0.0]) print p.merge(q)
c2 = Configuration({1:q1,2:q2}) print q.merge(p)
print c1 == c2
if __name__ == "__main__": test() if __name__ == "__main__": test()

View File

@ -2,7 +2,7 @@
problems incrementally.""" problems incrementally."""
import vector import vector
from clsolver import PrototypeMethod, is_information_increasing from clsolver import PrototypeMethod
from clsolver2D import ClusterSolver2D from clsolver2D import ClusterSolver2D
from clsolver3D import ClusterSolver3D from clsolver3D import ClusterSolver3D
from cluster import Rigid, Hedgehog from cluster import Rigid, Hedgehog
@ -309,7 +309,6 @@ class GeometricSolver (Listener):
# determine subclusters # determine subclusters
for method in self.dr.methods(): for method in self.dr.methods():
#if is_information_increasing(method):
for out in method.outputs(): for out in method.outputs():
if isinstance(out, Rigid): if isinstance(out, Rigid):
parent = map[out] parent = map[out]
@ -414,11 +413,12 @@ class GeometricSolver (Listener):
elif isinstance(con, FixConstraint): elif isinstance(con, FixConstraint):
if self.fixcluster != None: if self.fixcluster != None:
self.dr.remove(self.fixcluster) self.dr.remove(self.fixcluster)
self.fixvars.append(self.get(con.variables()[0])) self.fixvars.append(con.variables()[0])
#if len(self.fixvars) >= 1:
if len(self.fixvars) >= self.problem.dimension: if len(self.fixvars) >= self.problem.dimension:
self.fixcluster = Cluster(self.fixvars) self.fixcluster = Rigid(self.fixvars)
self.dr.add(self.fixcluster) self.dr.add(self.fixcluster)
self.dr.set_root(fixcluster) self.dr.set_root(self.fixcluster)
self._update_fix() self._update_fix()
else: else:
## raise StandardError, "unknown constraint type" ## raise StandardError, "unknown constraint type"
@ -429,13 +429,13 @@ class GeometricSolver (Listener):
if isinstance(con,FixConstraint): if isinstance(con,FixConstraint):
if self.fixcluster != None: if self.fixcluster != None:
self.dr.remove(self.fixcluster) self.dr.remove(self.fixcluster)
var = self.get(con.variables()[0]) var = con.variables()[0]
if var in self.fixvars: if var in self.fixvars:
self.fixvars.remove(var) self.fixvars.remove(var)
if len(self.fixvars) < self.problem.dimension: if len(self.fixvars) < self.problem.dimension:
self.fixcluster = None self.fixcluster = None
else: else:
self.fixcluster = Cluster(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 con in self._map: elif con in self._map:
@ -492,14 +492,14 @@ class GeometricSolver (Listener):
def _update_fix(self): def _update_fix(self):
if self.fixcluster: if self.fixcluster:
vars = fixcluster.vars vars = self.fixcluster.vars
map = {} map = {}
for var in vars: for var in vars:
map[var] = self.problem.get_fix(var).get_parameter() map[var] = self.problem.get_fix(var).get_parameter()
conf = Configuration(map) conf = Configuration(map)
self.dr.set(fixcluster, [conf]) self.dr.set(self.fixcluster, [conf])
else: else:
print "warning: no fixcluster to update" diag_print("no fixcluster to update","geometric")
pass pass
#class GeometricSolver #class GeometricSolver
@ -611,8 +611,13 @@ class FixConstraint(ParametricConstraint):
def satisfied(self, mapping): def satisfied(self, mapping):
"""return True iff mapping from variable names to points satisfies constraint""" """return True iff mapping from variable names to points satisfies constraint"""
a = mapping[self._variables[0]] point = mapping[self._variables[0]]
result = tol_eq(a[0], self._value[0]) and tol_eq(a[1], self.value[1]) if len(point) != len(self._value):
diag_print("warning: FixConstraint.satisfied: vectors of unequal length", "geometric.FixConstraint.satisfied")
return False
result = True;
for i in range(len(self._value)):
result &= tol_eq(point[i], self._value[i])
return result return result
def __str__(self): def __str__(self):

View File

@ -132,6 +132,9 @@ class MethodGraph:
"""return a list of methods""" """return a list of methods"""
return self._methods.keys() return self._methods.keys()
def contains(self, object):
return self._graph.has_vertex(object)
def add_variable(self, varname, value = None): def add_variable(self, varname, value = None):
"""Add a variable, optionally with a value""" """Add a variable, optionally with a value"""
if not varname in self._map: if not varname in self._map:
@ -298,6 +301,30 @@ class MethodGraph:
# ----------- various Methods --------- # ----------- various Methods ---------
class OrMethod(Method):
def __init__(self, inputs, output):
"""new method output := input[0] | input[1] | ... """
self._inputs = list(inputs)
self._outputs = [output]
def execute(self, inmap):
result = False
for input in self._inputs:
result = result | inmap[input]
outmap = {}
outmap[self._outputs[0]] = result
return outmap
def __str__(self):
s = "OrMethod("
s += str(self._inputs[0])
s += ','
s += str(self._inputs[1])
s += ','
s += str(self._outputs[0])
s += ')'
return s
class AddMethod(Method): class AddMethod(Method):
def __init__(self, a, b, c): def __init__(self, a, b, c):
"""new method c := a + b""" """new method c := a + b"""

View File

@ -14,7 +14,7 @@ class MultiVariable:
def __str__(self): def __str__(self):
if self.name == None: if self.name == None:
return "MultiVariable #"+str(id(self)) return "MultiVariable#"+str(id(self))
else: else:
return "MultiVariable("+self.name+")" return "MultiVariable("+self.name+")"

View File

@ -5,11 +5,31 @@ 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
import geosolver.tolerance import geosolver.tolerance as tolerance
from time import time from time import time
# ---------- 3D problems ----- # ---------- 3D problems -----
def fix_problem_3d():
"""A problem with a fix constraint"""
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.0, 0.0, 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(FixConstraint('v1', vector([0.0,0.0,0.0])))
problem.add_constraint(FixConstraint('v2', vector([10.0,0.0,0.0])))
problem.add_constraint(FixConstraint('v3', vector([5.0,5.0,0.0])))
return problem
def double_banana_problem(): def double_banana_problem():
"""The double tetrahedron problem""" """The double tetrahedron problem"""
problem = GeometricProblem(dimension=3) problem = GeometricProblem(dimension=3)
@ -381,6 +401,8 @@ def twoscisors():
problem.add_constraint(DistanceConstraint('D', 'C', 6.0)) problem.add_constraint(DistanceConstraint('D', 'C', 6.0))
return problem return problem
# ------ 2D tests ------- # ------ 2D tests -------
def test_fix(n): def test_fix(n):
@ -742,15 +764,20 @@ def stats_parametric():
t = t2-t1 t = t2-t1
print size,"\t",i,"\t",t,"\t",result print size,"\t",i,"\t",t,"\t",result
def runstats():
stats_solving()
stats_incremental()
stats_parametric_incremental()
stats_parametric()
#if __name__ == "__main__": test(double_banana_plus_one_problem()) def runtests():
#if __name__ == "__main__": test(double_banana_problem()) #test(double_banana_plus_one_problem())
#if __name__ == "__main__": test(double_tetrahedron_problem()) #test(double_banana_problem())
#if __name__ == "__main__": test(ada_tetrahedron_problem()) #test(double_tetrahedron_problem())
#if __name__ == "__main__": test(random_triangular_problem_3D(10,10.0,0.0,0.5)) #test(ada_tetrahedron_problem())
if __name__ == "__main__": test(random_distance_problem_3D(15,1.0,0.0)) #test(random_triangular_problem_3D(10,10.0,0.0,0.5))
#if __name__ == "__main__": stats_solving() #test(random_distance_problem_3D(10,1.0,0.0))
#if __name__ == "__main__": stats_incremental() #diag_select("clsolver3D")
#if __name__ == "__main__": stats_parametric_incremental() test(fix_problem_3d())
#if __name__ == "__main__": stats_parametric()
#if __name__ == "__main__": test(ada_tetrahedron_problem()) if __name__ == "__main__": runtests()