예제 #1
0
 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
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
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")
예제 #5
0
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)