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:
parent
9fd7fdb797
commit
4fe301a5e6
|
@ -44,6 +44,8 @@ class ClusterMethod(MultiMethod):
|
|||
s += "wellconstrained"
|
||||
return s
|
||||
|
||||
def input_clusters(self):
|
||||
return filter(lambda var: isinstance(var, Cluster), self.inputs())
|
||||
|
||||
class PrototypeMethod(MultiMethod):
|
||||
"""A PrototypeMethod selects those solutions of a cluster for which
|
||||
|
@ -91,9 +93,9 @@ def is_information_increasing(method):
|
|||
infinc = True
|
||||
connected = Set()
|
||||
output = method.outputs()[0]
|
||||
for cluster in method.inputs():
|
||||
for cluster in method.input_clusters():
|
||||
if num_constraints(cluster.intersection(output)) >= num_constraints(output):
|
||||
infinc = False
|
||||
infinc = False
|
||||
break
|
||||
return infinc
|
||||
|
||||
|
@ -428,7 +430,7 @@ class ClusterSolver(Notifier):
|
|||
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._add_cluster(outcluster)
|
||||
self._add_method(selector)
|
||||
self._rem_top_level(incluster)
|
||||
|
|
|
@ -9,6 +9,7 @@ from configuration import Configuration
|
|||
from cluster import *
|
||||
from map import Map
|
||||
from gmatch import gmatch
|
||||
from method import OrMethod
|
||||
|
||||
def pattern2graph(pattern):
|
||||
"""convert pattern to pattern graph"""
|
||||
|
@ -55,6 +56,10 @@ def reference2graph(nlet):
|
|||
#diag_print("reference graph:"+str(rgraph),"match");
|
||||
return rgraph
|
||||
|
||||
def rootname(cluster):
|
||||
return "root#"+str(id(cluster))
|
||||
|
||||
|
||||
class ClusterSolver3D(ClusterSolver):
|
||||
"""A generic 3D geometric constraint solver.
|
||||
|
||||
|
@ -79,7 +84,19 @@ class ClusterSolver3D(ClusterSolver):
|
|||
def __init__(self):
|
||||
"""Instantiate a ClusterSolver3D"""
|
||||
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 --------
|
||||
|
||||
def _all_sources_constraint_in_cluster(self, constraint, cluster):
|
||||
|
@ -90,7 +107,7 @@ class ClusterSolver3D(ClusterSolver):
|
|||
else:
|
||||
method = self._determining_method(cluster)
|
||||
sources = Set()
|
||||
for inp in method.inputs():
|
||||
for inp in method.input_clusters():
|
||||
sources.union_update(self._all_sources_constraint_in_cluster(constraint, inp))
|
||||
return sources
|
||||
|
||||
|
@ -99,7 +116,7 @@ class ClusterSolver3D(ClusterSolver):
|
|||
# --------------
|
||||
|
||||
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
|
||||
connected = Set()
|
||||
for var in newcluster.vars:
|
||||
|
@ -112,80 +129,12 @@ class ClusterSolver3D(ClusterSolver):
|
|||
return True
|
||||
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):
|
||||
"""finds a possible rewrite rule applications on given set of clusters, applies it
|
||||
and returns True iff successfull
|
||||
"""
|
||||
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)
|
||||
if len(matches) > 0:
|
||||
diag_print("number of matches = "+str(len(matches)), "clsolver3D")
|
||||
|
@ -215,7 +164,7 @@ class ClusterSolver3D(ClusterSolver):
|
|||
dependend = self.find_dependend(var)
|
||||
dependend = filter(lambda x: self.is_top_level(x), dependend)
|
||||
connected.union_update(dependend)
|
||||
#for cluster in merge.inputs():
|
||||
#for cluster in merge.input_clusters():
|
||||
# if cluster in connected:
|
||||
# connected.remove(cluster)
|
||||
|
||||
|
@ -223,15 +172,13 @@ class ClusterSolver3D(ClusterSolver):
|
|||
|
||||
for cluster in connected:
|
||||
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
|
||||
break
|
||||
diag_print("information increasing:"+str(infinc),"clsolver3D")
|
||||
|
||||
# check if method reduces number of clusters (reduc)
|
||||
nremove = 0
|
||||
for cluster in merge.inputs():
|
||||
for cluster in merge.input_clusters():
|
||||
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
|
||||
# will be removed from toplevel
|
||||
nremove += 1
|
||||
|
@ -246,10 +193,10 @@ class ClusterSolver3D(ClusterSolver):
|
|||
# check consistency and local/global overconstrained
|
||||
consistent = True
|
||||
local_oc = False
|
||||
for i1 in range(0, len(merge.inputs())):
|
||||
for i2 in range(i1+1, len(merge.inputs())):
|
||||
c1 = merge.inputs()[i1]
|
||||
c2 = merge.inputs()[i2]
|
||||
for i1 in range(0, len(merge.input_clusters())):
|
||||
for i2 in range(i1+1, len(merge.input_clusters())):
|
||||
c1 = merge.input_clusters()[i1]
|
||||
c2 = merge.input_clusters()[i2]
|
||||
if num_constraints(c1.intersection(c2)) != 0:
|
||||
local_oc = True
|
||||
consistent = consistent and self._is_consistent_pair(c1, c2)
|
||||
|
@ -257,38 +204,66 @@ class ClusterSolver3D(ClusterSolver):
|
|||
merge.overconstrained = local_oc
|
||||
# global overconstrained? (store in output cluster)
|
||||
overconstrained = not consistent
|
||||
for cluster in merge.inputs():
|
||||
for cluster in merge.input_clusters():
|
||||
overconstrained = overconstrained or cluster.overconstrained
|
||||
output.overconstrained = overconstrained
|
||||
# add to graph
|
||||
self._add_cluster(output)
|
||||
self._add_method(merge)
|
||||
# remove inputs from top_level
|
||||
merge.restore_toplevel = [] # make restore list in method
|
||||
for cluster in merge.inputs():
|
||||
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
|
||||
diag_print("remove from top-level: "+str(cluster),"clsolver3D")
|
||||
self._rem_top_level(cluster)
|
||||
merge.restore_toplevel.append(cluster)
|
||||
else:
|
||||
diag_print("keep top-level: "+str(cluster),"clsolver3D")
|
||||
# remove input clusters from top_level
|
||||
if not (hasattr(merge,"noremove") and merge.noremove == True):
|
||||
merge.restore_toplevel = [] # make restore list in method
|
||||
for cluster in merge.input_clusters():
|
||||
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
|
||||
diag_print("remove from top-level: "+str(cluster),"clsolver3D")
|
||||
self._rem_top_level(cluster)
|
||||
merge.restore_toplevel.append(cluster)
|
||||
else:
|
||||
diag_print("keep top-level: "+str(cluster),"clsolver3D")
|
||||
# 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
|
||||
return True
|
||||
|
||||
def _add_root_method(self,inclusters,outcluster):
|
||||
inroots = []
|
||||
for cluster in inclusters:
|
||||
inroots.append(rootname(cluster))
|
||||
outroot = rootname(outcluster)
|
||||
method = OrMethod(inroots, outroot)
|
||||
# add method
|
||||
self._add_method(method)
|
||||
# make sure its deleted when cluster is deleted
|
||||
self._add_dependency(outcluster, method)
|
||||
|
||||
# overriding superclass function
|
||||
def _add_cluster(self, cluster):
|
||||
# call superclass function
|
||||
ClusterSolver._add_cluster(self, cluster)
|
||||
# add root-variable if needed with default value False
|
||||
root = rootname(cluster)
|
||||
if not self._mg.contains(root):
|
||||
self._mg.add_variable(root, False)
|
||||
self._mg.set(root, False)
|
||||
# add root-variable to dependency graph
|
||||
self._add_dependency(cluster, root)
|
||||
|
||||
# class ClusterSolver3D
|
||||
|
||||
# ----------------------------------------------
|
||||
# ---------- Methods for 3D solving -------------
|
||||
# ----------------------------------------------
|
||||
|
||||
# Merge<X> methods take root-cluster in considerations
|
||||
# Derive<X> methods do not take root cluster in consideration
|
||||
|
||||
class MergePR(ClusterMethod):
|
||||
"""Represents a merging of a one-point cluster with any other rigid
|
||||
The first cluster determines the orientation of the resulting cluster
|
||||
"""
|
||||
"""Represents a merging of a one-point cluster with any other rigid."""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
in1 = map["$p"]
|
||||
|
@ -297,7 +272,9 @@ class MergePR(ClusterMethod):
|
|||
outvars = Set(in1.vars).union(in2.vars)
|
||||
out = Rigid(outvars)
|
||||
# set method properties
|
||||
self._inputs = [in1, in2]
|
||||
in1root = rootname(in1)
|
||||
in2root = rootname(in2)
|
||||
self._inputs = [in1, in2, in1root, in2root]
|
||||
self._outputs = [out]
|
||||
ClusterMethod.__init__(self)
|
||||
|
||||
|
@ -314,21 +291,22 @@ class MergePR(ClusterMethod):
|
|||
|
||||
def multi_execute(self, inmap):
|
||||
diag_print("MergePR.multi_execute called","clmethods")
|
||||
c1 = self._inputs[0]
|
||||
c2 = self._inputs[1]
|
||||
conf1 = inmap[c1]
|
||||
conf2 = inmap[c2]
|
||||
#res = conf1.merge2D(conf2)
|
||||
#return [res]
|
||||
if len(c1.vars) == 1:
|
||||
return [conf2.copy()]
|
||||
else:
|
||||
return [conf1.copy()]
|
||||
#c1 = self._inputs[0]
|
||||
#c2 = self._inputs[1]
|
||||
conf1 = inmap[self._inputs[0]]
|
||||
conf2 = inmap[self._inputs[1]]
|
||||
isroot1 = inmap[self._inputs[2]]
|
||||
isroot2 = inmap[self._inputs[3]]
|
||||
if isroot1:
|
||||
res = conf1.merge(conf2)
|
||||
elif isroot2:
|
||||
res = conf2.merge(conf1)
|
||||
else: # cheapest
|
||||
res = conf2.merge(conf1)
|
||||
return [res]
|
||||
|
||||
class MergeDR(ClusterMethod):
|
||||
"""Represents a merging of a distance (two-point cluster) with a rigid
|
||||
The first cluster determines the orientation of the resulting cluster
|
||||
"""
|
||||
"""Represents a merging of a distance (two-point cluster) with a rigid."""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
in1 = map["$d"]
|
||||
|
@ -337,7 +315,9 @@ class MergeDR(ClusterMethod):
|
|||
outvars = Set(in1.vars).union(in2.vars)
|
||||
out = Rigid(outvars)
|
||||
# set method properties
|
||||
self._inputs = [in1, in2]
|
||||
in1root = rootname(in1)
|
||||
in2root = rootname(in2)
|
||||
self._inputs = [in1, in2, in1root, in2root]
|
||||
self._outputs = [out]
|
||||
ClusterMethod.__init__(self)
|
||||
|
||||
|
@ -358,17 +338,18 @@ class MergeDR(ClusterMethod):
|
|||
c2 = self._inputs[1]
|
||||
conf1 = inmap[c1]
|
||||
conf2 = inmap[c2]
|
||||
#res = conf1.merge2D(conf2)
|
||||
#return [res]
|
||||
if len(c1.vars) == 2:
|
||||
return [conf2.copy()]
|
||||
else:
|
||||
return [conf1.copy()]
|
||||
|
||||
isroot1 = inmap[self._inputs[2]]
|
||||
isroot2 = inmap[self._inputs[3]]
|
||||
if isroot1:
|
||||
res = conf1.merge(conf2)
|
||||
elif isroot2:
|
||||
res = conf2.merge(conf1)
|
||||
else: # cheapest
|
||||
res = conf2.merge(conf1)
|
||||
return [res]
|
||||
|
||||
class MergeRR(ClusterMethod):
|
||||
"""Represents a merging of two rigids sharing three points (overconstrained).
|
||||
The first cluster determines the orientation of the resulting cluster
|
||||
"""
|
||||
"""Represents a merging of two rigids sharing three points."""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
in1 = map["$r1"]
|
||||
|
@ -376,7 +357,9 @@ class MergeRR(ClusterMethod):
|
|||
# create output
|
||||
out = Rigid(Set(in1.vars).union(in2.vars))
|
||||
# set method parameters
|
||||
self._inputs = [in1, in2]
|
||||
in1root = rootname(in1)
|
||||
in2root = rootname(in2)
|
||||
self._inputs = [in1, in2, in1root, in2root]
|
||||
self._outputs = [out]
|
||||
ClusterMethod.__init__(self)
|
||||
|
||||
|
@ -397,9 +380,19 @@ class MergeRR(ClusterMethod):
|
|||
c2 = self._inputs[1]
|
||||
conf1 = inmap[c1]
|
||||
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"""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
|
@ -415,6 +408,8 @@ class MergeDDD(ClusterMethod):
|
|||
self._inputs = [self.d_ab, self.d_ac, self.d_bc]
|
||||
self._outputs = [out]
|
||||
ClusterMethod.__init__(self)
|
||||
# do not remove input clusters (because root not considered here)
|
||||
self.noremove = True
|
||||
|
||||
def _pattern():
|
||||
pattern = [["rigid","$d_ab",["$a", "$b"]],
|
||||
|
@ -426,12 +421,12 @@ class MergeDDD(ClusterMethod):
|
|||
|
||||
|
||||
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()+"]"
|
||||
return s
|
||||
|
||||
def multi_execute(self, inmap):
|
||||
diag_print("MergeDDD.multi_execute called","clmethods")
|
||||
diag_print("DeriveDDD.multi_execute called","clmethods")
|
||||
c12 = inmap[self.d_ab]
|
||||
c13 = inmap[self.d_ac]
|
||||
c23 = inmap[self.d_bc]
|
||||
|
@ -444,7 +439,7 @@ class MergeDDD(ClusterMethod):
|
|||
solutions = solve_ddd_3D(v1,v2,v3,d12,d23,d31)
|
||||
return solutions
|
||||
|
||||
class MergeTTD(ClusterMethod):
|
||||
class DeriveTTD(ClusterMethod):
|
||||
"""Represents a derive of a tetra from six distances"""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
|
@ -461,9 +456,11 @@ class MergeTTD(ClusterMethod):
|
|||
self._inputs = [self.t_abc, self.t_abd, self.d_cd]
|
||||
self._outputs = [out]
|
||||
ClusterMethod.__init__(self)
|
||||
# do not remove input clusters (because root not considered here)
|
||||
self.noremove = True
|
||||
|
||||
def __str__(self):
|
||||
s = "MergeTTD("+str(self._inputs[0])+\
|
||||
s = "DeriveTTD("+str(self._inputs[0])+\
|
||||
str(self._inputs[1])+\
|
||||
str(self._inputs[2])+\
|
||||
", ... -> "+\
|
||||
|
@ -480,7 +477,7 @@ class MergeTTD(ClusterMethod):
|
|||
patterngraph = _pattern()
|
||||
|
||||
def multi_execute(self, inmap):
|
||||
diag_print("MergeTTD.multi_execute called","clmethods")
|
||||
diag_print("DeriveTTD.multi_execute called","clmethods")
|
||||
c123 = inmap[self.t_abc]
|
||||
c124 = inmap[self.t_abd]
|
||||
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]))
|
||||
return constraints
|
||||
|
||||
class MergeDAD(ClusterMethod):
|
||||
"""Represents a merging of three distances"""
|
||||
class DeriveDAD(ClusterMethod):
|
||||
"""Represents a merging of two distances and an angle"""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
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._outputs = [out]
|
||||
ClusterMethod.__init__(self)
|
||||
# do not remove input clusters (because root not considered here)
|
||||
self.noremove = True
|
||||
|
||||
def _pattern():
|
||||
pattern = [["rigid","$d_ab",["$a", "$b"]],
|
||||
|
@ -528,12 +527,12 @@ class MergeDAD(ClusterMethod):
|
|||
patterngraph = _pattern()
|
||||
|
||||
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()+"]"
|
||||
return s
|
||||
|
||||
def multi_execute(self, inmap):
|
||||
diag_print("MergeDDD.multi_execute called","clmethods")
|
||||
diag_print("DeriveDAD.multi_execute called","clmethods")
|
||||
c12 = inmap[self.d_ab]
|
||||
c123 = inmap[self.a_abc]
|
||||
c23 = inmap[self.d_bc]
|
||||
|
@ -546,8 +545,8 @@ class MergeDAD(ClusterMethod):
|
|||
solutions = solve_dad_3D(v1,v2,v3,d12,a123,d23)
|
||||
return solutions
|
||||
|
||||
class MergeADD(ClusterMethod):
|
||||
"""Represents a merging of three distances"""
|
||||
class DeriveADD(ClusterMethod):
|
||||
"""Represents a merging of one distance and to distances"""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
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._outputs = [out]
|
||||
ClusterMethod.__init__(self)
|
||||
# do not remove input clusters (because root not considered here)
|
||||
self.noremove = True
|
||||
|
||||
|
||||
def _pattern():
|
||||
pattern = [["hedgehog","$a_cab",["$a", "$c", "$b"]],
|
||||
|
@ -572,12 +574,12 @@ class MergeADD(ClusterMethod):
|
|||
patterngraph = _pattern()
|
||||
|
||||
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()+"]"
|
||||
return s
|
||||
|
||||
def multi_execute(self, inmap):
|
||||
diag_print("MergeADD.multi_execute called","clmethods")
|
||||
diag_print("DeriveADD.multi_execute called","clmethods")
|
||||
c312 = inmap[self.a_cab]
|
||||
c12 = inmap[self.d_ab]
|
||||
c23 = inmap[self.d_bc]
|
||||
|
@ -590,7 +592,7 @@ class MergeADD(ClusterMethod):
|
|||
solutions = solve_add_3D(v1,v2,v3,a312,d12,d23)
|
||||
return solutions
|
||||
|
||||
class MergeAA(ClusterMethod):
|
||||
class DeriveAA(ClusterMethod):
|
||||
"""Derive a scalable from two angles"""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
|
@ -605,6 +607,10 @@ class MergeAA(ClusterMethod):
|
|||
self._inputs = [self.a_cab, self.a_abc]
|
||||
self._outputs = [out]
|
||||
ClusterMethod.__init__(self)
|
||||
# do not remove input clusters (because root not considered here)
|
||||
self.noremove = True
|
||||
|
||||
|
||||
|
||||
def _pattern():
|
||||
pattern = [["hedgehog","$a_cab",["$a", "$c", "$b"]],
|
||||
|
@ -614,12 +620,12 @@ class MergeAA(ClusterMethod):
|
|||
patterngraph = _pattern()
|
||||
|
||||
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()+"]"
|
||||
return s
|
||||
|
||||
def multi_execute(self, inmap):
|
||||
diag_print("MergeAA.multi_execute called","clmethods")
|
||||
diag_print("DeriveAA.multi_execute called","clmethods")
|
||||
c312 = inmap[self.a_cab]
|
||||
c123 = inmap[self.a_abc]
|
||||
v1 = self.a
|
||||
|
@ -631,8 +637,8 @@ class MergeAA(ClusterMethod):
|
|||
solutions = solve_ada_3D(v1,v2,v3,a312,d12,a123)
|
||||
return solutions
|
||||
|
||||
class MergeSD(ClusterMethod):
|
||||
"""Derive a Rigid from a Scalabe and a Rigid sharing two points"""
|
||||
class MergeSR(ClusterMethod):
|
||||
"""Merge a Rigid from a Scalabe and a Rigid sharing two points"""
|
||||
def __init__(self, map):
|
||||
# check inputs
|
||||
in1 = map["$r"]
|
||||
|
@ -651,12 +657,12 @@ class MergeSD(ClusterMethod):
|
|||
patterngraph = _pattern()
|
||||
|
||||
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()+"]"
|
||||
return s
|
||||
|
||||
def multi_execute(self, inmap):
|
||||
diag_print("MergeSD.multi_execute called","clmethods")
|
||||
diag_print("MergeSR.multi_execute called","clmethods")
|
||||
c1 = self._inputs[0]
|
||||
c2 = self._inputs[1]
|
||||
conf1 = inmap[c1]
|
||||
|
|
|
@ -25,10 +25,10 @@ class Distance:
|
|||
return hash(ImmutableSet(self.vars))
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Distance):
|
||||
return ImmutableSet(self.vars) == ImmutableSet(other.vars)
|
||||
else:
|
||||
return False
|
||||
if isinstance(other, Distance):
|
||||
return ImmutableSet(self.vars) == ImmutableSet(other.vars)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class Angle:
|
||||
|
@ -45,10 +45,10 @@ class Angle:
|
|||
self.vars = (a,b,c)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Angle):
|
||||
return self.vars[2] == other.vars[2] and ImmutableSet(self.vars) == ImmutableSet(other.vars)
|
||||
else:
|
||||
return False
|
||||
if isinstance(other, Angle):
|
||||
return self.vars[2] == other.vars[2] and ImmutableSet(self.vars) == ImmutableSet(other.vars)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def __hash__(self):
|
||||
|
|
|
@ -319,18 +319,32 @@ class Configuration:
|
|||
return self.hashvalue
|
||||
|
||||
def __str__(self):
|
||||
return "Configuration("+str(self.map)+")"
|
||||
if self.underconstrained:
|
||||
return "Configuration("+str(self.map)+" underconstrained)"
|
||||
else:
|
||||
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():
|
||||
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})
|
||||
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})
|
||||
print c1 == c2
|
||||
p = Configuration({1:p1,2:p2,3:p3})
|
||||
q1 = vector.vector([0.0,0.0,3.0])
|
||||
q = Configuration({1:q1})
|
||||
print p.merge(q)
|
||||
print q.merge(p)
|
||||
|
||||
|
||||
if __name__ == "__main__": test()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
problems incrementally."""
|
||||
|
||||
import vector
|
||||
from clsolver import PrototypeMethod, is_information_increasing
|
||||
from clsolver import PrototypeMethod
|
||||
from clsolver2D import ClusterSolver2D
|
||||
from clsolver3D import ClusterSolver3D
|
||||
from cluster import Rigid, Hedgehog
|
||||
|
@ -309,7 +309,6 @@ class GeometricSolver (Listener):
|
|||
|
||||
# determine subclusters
|
||||
for method in self.dr.methods():
|
||||
#if is_information_increasing(method):
|
||||
for out in method.outputs():
|
||||
if isinstance(out, Rigid):
|
||||
parent = map[out]
|
||||
|
@ -414,12 +413,13 @@ class GeometricSolver (Listener):
|
|||
elif isinstance(con, FixConstraint):
|
||||
if self.fixcluster != None:
|
||||
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:
|
||||
self.fixcluster = Cluster(self.fixvars)
|
||||
self.fixcluster = Rigid(self.fixvars)
|
||||
self.dr.add(self.fixcluster)
|
||||
self.dr.set_root(fixcluster)
|
||||
self._update_fix()
|
||||
self.dr.set_root(self.fixcluster)
|
||||
self._update_fix()
|
||||
else:
|
||||
## raise StandardError, "unknown constraint type"
|
||||
pass
|
||||
|
@ -429,13 +429,13 @@ class GeometricSolver (Listener):
|
|||
if isinstance(con,FixConstraint):
|
||||
if self.fixcluster != None:
|
||||
self.dr.remove(self.fixcluster)
|
||||
var = self.get(con.variables()[0])
|
||||
var = con.variables()[0]
|
||||
if var in self.fixvars:
|
||||
self.fixvars.remove(var)
|
||||
if len(self.fixvars) < self.problem.dimension:
|
||||
self.fixcluster = None
|
||||
else:
|
||||
self.fixcluster = Cluster(self.fixvars)
|
||||
self.fixcluster = Rigid(self.fixvars)
|
||||
self.dr.add(self.fixcluster)
|
||||
self.dr.set_root(self.fixcluster)
|
||||
elif con in self._map:
|
||||
|
@ -492,14 +492,14 @@ class GeometricSolver (Listener):
|
|||
|
||||
def _update_fix(self):
|
||||
if self.fixcluster:
|
||||
vars = fixcluster.vars
|
||||
vars = self.fixcluster.vars
|
||||
map = {}
|
||||
for var in vars:
|
||||
map[var] = self.problem.get_fix(var).get_parameter()
|
||||
conf = Configuration(map)
|
||||
self.dr.set(fixcluster, [conf])
|
||||
self.dr.set(self.fixcluster, [conf])
|
||||
else:
|
||||
print "warning: no fixcluster to update"
|
||||
diag_print("no fixcluster to update","geometric")
|
||||
pass
|
||||
|
||||
#class GeometricSolver
|
||||
|
@ -611,8 +611,13 @@ class FixConstraint(ParametricConstraint):
|
|||
|
||||
def satisfied(self, mapping):
|
||||
"""return True iff mapping from variable names to points satisfies constraint"""
|
||||
a = mapping[self._variables[0]]
|
||||
result = tol_eq(a[0], self._value[0]) and tol_eq(a[1], self.value[1])
|
||||
point = mapping[self._variables[0]]
|
||||
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
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -132,6 +132,9 @@ class MethodGraph:
|
|||
"""return a list of methods"""
|
||||
return self._methods.keys()
|
||||
|
||||
def contains(self, object):
|
||||
return self._graph.has_vertex(object)
|
||||
|
||||
def add_variable(self, varname, value = None):
|
||||
"""Add a variable, optionally with a value"""
|
||||
if not varname in self._map:
|
||||
|
@ -298,6 +301,30 @@ class MethodGraph:
|
|||
|
||||
# ----------- 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):
|
||||
def __init__(self, a, b, c):
|
||||
"""new method c := a + b"""
|
||||
|
|
|
@ -14,7 +14,7 @@ class MultiVariable:
|
|||
|
||||
def __str__(self):
|
||||
if self.name == None:
|
||||
return "MultiVariable #"+str(id(self))
|
||||
return "MultiVariable#"+str(id(self))
|
||||
else:
|
||||
return "MultiVariable("+self.name+")"
|
||||
|
||||
|
|
|
@ -5,11 +5,31 @@ from geosolver.geometric import *
|
|||
from geosolver.vector import vector
|
||||
from geosolver.randomproblem import *
|
||||
from geosolver.diagnostic import diag_select, diag_print
|
||||
import geosolver.tolerance
|
||||
import geosolver.tolerance as tolerance
|
||||
from time import time
|
||||
|
||||
# ---------- 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():
|
||||
"""The double tetrahedron problem"""
|
||||
problem = GeometricProblem(dimension=3)
|
||||
|
@ -381,6 +401,8 @@ def twoscisors():
|
|||
problem.add_constraint(DistanceConstraint('D', 'C', 6.0))
|
||||
return problem
|
||||
|
||||
|
||||
|
||||
# ------ 2D tests -------
|
||||
|
||||
def test_fix(n):
|
||||
|
@ -742,15 +764,20 @@ def stats_parametric():
|
|||
t = t2-t1
|
||||
print size,"\t",i,"\t",t,"\t",result
|
||||
|
||||
|
||||
#if __name__ == "__main__": test(double_banana_plus_one_problem())
|
||||
#if __name__ == "__main__": test(double_banana_problem())
|
||||
#if __name__ == "__main__": test(double_tetrahedron_problem())
|
||||
#if __name__ == "__main__": test(ada_tetrahedron_problem())
|
||||
#if __name__ == "__main__": test(random_triangular_problem_3D(10,10.0,0.0,0.5))
|
||||
if __name__ == "__main__": test(random_distance_problem_3D(15,1.0,0.0))
|
||||
#if __name__ == "__main__": stats_solving()
|
||||
#if __name__ == "__main__": stats_incremental()
|
||||
#if __name__ == "__main__": stats_parametric_incremental()
|
||||
#if __name__ == "__main__": stats_parametric()
|
||||
#if __name__ == "__main__": test(ada_tetrahedron_problem())
|
||||
def runstats():
|
||||
stats_solving()
|
||||
stats_incremental()
|
||||
stats_parametric_incremental()
|
||||
stats_parametric()
|
||||
|
||||
def runtests():
|
||||
#test(double_banana_plus_one_problem())
|
||||
#test(double_banana_problem())
|
||||
#test(double_tetrahedron_problem())
|
||||
#test(ada_tetrahedron_problem())
|
||||
#test(random_triangular_problem_3D(10,10.0,0.0,0.5))
|
||||
#test(random_distance_problem_3D(10,1.0,0.0))
|
||||
#diag_select("clsolver3D")
|
||||
test(fix_problem_3d())
|
||||
|
||||
if __name__ == "__main__": runtests()
|
||||
|
|
Loading…
Reference in New Issue
Block a user