fixed endless iteration bug due to combination of icremental search, noremove flag and redundant methods
This commit is contained in:
parent
60917c6c1d
commit
e747c5c348
3
TODO.txt
3
TODO.txt
|
@ -35,8 +35,9 @@ BUGS:
|
||||||
- solver sometimes does not terminate - keeps adding merges where the result
|
- solver sometimes does not terminate - keeps adding merges where the result
|
||||||
cluster and one of the original clusters merge again and again. Problem
|
cluster and one of the original clusters merge again and again. Problem
|
||||||
likely that a source cluster should be removed (reducdant) but isn't.
|
likely that a source cluster should be removed (reducdant) but isn't.
|
||||||
|
Often happens with test(triple_double_triangle_problem)
|
||||||
|
|
||||||
- following should be well-constraint, gives underconstrained (need extra rule/pattern)
|
- following should be well-constrained, but gives underconstrained (need extra rule/pattern)
|
||||||
def diamond_3d():
|
def diamond_3d():
|
||||||
"""creates a diamond shape with point 'v1'...'v4' in 3D with one solution"""
|
"""creates a diamond shape with point 'v1'...'v4' in 3D with one solution"""
|
||||||
L=10.0
|
L=10.0
|
||||||
|
|
|
@ -13,7 +13,7 @@ from cluster import *
|
||||||
from configuration import Configuration
|
from configuration import Configuration
|
||||||
from gmatch import gmatch
|
from gmatch import gmatch
|
||||||
from method import OrMethod
|
from method import OrMethod
|
||||||
from incremental import MutableSet,Union
|
from incremental import MutableSet,Union,Filter
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
# ---------- ClusterSolver main class --------------
|
# ---------- ClusterSolver main class --------------
|
||||||
|
@ -53,6 +53,7 @@ class ClusterSolver(Notifier):
|
||||||
# self._graph.add_vertex("_toplevel")
|
# self._graph.add_vertex("_toplevel")
|
||||||
self._graph.add_vertex("_variables")
|
self._graph.add_vertex("_variables")
|
||||||
self._graph.add_vertex("_clusters")
|
self._graph.add_vertex("_clusters")
|
||||||
|
self._graph.add_vertex("_methods")
|
||||||
self._new = []
|
self._new = []
|
||||||
self._mg = MethodGraph()
|
self._mg = MethodGraph()
|
||||||
# add prototype_selection boolean var to method graph
|
# add prototype_selection boolean var to method graph
|
||||||
|
@ -119,7 +120,8 @@ class ClusterSolver(Notifier):
|
||||||
selector.add_constraint(con)
|
selector.add_constraint(con)
|
||||||
self._selection_method[con] = selector
|
self._selection_method[con] = selector
|
||||||
self._mg.execute(selector)
|
self._mg.execute(selector)
|
||||||
self._selection_method[con] = None
|
#self._selection_method[con] = None # this line wrong?
|
||||||
|
self._selection_method[con] = selector # this line better?
|
||||||
|
|
||||||
def rem_selection_constraint(self, con):
|
def rem_selection_constraint(self, con):
|
||||||
"""Remove a SelectionConstraint"""
|
"""Remove a SelectionConstraint"""
|
||||||
|
@ -220,11 +222,6 @@ class ClusterSolver(Notifier):
|
||||||
# -- add object types
|
# -- add object types
|
||||||
|
|
||||||
def _add_variable(self, var):
|
def _add_variable(self, var):
|
||||||
"""Add a variable if not already in system
|
|
||||||
|
|
||||||
arguments:
|
|
||||||
var: any hashable object
|
|
||||||
"""
|
|
||||||
if not self._graph.has_vertex(var):
|
if not self._graph.has_vertex(var):
|
||||||
diag_print("_add_variable "+str(var), "clsolver")
|
diag_print("_add_variable "+str(var), "clsolver")
|
||||||
self._add_to_group("_variables", var)
|
self._add_to_group("_variables", var)
|
||||||
|
@ -354,10 +351,11 @@ class ClusterSolver(Notifier):
|
||||||
|
|
||||||
def _process_new(self):
|
def _process_new(self):
|
||||||
# try incremental matchers and old style matching alternatingly
|
# try incremental matchers and old style matching alternatingly
|
||||||
while len(self._applicable_methods) > 0 or len(self._new) > 0:
|
non_redundant_methods = filter(lambda m: not self._is_redundant_method(m), self._applicable_methods)
|
||||||
|
while len(non_redundant_methods) > 0 or len(self._new) > 0:
|
||||||
# check incremental matches
|
# check incremental matches
|
||||||
if len(self._applicable_methods) > 0:
|
if len(non_redundant_methods) > 0:
|
||||||
method = iter(self._applicable_methods).next()
|
method = iter(non_redundant_methods).next()
|
||||||
#print "applicable methods:", map(str, self._applicable_methods)
|
#print "applicable methods:", map(str, self._applicable_methods)
|
||||||
diag_print("incremental search found:"+str(method),"clsolver._process_new")
|
diag_print("incremental search found:"+str(method),"clsolver._process_new")
|
||||||
self._add_method_complete(method)
|
self._add_method_complete(method)
|
||||||
|
@ -370,6 +368,7 @@ class ClusterSolver(Notifier):
|
||||||
self._new.append(newobject)
|
self._new.append(newobject)
|
||||||
#endif
|
#endif
|
||||||
# endif
|
# endif
|
||||||
|
non_redundant_methods = filter(lambda m: not self._is_redundant_method(m), self._applicable_methods)
|
||||||
# endwhile
|
# endwhile
|
||||||
#end def
|
#end def
|
||||||
|
|
||||||
|
@ -426,46 +425,57 @@ class ClusterSolver(Notifier):
|
||||||
# end for match
|
# end for match
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _add_method_complete(self, merge):
|
def _is_information_increasing(self, merge):
|
||||||
diag_print("add_method_complete "+str(merge), "clsolver")
|
|
||||||
# check that method has one output
|
|
||||||
if len(merge.outputs()) != 1:
|
|
||||||
raise StandardError, "merge number of outputs != 1"
|
|
||||||
output = merge.outputs()[0]
|
|
||||||
|
|
||||||
# check that the method is information increasing (infinc)
|
# check that the method is information increasing (infinc)
|
||||||
|
output = merge.outputs()[0]
|
||||||
infinc = True
|
infinc = True
|
||||||
connected = set()
|
connected = set()
|
||||||
for var in output.vars:
|
for var in output.vars:
|
||||||
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.update(dependend)
|
connected.update(dependend)
|
||||||
#for cluster in merge.input_clusters():
|
|
||||||
# if cluster in connected:
|
|
||||||
# connected.remove(cluster)
|
|
||||||
|
|
||||||
# NOTE 07-11-2007 (while writing the paper): this implementation of information increasing may not be correct. We may need to check that the total sum of the information in the overlapping clusters is equal to the information in the output.
|
# NOTE 07-11-2007 (while writing the paper): this implementation of information increasing may not be correct. We may need to check that the total sum of the information in the overlapping clusters is equal to the information in the output.
|
||||||
|
|
||||||
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):
|
||||||
infinc = False
|
infinc = False
|
||||||
break
|
break
|
||||||
diag_print("information increasing:"+str(infinc),"clsolver")
|
diag_print("information increasing:"+str(infinc),"clsolver")
|
||||||
|
return infinc
|
||||||
|
|
||||||
|
|
||||||
|
def _is_cluster_reducing(self, merge):
|
||||||
# check if method reduces number of clusters (reduc)
|
# check if method reduces number of clusters (reduc)
|
||||||
|
output = merge.outputs()[0]
|
||||||
nremove = 0
|
nremove = 0
|
||||||
for cluster in merge.input_clusters():
|
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
|
||||||
|
# exeption if method sets noremove flag
|
||||||
|
if hasattr(merge,"noremove") and merge.noremove == True:
|
||||||
|
nremove = 0
|
||||||
reduc = (nremove > 1)
|
reduc = (nremove > 1)
|
||||||
diag_print("reduce # clusters:"+str(reduc),"clsolver")
|
diag_print("reduce # clusters:"+str(reduc),"clsolver")
|
||||||
|
return reduc
|
||||||
|
|
||||||
# check if the method is redundant
|
def _is_redundant_method(self, merge):
|
||||||
|
# check if the method is redundant (not information increasing and not reducing number of clusters)
|
||||||
|
infinc = self._is_information_increasing(merge)
|
||||||
|
reduc = self._is_cluster_reducing(merge)
|
||||||
if not infinc and not reduc:
|
if not infinc and not reduc:
|
||||||
diag_print("method is redundant","clsolver")
|
diag_print("method is redundant","clsolver")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
diag_print("method is not redundant","clsolver")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _add_method_complete(self, merge):
|
||||||
|
diag_print("add_method_complete "+str(merge), "clsolver")
|
||||||
|
if self._is_redundant_method(merge):
|
||||||
|
return False
|
||||||
|
|
||||||
|
output = merge.outputs()[0]
|
||||||
|
|
||||||
# check consistency and local/global overconstrained
|
# check consistency and local/global overconstrained
|
||||||
consistent = True
|
consistent = True
|
||||||
local_oc = False
|
local_oc = False
|
||||||
|
@ -478,14 +488,17 @@ class ClusterSolver(Notifier):
|
||||||
consistent = consistent and self._is_consistent_pair(c1, c2)
|
consistent = consistent and self._is_consistent_pair(c1, c2)
|
||||||
merge.consistent = consistent
|
merge.consistent = consistent
|
||||||
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.input_clusters():
|
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 input clusters from top_level
|
# remove input clusters from top_level
|
||||||
merge.restore_toplevel = [] # make restore list in method
|
merge.restore_toplevel = [] # make restore list in method
|
||||||
for cluster in merge.input_clusters():
|
for cluster in merge.input_clusters():
|
||||||
|
@ -501,10 +514,12 @@ class ClusterSolver(Notifier):
|
||||||
merge.restore_toplevel.append(cluster)
|
merge.restore_toplevel.append(cluster)
|
||||||
else:
|
else:
|
||||||
diag_print("keep top-level: "+str(cluster),"clsolver")
|
diag_print("keep top-level: "+str(cluster),"clsolver")
|
||||||
|
|
||||||
# add method to determine root-variable
|
# add method to determine root-variable
|
||||||
self._add_root_method(merge.input_clusters(),merge.outputs()[0])
|
self._add_root_method(merge.input_clusters(),merge.outputs()[0])
|
||||||
|
|
||||||
# add solution selection methods, only if information increasing
|
# add solution selection methods, only if information increasing
|
||||||
if infinc:
|
if self._is_information_increasing(merge):
|
||||||
output2 = self._add_prototype_selector(merge)
|
output2 = self._add_prototype_selector(merge)
|
||||||
output3 = self._add_solution_selector(output2)
|
output3 = self._add_solution_selector(output2)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -363,8 +363,8 @@ class DeriveADD(ClusterMethod):
|
||||||
def _incremental_matcher(solver):
|
def _incremental_matcher(solver):
|
||||||
|
|
||||||
def isadd(triplet):
|
def isadd(triplet):
|
||||||
dad = triplet2add(triplet)
|
add = triplet2add(triplet)
|
||||||
return isinstance(dad, DeriveADD)
|
return isinstance(add, DeriveADD)
|
||||||
|
|
||||||
def triplet2add(triplet):
|
def triplet2add(triplet):
|
||||||
#print "triplet2add: start"
|
#print "triplet2add: start"
|
||||||
|
@ -515,10 +515,10 @@ class CheckAR(ClusterMethod):
|
||||||
self.sharedx = self.hog.xvars.intersection(self.rigid.vars)
|
self.sharedx = self.hog.xvars.intersection(self.rigid.vars)
|
||||||
# create ouptut cluster
|
# create ouptut cluster
|
||||||
outvars = set(self.rigid.vars)
|
outvars = set(self.rigid.vars)
|
||||||
self.out = Rigid(outvars)
|
out = Rigid(outvars)
|
||||||
# set method properties
|
# set method properties
|
||||||
self._inputs = [self.hog, self.rigid]
|
self._inputs = [self.hog, self.rigid]
|
||||||
self._outputs = [self.out]
|
self._outputs = [out]
|
||||||
ClusterMethod.__init__(self)
|
ClusterMethod.__init__(self)
|
||||||
|
|
||||||
def _handcoded_match(problem, newcluster, connected):
|
def _handcoded_match(problem, newcluster, connected):
|
||||||
|
|
|
@ -106,13 +106,9 @@ class IncrementalSet(notify.Notifier, notify.Listener):
|
||||||
# remove object from ref, if given
|
# remove object from ref, if given
|
||||||
self._ref()._remove(object)
|
self._ref()._remove(object)
|
||||||
else:
|
else:
|
||||||
# else add object to self
|
if object in self._objects:
|
||||||
if object not in self._objects:
|
self._objects.remove(object)
|
||||||
self._objects.add(object)
|
self.send_notify(("remove", object))
|
||||||
self.send_notify(("add", object))
|
|
||||||
if object in self._objects:
|
|
||||||
self._objects.remove(object)
|
|
||||||
self.send_notify(("remove", object))
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"""Returns an iterator for the objects contained here.
|
"""Returns an iterator for the objects contained here.
|
||||||
|
@ -217,8 +213,7 @@ class Filter(IncrementalSet):
|
||||||
self._add(object)
|
self._add(object)
|
||||||
|
|
||||||
def _receive_remove(self, source, object):
|
def _receive_remove(self, source, object):
|
||||||
if self._testfunction(object):
|
self._remove(object)
|
||||||
self._remove(object)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, Filter):
|
if isinstance(other, Filter):
|
||||||
|
@ -240,13 +235,18 @@ class Map(IncrementalSet):
|
||||||
def __init__(self, mapfunction, incrset):
|
def __init__(self, mapfunction, incrset):
|
||||||
self._incrset = incrset
|
self._incrset = incrset
|
||||||
self._mapfunction = mapfunction
|
self._mapfunction = mapfunction
|
||||||
|
self._localmap = {} # ensure we don't have to evalute mapfunction on removal
|
||||||
IncrementalSet.__init__(self, [incrset])
|
IncrementalSet.__init__(self, [incrset])
|
||||||
|
|
||||||
def _receive_add(self, source, object):
|
def _receive_add(self, source, object):
|
||||||
self._add(self._mapfunction(object))
|
if object not in self._localmap:
|
||||||
|
mapped = self._mapfunction(object)
|
||||||
|
self._add(mapped)
|
||||||
|
self._localmap[object] = mapped
|
||||||
|
|
||||||
def _receive_remove(self, source, object):
|
def _receive_remove(self, source, object):
|
||||||
self._remove(self._mapfunction(object))
|
if object in self._localmap:
|
||||||
|
self._remove(self._localmap[object])
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, Map):
|
if isinstance(other, Map):
|
||||||
|
|
|
@ -965,9 +965,10 @@ def test2d():
|
||||||
#test(ddd_problem())
|
#test(ddd_problem())
|
||||||
#test(double_triangle())
|
#test(double_triangle())
|
||||||
#test(triple_double_triangle())
|
#test(triple_double_triangle())
|
||||||
#test(dad_problem())
|
diag_select("clsolver")
|
||||||
|
test(dad_problem())
|
||||||
#test(add_problem())
|
#test(add_problem())
|
||||||
test(ada_problem())
|
#test(ada_problem())
|
||||||
#test(aad_problem())
|
#test(aad_problem())
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in New Issue
Block a user