def test_inconsistent2(self): solver = SimplexSolver() x = Variable('x') solver.add_constraint(Constraint(x, Constraint.GEQ, 10)) with self.assertRaises(RequiredFailure): solver.add_constraint(Constraint(x, Constraint.LEQ, 5))
def test_variable_equal_constant(self): solver = SimplexSolver() x = Variable('x', 10) eq = Constraint(100, Constraint.EQ, x) solver.add_constraint(eq) self.assertAlmostEqual(x.value, 100)
def test_variable_leq_constant(self): solver = SimplexSolver() x = Variable('x', 100) ieq = Constraint(x, Constraint.LEQ, 10) solver.add_constraint(ieq) self.assertAlmostEqual(x.value, 10)
def test_constant_leq_variable(self): # 100 <= x solver = SimplexSolver() x = Variable('x', 10) ieq = Constraint(100, Constraint.LEQ, x) solver.add_constraint(ieq) self.assertAlmostEqual(x.value, 100)
def __init__(self, game): self.game = game self.solver = SimplexSolver() self.current_adjacent_fields = [] self.newly_opened = [] self.vars = [[ Variable("a[{}][{}]".format(j, i)) for i in range(self.game.board_dim) ] for j in range(self.game.board_dim)]
def test_stay(self): x = Variable('x', 5) y = Variable('y', 10) solver = SimplexSolver() solver.add_stay(x) solver.add_stay(y) self.assertAlmostEqual(x.value, 5) self.assertAlmostEqual(y.value, 10)
def test_simple(self): solver = SimplexSolver() x = Variable('x', 167) y = Variable('y', 2) eq = Constraint(x, Constraint.EQ, y) solver.add_constraint(eq) self.assertAlmostEqual(x.value, y.value) self.assertAlmostEqual(x.value, 0) self.assertAlmostEqual(y.value, 0)
def initialSetup(self, layer): self.solver = SimplexSolver() self.nodehash = {} for p in layer.paths: for n in p.nodes: nid = n.hash() self.nodehash[nid] = { "node": n, "xvar": Variable("x"+str(nid), n.position.x), "yvar": Variable("y"+str(nid), n.position.y), "affected": set() } self.setConstraintsFromHints(layer)
def test_inconsistent3(self): solver = SimplexSolver() w = Variable('w') x = Variable('x') y = Variable('y') z = Variable('z') solver.add_constraint(Constraint(w, Constraint.GEQ, 10)) solver.add_constraint(Constraint(x, Constraint.GEQ, w)) solver.add_constraint(Constraint(y, Constraint.GEQ, x)) solver.add_constraint(Constraint(z, Constraint.GEQ, y)) solver.add_constraint(Constraint(z, Constraint.GEQ, 8)) with self.assertRaises(RequiredFailure): solver.add_constraint(Constraint(z, Constraint.LEQ, 4))
def test_leq_with_stay(self): # stay width # 100 <= right solver = SimplexSolver() x = Variable('x', 10) width = Variable('width', 10) right = x + width ieq = Constraint(100, Constraint.LEQ, right) solver.add_stay(width) solver.add_constraint(ieq) self.assertAlmostEqual(x.value, 90) self.assertAlmostEqual(width.value, 10)
def test_geq_with_stay(self): # stay width # right >= 100 solver = SimplexSolver() # x = 10 x = Variable('x', 10) # width = 10 width = Variable('width', 10) # right = x + width right = x + width # right >= 100 ieq = Constraint(right, Constraint.GEQ, 100) solver.add_stay(width) solver.add_constraint(ieq) self.assertAlmostEqual(x.value, 90) self.assertAlmostEqual(width.value, 10)
def test_constructor(self): "A solver can be constructed" solver = SimplexSolver() self.assertEqual(len(solver.columns), 0) self.assertEqual(len(solver.rows), 1) self.assertEqual(len(solver.infeasible_rows), 0) self.assertEqual(len(solver.external_rows), 0) self.assertEqual(len(solver.external_parametric_vars), 0)
def test_inconsistent4(self): solver = SimplexSolver() x = Variable('x') y = Variable('y') # x = 10 solver.add_constraint(Constraint(x, Constraint.EQ, 10)) # x = y solver.add_constraint(Constraint(x, Constraint.EQ, y)) # y = 5. Should fail. with self.assertRaises(RequiredFailure): solver.add_constraint(Constraint(y, Constraint.EQ, 5))
def test_casso1(self): solver = SimplexSolver() x = Variable('x') y = Variable('y') solver.add_constraint(Constraint(x, Constraint.LEQ, y)) solver.add_constraint(Constraint(y, Constraint.EQ, x + 3)) solver.add_constraint(Constraint(x, Constraint.EQ, 10, WEAK)) solver.add_constraint(Constraint(y, Constraint.EQ, 10, WEAK)) self.assertTrue( (approx_equal(x.value, 10) and approx_equal(y.value, 13)) or (approx_equal(x.value, 7) and approx_equal(y.value, 10)))
def test_error_weights(self): solver = SimplexSolver() x = Variable('x', 100) y = Variable('y', 200) z = Variable('z', 50) self.assertAlmostEqual(x.value, 100) self.assertAlmostEqual(y.value, 200) self.assertAlmostEqual(z.value, 50) solver.add_constraint(Constraint(z, Constraint.EQ, x, WEAK)) solver.add_constraint(Constraint(x, Constraint.EQ, 20, WEAK)) solver.add_constraint(Constraint(y, Constraint.EQ, 200, STRONG)) self.assertAlmostEqual(x.value, 20) self.assertAlmostEqual(y.value, 200) self.assertAlmostEqual(z.value, 20) solver.add_constraint(Constraint(z + 150, Constraint.LEQ, y, MEDIUM)) self.assertAlmostEqual(x.value, 20) self.assertAlmostEqual(y.value, 200) self.assertAlmostEqual(z.value, 20)
def test_casso1(self): solver = SimplexSolver() x = Variable('x') y = Variable('y') solver.add_constraint(Constraint(x, Constraint.LEQ, y)) solver.add_constraint(Constraint(y, Constraint.EQ, x + 3)) solver.add_constraint(Constraint(x, Constraint.EQ, 10, WEAK)) solver.add_constraint(Constraint(y, Constraint.EQ, 10, WEAK)) self.assertTrue( (approx_equal(x.value, 10) and approx_equal(y.value, 13)) or (approx_equal(x.value, 7) and approx_equal(y.value, 10)) )
def test_leq_with_variable(self): # stay width # right >= rightMin solver = SimplexSolver() x = Variable('x', 10) width = Variable('width', 10) rightMin = Variable('rightMin', 100) right = x + width ieq = Constraint(rightMin, Constraint.LEQ, right) solver.add_stay(width) solver.add_stay(rightMin) solver.add_constraint(ieq) self.assertAlmostEqual(x.value, 90) self.assertAlmostEqual(width.value, 10)
def test_equality_with_stay(self): # stay width, rightMin # right >= rightMin solver = SimplexSolver() x = Variable('x', 10) width = Variable('width', 10) rightMin = Variable('rightMin', 100) right = x + width eq = Constraint(right, Constraint.EQ, rightMin) solver.add_stay(width) solver.add_stay(rightMin) solver.add_constraint(eq) self.assertAlmostEqual(x.value, 90) self.assertAlmostEqual(width.value, 10)
def test_leq_with_expression(self): # stay width, rightMin # right >= rightMin solver = SimplexSolver() x1 = Variable('x1', 10) width1 = Variable('width1', 10) right1 = x1 + width1 x2 = Variable('x2', 100) width2 = Variable('width2', 10) right2 = x2 + width2 ieq = Constraint(right2, Constraint.LEQ, right1) solver.add_stay(width1) solver.add_stay(width2) solver.add_stay(x2) solver.add_constraint(ieq) self.assertAlmostEqual(x1.value, 100)
def test_geq_with_variable(self): # stay width, rightMin # right >= rightMin solver = SimplexSolver() x = Variable('x', 10) width = Variable('width', 10) rightMin = Variable('rightMin', 100) right = x + width ieq = Constraint(right, Constraint.GEQ, rightMin) solver.add_stay(width) solver.add_stay(rightMin) solver.add_constraint(ieq) self.assertAlmostEqual(x.value, 90) self.assertAlmostEqual(width.value, 10)
def test_delete1(self): solver = SimplexSolver() x = Variable('x') cbl = Constraint(x, Constraint.EQ, 100, WEAK) solver.add_constraint(cbl) c10 = Constraint(x, Constraint.LEQ, 10) c20 = Constraint(x, Constraint.LEQ, 20) solver.add_constraint(c10) solver.add_constraint(c20) self.assertAlmostEqual(x.value, 10) solver.remove_constraint(c10) self.assertAlmostEqual(x.value, 20) solver.remove_constraint(c20) self.assertAlmostEqual(x.value, 100) c10again = Constraint(x, Constraint.LEQ, 10) solver.add_constraint(c10) solver.add_constraint(c10again) self.assertAlmostEqual(x.value, 10) solver.remove_constraint(c10) self.assertAlmostEqual(x.value, 10) solver.remove_constraint(c10again) self.assertAlmostEqual(x.value, 100)
class CSP(Strategy): def __init__(self, game): self.game = game self.solver = SimplexSolver() self.current_adjacent_fields = [] self.newly_opened = [] self.vars = [[ Variable("a[{}][{}]".format(j, i)) for i in range(self.game.board_dim) ] for j in range(self.game.board_dim)] def get_random_field(self): while True: i = randint(0, self.game.board_dim - 1) j = randint(0, self.game.board_dim - 1) if self.vars[i][j].value == 0 and self.game.board[i][j].covered: break return self.game.board[i][j] def open(self, field): boundary_list = [] for field in self.game.open_field(field): # this case is really interesting, we are opening a field that is safe but solver # thinks that it is mined. We need to edit and enforce as stay constraint if self.vars[field.row][field.column] != 0: self.vars[field.row][field.column].value = 0 self.solver.add_stay(self.vars[field.row][field.column]) # if adjacent_mines is set we are looking at boundary field and we need to add it # to the boundary_list if field.adjacent_mines: boundary_list.append(field) if field in self.current_adjacent_fields: self.current_adjacent_fields.remove(field) return boundary_list def make_constraint(self, row_index, col_index): """ Compute equations for field (row_index, col_index) """ adjacent_fields = self.game.get_adjacent_fields(row_index, col_index) adjacent_mines = self.game.get_adjacent_mines(row_index, col_index) assert adjacent_mines for field in adjacent_fields: if field not in self.current_adjacent_fields and self.game.board[ field.row][field.column].covered: self.current_adjacent_fields.append(field) return adjacent_mines == sum(self.vars[f.row][f.column] for f in adjacent_fields if f.covered) def first_step(self, first_field=None): self.solver.add_constraint(self.game.num_mines == sum( self.vars[i][j] for i in range(0, self.game.board_dim) for j in range(0, self.game.board_dim))) for (row_index, row) in enumerate(self.game.board): for (col_index, var) in enumerate(row): self.solver.add_constraint( self.vars[row_index][col_index] >= 0) self.solver.add_constraint( self.vars[row_index][col_index] <= 1) if first_field: row, column = first_field self.newly_opened = self.open(self.game.board[row][column]) else: self.newly_opened = self.open(self.get_random_field()) def step(self): for new in self.newly_opened: self.solver.add_constraint( self.make_constraint(new.row, new.column)) possible_fields = [] for field in self.current_adjacent_fields: if self.vars[field.row][field.column].value == 1: self.game.mark_field_dangerous(field) else: if field.is_mine: print( "[CST-PSST] Pushing mined field {} in possible fields". format(field)) else: print("[CST]Pushing field {} in possible fields".format( field)) possible_fields.append(field) if field in self.game.marked: self.game.mark_field_safe(field) if possible_fields: print("[CST] Randomly choosing from possible fields: {}".format( possible_fields)) new_field = choice(possible_fields) else: print( "[CST] I have no idea what to choose next, randomly generating..." ) new_field = self.get_random_field() self.newly_opened = self.open(new_field)
def resolve(parent, dimensions, children, constraint_list): """ Resolve a coordinates problem. The coordinates of the children entered in parameter will be computed. :param parent: the main box that contains the children entered in parameter :param dimensions: the dict of children's dimensions :param children: the children to dispose in the parent's box :param constraint_list: the list of constraints :return: the dict that contains the coordinates of the children in parameter """ if parent.orthogonal_state: if parent.axis == 'horizontal': height = max(map(lambda child: dimensions[child][1], parent.children)) for child in parent.children: w, h = dimensions[child] dimensions[child] = (w, height) else: width = max(map(lambda child: dimensions[child][0], parent.children)) for child in parent.children: w, h = dimensions[child] dimensions[child] = (width, h) boxes = list(map(lambda child: BoxWithConstraints(child, dimensions), children)) constraints = list(map(lambda constraint: Constraint(next(filter(lambda x: x.box == constraint.box1, boxes), BoxWithConstraints(constraint.box1, dimensions)), constraint.direction, next(filter(lambda x: x.box == constraint.box2, boxes), BoxWithConstraints(constraint.box2, dimensions))), constraint_list)) def add_constraint(solver, constraint): box1 = constraint.box1 box2 = constraint.box2 x1, y1, x2, y2 = box1.space x3, y3, x4, y4 = box2.space { 'north': lambda: solver.add_constraint(box1.y + box1.height + y2 + space + y3 < box2.y), 'east': lambda: solver.add_constraint(box1.x > box2.x + box2.width + x4 + space + x1), 'south': lambda: solver.add_constraint(box1.y > box2.y + box2.height + y4 + space + y1), 'west': lambda: solver.add_constraint(box1.x + box1.width + x2 + space + x3 < box2.x) }[constraint.direction]() solver = SimplexSolver() # dimension of the frame left_limit = Variable('left', 0) top_limit = Variable('top', parent.header) right_limit = Variable('right', 0) bot_limit = Variable('bottom', 0) # define the base constraints (stay) solver.add_stay(left_limit) solver.add_stay(top_limit) for i in range(len(boxes)): b1 = boxes[i] x1, y1, x2, y2 = b1.space solver.add_constraint(b1.x > left_limit + space + x1) solver.add_constraint(b1.y > top_limit + space + y1) solver.add_constraint(b1.x + b1.width + x2 + space < right_limit) solver.add_constraint(b1.y + b1.height + y2 + space < bot_limit) if parent.axis == 'horizontal': solver.add_constraint(b1.y + y1 - top_limit == bot_limit - b1.y - b1.height - y2, strength=WEAK) else: solver.add_constraint(b1.x + x1 - left_limit == right_limit - b1.x - b1.width - x2, strength=WEAK) for b2 in boxes[i + 1:]: if not (any(filter(lambda constraint: b1 in [constraint.box1, constraint.box2] \ and b2 in [constraint.box1, constraint.box2], constraints))): x3, y3, x4, y4 = b2.space if parent.axis == 'horizontal': solver.add_constraint(b2.x > b1.x + b1.width + space + x2 + x3, strength=WEAK) else: solver.add_constraint(b2.y > b1.y + b1.height + space + y2 + y3, strength=WEAK) for constraint in constraints: add_constraint(solver, constraint) width, height = max(map(lambda box: box.x.value + box.width + box.space[2] + space, boxes)), \ max(map(lambda box: box.y.value + box.height + box.space[3] + space, boxes)) new_coordinates = OrderedDict({parent: (0, 0, width, height)}) for box in boxes: new_coordinates[box.box] = (box.x.value, box.y.value, box.x.value + box.width, box.y.value + box.height) return new_coordinates
def test_delete2(self): solver = SimplexSolver() x = Variable('x') y = Variable('y') solver.add_constraint(Constraint(x, Constraint.EQ, 100, WEAK)) solver.add_constraint(Constraint(y, Constraint.EQ, 120, STRONG)) c10 = Constraint(x, Constraint.LEQ, 10) c20 = Constraint(x, Constraint.LEQ, 20) solver.add_constraint(c10) solver.add_constraint(c20) self.assertAlmostEqual(x.value, 10) self.assertAlmostEqual(y.value, 120) solver.remove_constraint(c10) self.assertAlmostEqual(x.value, 20) self.assertAlmostEqual(y.value, 120) cxy = Constraint(x * 2, Constraint.EQ, y) solver.add_constraint(cxy) self.assertAlmostEqual(x.value, 20) self.assertAlmostEqual(y.value, 40) solver.remove_constraint(c20) self.assertAlmostEqual(x.value, 60) self.assertAlmostEqual(y.value, 120) solver.remove_constraint(cxy) self.assertAlmostEqual(x.value, 100) self.assertAlmostEqual(y.value, 120)
class TTSolver: def initialSetup(self, layer): self.solver = SimplexSolver() self.nodehash = {} for p in layer.paths: for n in p.nodes: nid = n.hash() self.nodehash[nid] = { "node": n, "xvar": Variable("x"+str(nid), n.position.x), "yvar": Variable("y"+str(nid), n.position.y), "affected": set() } self.setConstraintsFromHints(layer) def yvar(self,n): if not n: raise ValueError if not (n.hash() in self.nodehash): raise KeyError(n) return self.nodehash[n.hash()]["yvar"] def xvar(self,n): if not n: raise ValueError if not (n.hash() in self.nodehash): raise KeyError(n) return self.nodehash[n.hash()]["xvar"] def setConstraintsFromHints(self, layer): c = [] for h in layer.hints: if h.type == TAG: try: if h.options() == DIAGONAL or (h.options() < 8 and not h.horizontal): v1 = self.yvar(h.originNode) v2 = self.yvar(h.targetNode) yValue = v1.value - v2.value ystem = v1 - v2 c.append(ystem == yValue) if h.options() == DIAGONAL or (h.options() < 8 and h.horizontal): v1 = self.xvar(h.originNode) v2 = self.xvar(h.targetNode) xValue = v1.value - v2.value xstem = v1 - v2 c.append(xstem == xValue) if h.options() == PROPORTIONAL_TRIPLE: if h.horizontal: v1 = self.xvar(h.originNode) v2 = self.xvar(h.targetNode) v3 = self.xvar(h.otherNode1) proportion = safediv(h.targetNode.position.x - h.originNode.position.x , h.otherNode1.position.x - h.targetNode.position.x) else: v1 = self.yvar(h.originNode) v2 = self.yvar(h.targetNode) v3 = self.yvar(h.otherNode1) proportion = safediv(h.targetNode.position.y - h.originNode.position.y, h.otherNode1.position.y - h.targetNode.position.y) d1 = v2 - v1 d2 = v3 - v2 c.append(d2 * proportion == d1) if h.options() == PROPORTIONAL_QUAD: on2 = h.valueForKey_("otherNode2") # Glyphs bug if h.horizontal: v1 = self.xvar(h.originNode) v2 = self.xvar(h.targetNode) v3 = self.xvar(h.otherNode1) v4 = self.xvar(on2) proportion = safediv(h.targetNode.position.x - h.originNode.position.x, on2.position.x - h.otherNode1.position.x) else: v1 = self.yvar(h.originNode) v2 = self.yvar(h.targetNode) v3 = self.yvar(h.otherNode1) v4 = self.yvar(on2) proportion = safediv(h.targetNode.position.y - h.originNode.position.y, on2.position.y - h.otherNode1.position.y) d1 = v2 - v1 d2 = v4 - v3 # print(d1,d2,proportion) c.append(d2 * proportion == d1) except ValueError: print("I found a busted constraint. It'll get cleaned up soon.") for cs in c: self.solver.add_constraint(cs) def _diagonalConstraints(self, c, n1,n2): v1 = self.xvar(n1) v2 = self.xvar(n2) xValue = v1.value - v2.value xstem = v1 - v2 v1 = self.yvar(n1) v2 = self.yvar(n2) yValue = v1.value - v2.value ystem = v1 - v2 c.append(xstem == xValue) c.append(ystem == yValue) def _makeEditable(self,node): n = self.nodehash[node.hash()] self.solver.add_edit_var(n["xvar"]) self.solver.add_edit_var(n["yvar"]) def _suggest(self,node): n = self.nodehash[node.hash()] # print("Suggesting ",n["node"].position.x,n["node"].position.y) self.solver.suggest_value(n["xvar"], n["node"].position.x) self.solver.suggest_value(n["yvar"], n["node"].position.y) def setStayFromNodes(self, layer): nodes = filter(lambda x:x.hash() in self.nodehash, layer.selection) if len(nodes) < 1: return temporaryConstraints = [] c = [] for p in layer.paths: for n in p.nodes: if n.type != GSOFFCURVE: # Constrain left handle and this node if n.prevNode.type == GSOFFCURVE and not n.prevNode.selected: self._diagonalConstraints(c, n, n.prevNode) if n.nextNode.type == GSOFFCURVE and not n.nextNode.selected: self._diagonalConstraints(c, n, n.nextNode) for cs in c: temporaryConstraints.append(self.solver.add_constraint(cs, strength=WEAK)) # print("Putting stuff into solver") for n in nodes: self._makeEditable(n) if n.type != GSOFFCURVE: if n.nextNode.type == GSOFFCURVE: self._makeEditable(n.nextNode) if n.prevNode.type == GSOFFCURVE: self._makeEditable(n.prevNode) else: if n.nextNode.type == CURVE and n.nextNode.smooth: self._makeEditable(n.nextNode) self._makeEditable(n.nextNode.nextNode) elif n.prevNode.type == CURVE and n.prevNode.smooth: self._makeEditable(n.prevNode) self._makeEditable(n.prevNode.prevNode) with self.solver.edit(): for n in nodes: self._suggest(n) if n.type != GSOFFCURVE: if n.nextNode.type == GSOFFCURVE: self._suggest(n.nextNode) if n.prevNode.type == GSOFFCURVE: self._suggest(n.prevNode) else: if n.nextNode.type == CURVE and n.nextNode.smooth: self._suggest(n.nextNode) self._suggest(n.nextNode.nextNode) elif n.prevNode.type == CURVE and n.prevNode.smooth: self._suggest(n.prevNode) self._suggest(n.prevNode.prevNode) for j in nodes: n = self.nodehash[j.hash()] # print("Got: ",n["xvar"],n["yvar"]) for c in temporaryConstraints: self.solver.remove_constraint(c) def updateGlyphWithSolution(self): for i in self.nodehash: n = self.nodehash[i] # print(n["node"], "->", n["xvar"].value, n["yvar"].value) n["node"].position = (n["xvar"].value, n["yvar"].value) def updateSolverFromGlyph(self): for i in self.nodehash: n = self.nodehash[i] n["xvar"].value = n["node"].position.x n["yvar"].value = n["node"].position.y n["xstay"] = self.solver.add_stay(n["xvar"], strength=WEAK) n["ystay"] = self.solver.add_stay(n["yvar"], strength=WEAK) self.solver.add_edit_var(n["xvar"]) self.solver.add_edit_var(n["yvar"]) if len(self.nodehash) > 0: with self.solver.edit(): for i in self.nodehash: n = self.nodehash[i] self.solver.suggest_value(n["xvar"], n["node"].position.x) self.solver.suggest_value(n["yvar"], n["node"].position.y)
def test_multiedit1(self): # This test stresses the edit session stack. begin_edit() starts a new # "edit variable group" and "end_edit" closes it, leaving only the # previously opened edit variables still active. x = Variable('x') y = Variable('y') w = Variable('w') h = Variable('h') solver = SimplexSolver() # Add some stays solver.add_stay(x) solver.add_stay(y) solver.add_stay(w) solver.add_stay(h) # start an editing session solver.add_edit_var(x) solver.add_edit_var(y) with solver.edit(): solver.suggest_value(x, 10) solver.suggest_value(y, 20) # Force the system to resolve. solver.resolve() self.assertAlmostEqual(x.value, 10) self.assertAlmostEqual(y.value, 20) self.assertAlmostEqual(w.value, 0) self.assertAlmostEqual(h.value, 0) # Open a second set of variables for editing solver.add_edit_var(w) solver.add_edit_var(h) with solver.edit(): solver.suggest_value(w, 30) solver.suggest_value(h, 40) # Close the second set... self.assertAlmostEqual(x.value, 10) self.assertAlmostEqual(y.value, 20) self.assertAlmostEqual(w.value, 30) self.assertAlmostEqual(h.value, 40) # Now make sure the first set can still be edited solver.suggest_value(x, 50) solver.suggest_value(y, 60) self.assertAlmostEqual(x.value, 50) self.assertAlmostEqual(y.value, 60) self.assertAlmostEqual(w.value, 30) self.assertAlmostEqual(h.value, 40)
def test_quadrilateral(self): "A simple version of the quadrilateral test" solver = SimplexSolver() class Point(object): def __init__(self, identifier, x, y): self.x = Variable('x' + identifier, x) self.y = Variable('y' + identifier, y) def __repr__(self): return u'(%s, %s)' % (self.x.value, self.y.value) __hash__ = object.__hash__ def __eq__(self, other): return self.x.value == other[0] and self.y.value == other[1] points = [ Point('0', 10, 10), Point('1', 10, 200), Point('2', 200, 200), Point('3', 200, 10), Point('m0', 0, 0), Point('m1', 0, 0), Point('m2', 0, 0), Point('m3', 0, 0), ] midpoints = points[4:] # Add point stays weight = 1.0 multiplier = 2.0 for point in points[:4]: solver.add_stay(point.x, WEAK, weight) solver.add_stay(point.y, WEAK, weight) weight = weight * multiplier for start, end in [(0, 1), (1, 2), (2, 3), (3, 0)]: cle = (points[start].x + points[end].x) / 2 cleq = midpoints[start].x == cle solver.add_constraint(cleq) cle = (points[start].y + points[end].y) / 2 cleq = midpoints[start].y == cle solver.add_constraint(cleq) cle = points[0].x + 20 solver.add_constraint(cle <= points[2].x) solver.add_constraint(cle <= points[3].x) cle = points[1].x + 20 solver.add_constraint(cle <= points[2].x) solver.add_constraint(cle <= points[3].x) cle = points[0].y + 20 solver.add_constraint(cle <= points[1].y) solver.add_constraint(cle <= points[2].y) cle = points[3].y + 20 solver.add_constraint(cle <= points[1].y) solver.add_constraint(cle <= points[2].y) for point in points: solver.add_constraint(point.x >= 0) solver.add_constraint(point.y >= 0) solver.add_constraint(point.x <= 500) solver.add_constraint(point.y <= 500) # Check the initial answers self.assertEqual(points[0], (10.0, 10.0)) self.assertEqual(points[1], (10.0, 200.0)) self.assertEqual(points[2], (200.0, 200.0)) self.assertEqual(points[3], (200.0, 10.0)) self.assertEqual(points[4], (10.0, 105.0)) self.assertEqual(points[5], (105.0, 200.0)) self.assertEqual(points[6], (200.0, 105.0)) self.assertEqual(points[7], (105.0, 10.0)) # Now move point 2 to a new location solver.add_edit_var(points[2].x) solver.add_edit_var(points[2].y) solver.begin_edit() solver.suggest_value(points[2].x, 300) solver.suggest_value(points[2].y, 400) solver.end_edit() # Check that the other points have been moved. self.assertEqual(points[0], (10.0, 10.0)) self.assertEqual(points[1], (10.0, 200.0)) self.assertEqual(points[2], (300.0, 400.0)) self.assertEqual(points[3], (200.0, 10.0)) self.assertEqual(points[4], (10.0, 105.0)) self.assertEqual(points[5], (155.0, 300.0)) self.assertEqual(points[6], (250.0, 205.0)) self.assertEqual(points[7], (105.0, 10.0))
def test_multiedit3(self): MIN = 100 MAX = 500 width = Variable('width') height = Variable('height') top = Variable('top') bottom = Variable('bottom') left = Variable('left') right = Variable('right') solver = SimplexSolver() iw = Variable('window_innerWidth', random.randrange(MIN, MAX)) ih = Variable('window_innerHeight', random.randrange(MIN, MAX)) solver.add_constraint(Constraint(width, Constraint.EQ, iw, strength=STRONG, weight=0.0)) solver.add_constraint(Constraint(height, Constraint.EQ, ih, strength=STRONG, weight=0.0)) solver.add_constraint(Constraint(top, Constraint.EQ, 0, strength=WEAK, weight=0.0)) solver.add_constraint(Constraint(left, Constraint.EQ, 0, strength=WEAK, weight=0.0)) solver.add_constraint(Constraint(bottom, Constraint.EQ, top + height, strength=MEDIUM, weight=0.0)) # Right is at least left + width solver.add_constraint(Constraint(right, Constraint.EQ, left + width, strength=MEDIUM, weight=0.0)) solver.add_stay(iw) solver.add_stay(ih) # Propegate viewport size changes. for i in range(0, 30): # Measurement should be cheap here. iwv = random.randrange(MIN, MAX) ihv = random.randrange(MIN, MAX) solver.add_edit_var(iw) solver.add_edit_var(ih) with solver.edit(): solver.suggest_value(iw, iwv) solver.suggest_value(ih, ihv) # solver.resolve() self.assertAlmostEqual(top.value, 0) self.assertAlmostEqual(left.value, 0) self.assertLessEqual(bottom.value, MAX) self.assertGreaterEqual(bottom.value, MIN) self.assertLessEqual(right.value, MAX) self.assertGreaterEqual(right.value, MIN)
def test_paper_example(self): solver = SimplexSolver() left = Variable('left') middle = Variable('middle') right = Variable('right') solver.add_constraint(middle == (left + right) / 2) solver.add_constraint(right == left + 10) solver.add_constraint(right <= 100) solver.add_constraint(left >= 0) # Check that all the required constraints are true: self.assertAlmostEqual((left.value + right.value) / 2, middle.value) self.assertAlmostEqual(right.value, left.value + 10) self.assertGreaterEqual(left.value, 0) self.assertLessEqual(right.value, 100) # Set the middle value to a stay middle.value = 45.0 solver.add_stay(middle) # Check that all the required constraints are true: self.assertAlmostEqual((left.value + right.value) / 2, middle.value) self.assertAlmostEqual(right.value, left.value + 10) self.assertGreaterEqual(left.value, 0) self.assertLessEqual(right.value, 100) # But more than that - since we gave a position for middle, we know # where all the points should be. self.assertAlmostEqual(left.value, 40) self.assertAlmostEqual(middle.value, 45) self.assertAlmostEqual(right.value, 50)
def test_buttons(self): "A test of a horizontal layout of two buttons on a screen." class Button(object): def __init__(self, identifier): self.left = Variable('left' + identifier, 0) self.width = Variable('width' + identifier, 0) def __repr__(self): return u'(%s:%s)' % (self.left.value, self.width.value) solver = SimplexSolver() b1 = Button('b1') b2 = Button('b2') left_limit = Variable('left', 0) right_limit = Variable('width', 0) left_limit.value = 0 solver.add_stay(left_limit, REQUIRED) stay = solver.add_stay(right_limit, WEAK) # The two buttons are the same width solver.add_constraint(b1.width == b2.width) # b1 starts 50 from the left margin. solver.add_constraint(b1.left == left_limit + 50) # b2 ends 50 from the right margin solver.add_constraint(left_limit + right_limit == b2.left + b2.width + 50) # b2 starts at least 100 from the end of b1 solver.add_constraint(b2.left >= (b1.left + b1.width + 100)) # b1 has a minimum width of 87 solver.add_constraint(b1.width >= 87) # b1's preferred width is 87 solver.add_constraint(b1.width == 87, STRONG) # b2's minimum width is 113 solver.add_constraint(b2.width >= 113) # b2's preferred width is 113 solver.add_constraint(b2.width == 113, STRONG) # Without imposign a stay on the right, right_limit will be the minimum width for the layout self.assertAlmostEqual(b1.left.value, 50.0) self.assertAlmostEqual(b1.width.value, 113.0) self.assertAlmostEqual(b2.left.value, 263.0) self.assertAlmostEqual(b2.width.value, 113.0) self.assertAlmostEqual(right_limit.value, 426.0) # The window is 500 pixels wide. right_limit.value = 500 stay = solver.add_stay(right_limit, REQUIRED) self.assertAlmostEqual(b1.left.value, 50.0) self.assertAlmostEqual(b1.width.value, 113.0) self.assertAlmostEqual(b2.left.value, 337.0) self.assertAlmostEqual(b2.width.value, 113.0) self.assertAlmostEqual(right_limit.value, 500.0) solver.remove_constraint(stay) # Expand to 700 pixels right_limit.value = 700 stay = solver.add_stay(right_limit, REQUIRED) self.assertAlmostEqual(b1.left.value, 50.0) self.assertAlmostEqual(b1.width.value, 113.0) self.assertAlmostEqual(b2.left.value, 537.0) self.assertAlmostEqual(b2.width.value, 113.0) self.assertAlmostEqual(right_limit.value, 700.0) solver.remove_constraint(stay) # Contract to 600 right_limit.value = 600 stay = solver.add_stay(right_limit, REQUIRED) self.assertAlmostEqual(b1.left.value, 50.0) self.assertAlmostEqual(b1.width.value, 113.0) self.assertAlmostEqual(b2.left.value, 437.0) self.assertAlmostEqual(b2.width.value, 113.0) self.assertAlmostEqual(right_limit.value, 600.0) solver.remove_constraint(stay)
def test_add_edit_var_required_after_suggestions(self): "Solver works with REQUIRED strength after many suggestions" solver = SimplexSolver() a = Variable(name='a') b = Variable(name='b') solver.add_stay(a, STRONG, 0) solver.add_constraint(Constraint(a, Constraint.EQ, b, REQUIRED)) solver.resolve() self.assertEqual(b.value, 0) self.assertEqual(a.value, 0) solver.add_edit_var(a, REQUIRED) solver.begin_edit() solver.suggest_value(a, 2) solver.resolve() self.assertEqual(a.value, 2) self.assertEqual(b.value, 2) solver.suggest_value(a, 10) solver.resolve() self.assertEqual(a.value, 10) self.assertEqual(b.value, 10)
def test_multiedit2(self): x = Variable('x') y = Variable('y') w = Variable('w') h = Variable('h') solver = SimplexSolver() solver.add_stay(x) solver.add_stay(y) solver.add_stay(w) solver.add_stay(h) solver.add_edit_var(x) solver.add_edit_var(y) solver.begin_edit() solver.suggest_value(x, 10) solver.suggest_value(y, 20) solver.resolve() solver.end_edit() self.assertAlmostEqual(x.value, 10) self.assertAlmostEqual(y.value, 20) self.assertAlmostEqual(w.value, 0) self.assertAlmostEqual(h.value, 0) solver.add_edit_var(w) solver.add_edit_var(h) solver.begin_edit() solver.suggest_value(w, 30) solver.suggest_value(h, 40) solver.end_edit() self.assertAlmostEqual(x.value, 10) self.assertAlmostEqual(y.value, 20) self.assertAlmostEqual(w.value, 30) self.assertAlmostEqual(h.value, 40) solver.add_edit_var(x) solver.add_edit_var(y) solver.begin_edit() solver.suggest_value(x, 50) solver.suggest_value(y, 60) solver.end_edit() self.assertAlmostEqual(x.value, 50) self.assertAlmostEqual(y.value, 60) self.assertAlmostEqual(w.value, 30) self.assertAlmostEqual(h.value, 40)
def test_strength(self): "Solvers should handle strengths correctly" solver = SimplexSolver() x = Variable(name='x', value=10) y = Variable(name='y', value=20) z = Variable(name='z', value=1) w = Variable(name='w', value=1) # Default weights. e0 = Constraint(x, Constraint.EQ, y) solver.add_stay(y) solver.add_constraint(e0) self.assertAlmostEqual(x.value, 20.0) self.assertAlmostEqual(y.value, 20.0) # Add a weak constraint. e1 = Constraint(x, Constraint.EQ, z, strength=WEAK) solver.add_stay(x) solver.add_constraint(e1) self.assertAlmostEqual(x.value, 20.0) self.assertAlmostEqual(z.value, 20.0) # Add a strong constraint. e2 = Constraint(z, Constraint.EQ, w, strength=STRONG) solver.add_stay(w) solver.add_constraint(e2) self.assertAlmostEqual(w.value, 1.0) self.assertAlmostEqual(z.value, 1.0)
def test_add_edit_var_required(self): "Solver works with REQUIRED strength" solver = SimplexSolver() a = Variable(name='a') solver.add_stay(a, STRONG, 0) solver.resolve() self.assertEqual(a.value, 0) solver.add_edit_var(a, REQUIRED) solver.begin_edit() solver.suggest_value(a, 2) solver.resolve() self.assertEqual(a.value, 2)
from cassowary import SimplexSolver, Variable import numpy as np solver = SimplexSolver() #create variables with default value x1 = Variable('x1') x2 = Variable('x2') z1 = Variable('z1') z2 = Variable('z2') z = Variable('z') #known variables num = 2 c = np.array([9, 1]) m = np.array([1, 2]) ub = np.array([10, 3]) max_profit = sum(ub - c) max_total_residues = max_profit + sum(c - m) profit = 0 total_residues = profit + sum(c - m) avg = total_residues*1.0/num solver.add_constraint(z1 <= avg + x1) solver.add_constraint(x1 + z1 >= avg) solver.add_constraint(z2 <= avg + x2) solver.add_constraint(x2 + z2 >= avg) solver.add_constraint(x1 + x2 == total_residues) solver.add_constraint(x1 + m[0] <= ub[0]) solver.add_constraint(x2 + m[1] <= ub[1]) solver.add_constraint(x1 >= 0)
def test_multiedit3(self): MIN = 100 MAX = 500 width = Variable('width') height = Variable('height') top = Variable('top') bottom = Variable('bottom') left = Variable('left') right = Variable('right') solver = SimplexSolver() iw = Variable('window_innerWidth', random.randrange(MIN, MAX)) ih = Variable('window_innerHeight', random.randrange(MIN, MAX)) solver.add_constraint( Constraint(width, Constraint.EQ, iw, strength=STRONG, weight=0.0)) solver.add_constraint( Constraint(height, Constraint.EQ, ih, strength=STRONG, weight=0.0)) solver.add_constraint( Constraint(top, Constraint.EQ, 0, strength=WEAK, weight=0.0)) solver.add_constraint( Constraint(left, Constraint.EQ, 0, strength=WEAK, weight=0.0)) solver.add_constraint( Constraint(bottom, Constraint.EQ, top + height, strength=MEDIUM, weight=0.0)) # Right is at least left + width solver.add_constraint( Constraint(right, Constraint.EQ, left + width, strength=MEDIUM, weight=0.0)) solver.add_stay(iw) solver.add_stay(ih) # Propegate viewport size changes. for i in range(0, 30): # Measurement should be cheap here. iwv = random.randrange(MIN, MAX) ihv = random.randrange(MIN, MAX) solver.add_edit_var(iw) solver.add_edit_var(ih) with solver.edit(): solver.suggest_value(iw, iwv) solver.suggest_value(ih, ihv) # solver.resolve() self.assertAlmostEqual(top.value, 0) self.assertAlmostEqual(left.value, 0) self.assertLessEqual(bottom.value, MAX) self.assertGreaterEqual(bottom.value, MIN) self.assertLessEqual(right.value, MAX) self.assertGreaterEqual(right.value, MIN)