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_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)
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_variable(self): o = Variable('o', 10) a = Expression(o, 20, 2) v = Variable('v', 20) self.assertEqual(len(a.terms), 1) self.assertAlmostEqual(a.terms.get(o), 20.0) # implicit coefficient of 1 a.add_variable(v) self.assertEqual(len(a.terms), 2) self.assertAlmostEqual(a.terms.get(v), 1.0) # add again, with different coefficient a.add_variable(v, 2.0) self.assertEqual(len(a.terms), 2) self.assertAlmostEqual(a.terms.get(v), 3.0) # add again, with resulting 0 coefficient. should remove the term. a.add_variable(v, -3) self.assertEqual(len(a.terms), 1) self.assertIsNone(a.terms.get(v)) # try adding the removed term back, with 0 coefficient a.add_variable(v, 0) self.assertEqual(len(a.terms), 1) self.assertIsNone(a.terms.get(v))
def test_coefficient_for(self): va = Variable('a', 10) vb = Variable('b', 20) a = Expression(va, 20, 2) self.assertEqual(a.coefficient_for(va), 20) self.assertEqual(a.coefficient_for(vb), 0)
def test_mul(self): x = Variable('x', 167) y = Variable('y', 42) # Multiply an expression by a constant self.assertExpressionEqual(Expression(x) * 2, '2.0*x[167.0]') self.assertExpressionEqual(3 * Expression(x), '3.0*x[167.0]') # Can't multiply an expression by a variable unless the expression is a constant with self.assertRaises(TypeError): y * Expression(x) with self.assertRaises(TypeError): Expression(x) * y self.assertExpressionEqual(x * Expression(constant=2), '2.0*x[167.0]') self.assertExpressionEqual(Expression(constant=3) * x, '3.0*x[167.0]') # Can't multiply an expression by an expression unless # one of the expressions is a constant. with self.assertRaises(TypeError): Expression(x) * Expression(y) with self.assertRaises(TypeError): Expression(x, 20, 2) * Expression(y, 10, 5) self.assertExpressionEqual( Expression(x, 20, 2) * Expression(constant=5), '10.0 + 100.0*x[167.0]') self.assertExpressionEqual( Expression(x, 20) * Expression(constant=5), '100.0*x[167.0]') self.assertExpressionEqual( Expression(constant=2) * Expression(y, 10, 5), '10.0 + 20.0*y[42.0]') self.assertExpressionEqual( Expression(constant=2) * Expression(y, 10), '20.0*y[42.0]')
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_operator_arguments_to_inequality(self): v1 = Variable(name='v1', value=10) v2 = Variable(name='v2', value=5) ieq = Constraint(v1, Constraint.GEQ, v2) self.assertExpressionEqual(ieq.expression, v1 - v2) ieq = Constraint(v1, Constraint.LEQ, v2) self.assertExpressionEqual(ieq.expression, v2 - v1)
def test_constructor_with_variable_operator_expression_args2(self): v = Variable(name='v', value=10) e = Expression(Variable(name='x', value=5), 2, 4) ieq = Constraint(e, Constraint.GEQ, v) self.assertExpressionEqual(ieq.expression, e - v) ieq = Constraint(e, Constraint.LEQ, v) self.assertExpressionEqual(ieq.expression, v - e)
def test_add_expression_variable(self): a = Expression(Variable('o', 10), 20, 2) v = Variable('v', 20) # should work just like add_variable a.add_expression(v, 2) self.assertEqual(len(a.terms), 2) self.assertEqual(a.terms.get(v), 2)
def test_change_subject(self): va = Variable('a', 10) vb = Variable('b', 5) e = Expression(va, 2, 5) e.change_subject(vb, va) self.assertEqual(e.constant, -2.5) self.assertIsNone(e.terms.get(va)) self.assertEqual(e.terms.get(vb), 0.5)
def test_is_constant(self): e1 = Expression() e2 = Expression(constant=10) e3 = Expression(Variable('o', 10), 20) e4 = Expression(Variable('o', 10), 20, 2) self.assertTrue(e1.is_constant) self.assertTrue(e2.is_constant) self.assertFalse(e3.is_constant) self.assertFalse(e4.is_constant)
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 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_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 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_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_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_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_set_variable(self): va = Variable('a', 10) vb = Variable('b', 20) a = Expression(va, 20, 2) # set existing variable a.set_variable(va, 2) self.assertEqual(len(a.terms), 1) self.assertEqual(a.coefficient_for(va), 2) # set new variable a.set_variable(vb, 2) self.assertEqual(len(a.terms), 2) self.assertEqual(a.coefficient_for(vb), 2)
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_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_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)
def test_expression_with_variable_and_operators(self): v = Variable(name='v', value=10) ieq = Constraint(v, Constraint.GEQ, 5) self.assertExpressionEqual(ieq.expression, v - 5) ieq = Constraint(v, Constraint.LEQ, 5) self.assertExpressionEqual(ieq.expression, 5 - v)
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_new_subject(self): v = Variable('v', 10) e = Expression(v, 2, 5) self.assertEqual(e.new_subject(v), 0.5) self.assertEqual(e.constant, -2.5) self.assertIsNone(e.terms.get(v)) self.assertTrue(e.is_constant)
def test_full_expression(self): x = Variable('x', 167) expr = Expression(x, 2, 3) self.assertExpressionEqual(expr, '3.0 + 2.0*x[167.0]') self.assertAlmostEqual(expr.constant, 3.0) self.assertEqual(len(expr.terms), 1) self.assertAlmostEqual(expr.terms.get(x), 2.0)
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_clone(self): v = Variable('v', 10) expr = Expression(v, 20, 2) clone = expr.clone() self.assertEqual(clone.constant, expr.constant) self.assertEqual(len(clone.terms), len(expr.terms)) self.assertEqual(clone.terms.get(v), 20)
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)