def __init__(self): self.marker = Marker() self.constraints = [] self.variables = [] self.forced_constraint = None self.check_constraints = False self.unenforced_constraints = set() self.redetermined_variables = set() self.exec_roots = [] self.mark = None self.plan = None self._cycle = None
from unittest import TestCase try: from unittest.mock import MagicMock as Mock except ImportError as e: from mock import Mock from skypyblue.models import * from skypyblue.core import Mvine, Marker, ConstraintSystem marker = Marker() new_mark = marker.new_mark class MVineTests(TestCase): def setUp(self): self.marker = marker self.mvine = Mvine(self.marker) self.constraint_system = ConstraintSystem() def test_build_mvine(self): v1, v2, v3 = self.constraint_system.create_variables(["v1", "v2", "v3"], [3, 4, 5]) method1 = Method( [v1, v2], [v3], lambda v1, v2: (v1 + v2) / 2) method2 = Method( [v3, v2], [v1], lambda v3, v2: 2 * v3 - v2)
class ConstraintSystem: def __init__(self): self.marker = Marker() self.constraints = [] self.variables = [] self.forced_constraint = None self.check_constraints = False self.unenforced_constraints = set() self.redetermined_vars = set() self.exec_roots = [] self.mark = None self._cycle = None def create_variables(self, names, initialValues): assert len(names) == len(initialValues) res = [] for i in range(len(names)): res.append(Variable(names[i], initialValues[i], self)) return res def change_variable_values(self, variables, values): if len(variables) == 1: values = values[0] m = Method([], variables, lambda: values) cn = Constraint( lambda x: True, InternalStrength.FORCED, variables, [m], "set") if self.forced_constraint is not None: # logger.DEBUG("removed %s" %(self.forced_constraint)) self.remove_constraint(self.forced_constraint, skip = True) # logger.DEBUG(", ".join([str(var.determined_by) for var in self.variables])) self.forced_constraint = cn self.add_constraint(cn) def variable_changed(self, var): self.change_variable_values([var], [var.get_value()]) def _check_constraints(self): if self.check_constraints == False: return for constraint in self.constraints: if not constraint.is_enforced(): continue check = constraint.check_function(*[var.get_value() for var in constraint.variables]) if not check: raise Exception("Constraint %s is not satisfied" % constraint) def add_stay_constraint(self, variable, strength): m = Method([], [variable], lambda: variable.get_value()) stay_constraint = Constraint( lambda x: True, strength, [variable], [m], "stay") self.add_constraint(stay_constraint) return stay_constraint @fail_on_cycle def add_constraint(self, constraint): constraint.selected_method = None constraint.mark = None for variable in constraint.variables: variable.add_constraint(constraint) self.unenforced_constraints = set([constraint]) self.exec_roots = [] self.update_method_graph() self.exec_from_roots() self.constraints.append(constraint) self._check_constraints() @fail_on_cycle def remove_constraint(self, constraint, skip = False): for variable in constraint.variables: variable.remove_constraint(constraint) self.constraints.remove(constraint) if constraint.is_enforced(): old_outputs = set(constraint.selected_method.outputs) constraint.selected_method = None self.exec_roots = [] for variable in old_outputs: variable.determined_by = None variable.walk_strength = Strength.WEAKEST self.exec_roots.append(variable) if skip: return self.propagate_walk_strength(old_outputs) self.unenforced_constraints = set() self.collect_unenforced(constraint.strength, True) self.update_method_graph() self.exec_from_roots() self._check_constraints() def update_method_graph(self): while self.unenforced_constraints: constraint = self.remove_strongest_constraint() self.redetermined_vars.clear() if not Mvine(self.marker).build(constraint, self.redetermined_vars): continue self.propagate_walk_strength(self.redetermined_vars.union([constraint])) self.collect_unenforced(constraint.strength, False) self.exec_roots.append(constraint) for var in self.redetermined_vars: if var.determined_by is None: self.exec_roots.append(var) # logger.DEBUG("exec_roots: %s" %self.exec_roots) def remove_strongest_constraint(self): cn = sorted(self.unenforced_constraints, key = lambda cn: cn.strength, reverse = True)[0] self.unenforced_constraints.remove(cn) return cn # not used yet # def weakest_constraint(self, constraints): # return sorted(constraints, key = lambda cn: cn.strength)[0] def propagate_walk_strength(self, roots): self._new_mark() for cn in self.pplan_add(roots): for var in cn.selected_method.inputs: if var.determined_by is not None and var.determined_by.mark == self.mark: var.walk_strength = Strength.WEAKEST self.compute_walkabout_strengths(cn) cn.mark = None def compute_walkabout_strengths(self, cn): outs = cn.selected_method.outputs for out_var in outs: max_strengths = [self.max_out(mt, outs) for mt in cn.methods if out_var not in mt.outputs] max_strengths.append(cn.strength) out_var.walk_strength = min(max_strengths) def max_out(self, mt, current_outputs): return max([var.walk_strength for var in mt.outputs if var not in current_outputs]) def collect_unenforced(self, collection_strength, collect_equal_strength): self._new_mark() for var in self.redetermined_vars: stack = [var] while stack: cur_var = stack.pop() for cn in cur_var.constraints: if cn == cur_var.determined_by or cn.mark == self.mark: continue cn.mark = self.mark if cn.is_enforced(): stack.extend(cn.selected_method.outputs) elif Strength.weaker(cn.strength, collection_strength) or \ (collect_equal_strength and (cn.strength == collection_strength)): self.unenforced_constraints.add(cn) def any_immediate_upstream_marked(self, cn): for var in cn.selected_method.inputs: if var.determined_by is not None and var.determined_by.mark == self.mark: return True return False def immediate_marked_upstream(self, cn): return [var.determined_by for var in cn.selected_method.inputs if var.determined_by is not None and var.determined_by.mark == self.mark] def exec_pplan_create(self): cn_pplan = [] var_pplan = [] for cn_or_var in self.exec_roots: if isinstance(cn_or_var, Constraint): cn = cn_or_var cn.add_to_pplan(cn_pplan, self.mark) elif isinstance(cn_or_var, Variable): var = cn_or_var if var.determined_by is None and not var.valid: var.add_to_pplan(var_pplan, self.mark) var.valid = True pplan = cn_pplan + var_pplan # logger.DEBUG("pplan:\t%s" %", ".join([str(cn) for cn in reversed(pplan)])) # logger.DEBUG("marks:\t%s" %", ".join([str(cn.mark) for cn in reversed(pplan)])) return pplan def exec_from_roots(self): self._new_mark() for cn in reversed(self.exec_pplan_create()): if cn.mark == self.mark: if self.any_immediate_upstream_marked(cn): self.exec_from_cycle(cn) else: cn.mark = None self.execute_propagate_valid(cn) def execute_propagate_valid(self, cn): inputs_valid = not cn.selected_method.has_invalid_vars() if inputs_valid: cn.selected_method.execute() for var in cn.selected_method.outputs: var.valid = inputs_valid def exec_from_cycle(self, cn): if self._cycle is None: self._cycle = set() self._cycle.add(cn) # logger.DEBUG("%s -> %s" %(cn, self.immediate_marked_upstream(cn))) # logger.DEBUG(", ".join([str([var.determined_by, var.determined_by.mark]) for var in self.variables if var.determined_by])) if cn.mark == self.mark: cn.mark = None for var in cn.selected_method.outputs: var.valid = False for consuming_cn in var.constraints: if consuming_cn != cn and consuming_cn.is_enforced: self.exec_from_cycle(consuming_cn) def pplan_add(self, objs): pplan = [] if not isinstance(objs, set): raise Exception("accepting only set of objs! Got %s of type %s" %(objs, type(objs))) for el in objs: el.add_to_pplan(pplan, self.mark) return pplan def _new_mark(self): self.marker.new_mark() self.mark = self.marker.mark
class ConstraintSystem(object): """This class encapsulates a set of constraints. Usage: >>> cs = ConstraintSystem() >>> v1 = Variable("v1", 1, cs) >>> v2 = Variable("v2", 2, cs) >>> m1 = Method([v1], [v2], lambda v1: v1) >>> m2 = Method([v2], [v2], lambda v2: v2) >>> constraint = Constraint( >>> lambda v1, v2: v1 == v2, >>> Strength.STRONG, >>> [v1, v2], [m1, m2]) >>> cs.add_constraint(constraint) >>> v1.get_value() # => 1 >>> v2.get_value() # => 1 """ def __init__(self): self.marker = Marker() self.constraints = [] self.variables = [] self.forced_constraint = None self.check_constraints = False self.unenforced_constraints = set() self.redetermined_variables = set() self.exec_roots = [] self.mark = None self.plan = None self._cycle = None def create_variables(self, names, initialValues): """ Creates several variables at once. >>> v1, v2, v3 = constraint_system.create_variables(["v1", "v2", "v3"], [4, 5, 3]) """ assert len(names) == len(initialValues) variables = [] for i in range(len(names)): variables.append(Variable(names[i], initialValues[i], self)) return variables def change_variable_values(self, variables, values): """Allows to change several variables at once. >>> constraint_system.change_variable_values( >>> [self.v1, self.v2], >>> [10, 20] >>> ) """ assert len(variables) == len(values) if len(variables) == 1: values = values[0] method = Method([], variables, lambda: values) constraint = Constraint( lambda x: True, InternalStrength.FORCED, variables, [method], "set") if self.forced_constraint is not None: self.remove_constraint(self.forced_constraint, skip = True) self.forced_constraint = constraint self.add_constraint(constraint) def _variable_changed(self, variable): self.change_variable_values([variable], [variable.get_value()]) def _check_constraints(self): if self.check_constraints == False: return for constraint in self.constraints: if not constraint.is_enforced(): continue check = constraint.check_function(*[var.get_value() for var in constraint.variables]) if not check: raise Exception("Constraint %s is not satisfied" % constraint) def _add_stay_constraint(self, variable, strength): method = Method([], [variable], lambda: variable.get_value()) stay_constraint = Constraint( lambda x: True, strength, [variable], [method], "stay") self.add_constraint(stay_constraint) return stay_constraint @fail_on_cycle def add_constraint(self, constraint): constraint.selected_method = None constraint.mark = None for variable in constraint.variables: variable.add_constraint(constraint) self.unenforced_constraints = set([constraint]) self.exec_roots = [] self._update_method_graph() self._extract_plan() self._execute_plan() self.constraints.append(constraint) self._check_constraints() @fail_on_cycle def remove_constraint(self, constraint, skip = False): for variable in constraint.variables: variable.remove_constraint(constraint) self.constraints.remove(constraint) if constraint.is_enforced(): old_outputs = set(constraint.selected_method.outputs) constraint.selected_method = None self.exec_roots = [] for variable in old_outputs: variable.determined_by = None variable.walk_strength = Strength.WEAKEST for var_constraint in variable.constraints: if var_constraint.is_enforced(): self.exec_roots.append(var_constraint) if skip: return self._propagate_walk_strength(old_outputs) self.unenforced_constraints = set() self._collect_unenforced(constraint.strength, True) self._update_method_graph() self._extract_plan() self._execute_plan() self._check_constraints() def _update_method_graph(self): while self.unenforced_constraints: constraint = self._remove_strongest_constraint() self.redetermined_variables.clear() if not Mvine(self.marker).build(constraint, self.redetermined_variables): continue self._propagate_walk_strength(self.redetermined_variables.union([constraint])) self._collect_unenforced(constraint.strength, False) self.exec_roots.append(constraint) self.exec_roots.extend([var_constraint \ for var in self.redetermined_variables \ for var_constraint in var.constraints \ if var.determined_by is None and \ var_constraint.is_enforced()]) def _remove_strongest_constraint(self): sorted_constraints = sorted(self.unenforced_constraints, key = lambda constraint: constraint.strength, reverse = True) strongest_constraint = sorted_constraints[0] self.unenforced_constraints.remove(strongest_constraint) return strongest_constraint def _propagate_walk_strength(self, roots): self._new_mark() for constraint in self._pplan_add(roots): for var in constraint.selected_method.inputs: if var.determined_by is not None and var.determined_by.mark == self.mark: var.walk_strength = Strength.WEAKEST self._compute_walkabout_strengths(constraint) constraint.mark = None def _compute_walkabout_strengths(self, constraint): outs = constraint.selected_method.outputs for out_var in outs: max_strengths = [self._max_out(method, outs) for method in constraint.methods if out_var not in method.outputs] max_strengths.append(constraint.strength) out_var.walk_strength = min(max_strengths) def _max_out(self, method, current_outputs): return max([variable.walk_strength for variable in method.outputs if variable not in current_outputs]) def _collect_unenforced(self, collection_strength, collect_equal_strength): self._new_mark() for variable in self.redetermined_variables: stack = [variable] while stack: cur_var = stack.pop() for constraint in cur_var.constraints: if constraint == cur_var.determined_by or constraint.mark == self.mark: continue constraint.mark = self.mark if constraint.is_enforced(): stack.extend(constraint.selected_method.outputs) elif Strength.weaker(constraint.strength, collection_strength) or \ (collect_equal_strength and (constraint.strength == collection_strength)): self.unenforced_constraints.add(constraint) def _any_immediate_upstream_marked(self, constraint): for variable in constraint.selected_method.inputs: if variable.determined_by is not None and variable.determined_by.mark == self.mark: return True return False def _execute_propagate_valid(self, constraint): inputs_valid = not constraint.selected_method.has_invalid_vars() if inputs_valid: constraint.selected_method.execute() for variable in constraint.selected_method.outputs: variable.valid = inputs_valid def _pplan_add(self, objs): pplan = [] if not isinstance(objs, set) and not isinstance(objs, list): raise Exception("accepting only set of objs! Got %s of type %s" %(objs, type(objs))) for el in objs: el.add_to_pplan(pplan, self.mark) return pplan def _new_mark(self): self.marker.new_mark() self.mark = self.marker.mark def _extract_plan(self): good_constraints = [] bad_constraints = [] self._new_mark() for constraint in reversed(self._pplan_add(self.exec_roots)): if constraint.mark != self.mark: continue elif self._any_immediate_upstream_marked(constraint): bad_constraints.append(constraint) else: constraint.mark = None good_constraints.append(constraint) self.plan = Plan(self.exec_roots, good_constraints, bad_constraints, True) def _execute_plan(self): if self.plan.valid: for constraint in self.plan.good_constraints: self._execute_propagate_valid(constraint) else: raise ValueError("trying to execute invalid plan")
from unittest import TestCase try: from unittest.mock import MagicMock as Mock except ImportError as e: from mock import Mock from skypyblue.core import ConstraintSystem, Marker from skypyblue.models import * new_mark = Marker().new_mark class HelperTests(TestCase): def test_max_out(self): cs = ConstraintSystem() v1 = Variable("v1", 13, cs) v2 = Variable("v2", 12, cs) method = Method(v1, v2, lambda v1: v1 - 1) self.assertEqual(Strength.WEAKEST, cs._max_out(method, [v1])) def test_new_mark_are_numbers(self): used_marks = set() for i in range(100): mark = new_mark() self.assertTrue(mark not in used_marks) used_marks.add(mark)