def test_quadratic_program_element_when_loaded_from_source(self): """Test QuadraticProgramElement when QuadraticProgram is loaded from an external source""" with self.subTest("from_ising"): q_p = QuadraticProgram() q_p.from_ising(PauliSumOp.from_list([("IZ", 1), ("ZZ", 2)])) self.assertEqual(id(q_p.objective.quadratic_program), id(q_p)) for elem in q_p.variables: self.assertEqual(id(elem.quadratic_program), id(q_p)) for elem in q_p.linear_constraints: self.assertEqual(id(elem.quadratic_program), id(q_p)) for elem in q_p.quadratic_constraints: self.assertEqual(id(elem.quadratic_program), id(q_p)) try: lp_file = self.get_resource_path("test_quadratic_program.lp", "problems/resources") q_p = QuadraticProgram() q_p.read_from_lp_file(lp_file) self.assertEqual(id(q_p.objective.quadratic_program), id(q_p)) for elem in q_p.variables: self.assertEqual(id(elem.quadratic_program), id(q_p)) for elem in q_p.linear_constraints: self.assertEqual(id(elem.quadratic_program), id(q_p)) for elem in q_p.quadratic_constraints: self.assertEqual(id(elem.quadratic_program), id(q_p)) except RuntimeError as ex: self.fail(str(ex))
def setUp(self): """Set up for the tests""" super().setUp() self.total_set = [1, 2, 3, 4, 5] self.list_of_subsets = [[1, 4], [2, 3, 4], [1, 5], [2, 3]] op = QuadraticProgram() for _ in range(4): op.binary_var() self.result = OptimizationResult( x=[0, 1, 1, 0], fval=2, variables=op.variables, status=OptimizationResultStatus.SUCCESS, )
def test_str_repr(self): """Test str and repr""" with self.subTest("5 variables"): n = 5 quadratic_program = QuadraticProgram() quadratic_program.binary_var_list(n) # x0,...,x4 expr = LinearExpression(quadratic_program, [float(e) for e in range(n)]) self.assertEqual(str(expr), "x1 + 2*x2 + 3*x3 + 4*x4") self.assertEqual(repr(expr), "<LinearExpression: x1 + 2*x2 + 3*x3 + 4*x4>") with self.subTest("50 variables"): # pylint: disable=cyclic-import from qiskit_optimization.translators.prettyprint import DEFAULT_TRUNCATE n = 50 quadratic_program = QuadraticProgram() quadratic_program.binary_var_list(n) # x0,...,x49 expr = LinearExpression(quadratic_program, [float(e) for e in range(n)]) expected = " ".join(["x1"] + sorted([f"+ {i}*x{i}" for i in range(2, n)], key=lambda e: e.split(" ")[1])) self.assertEqual(str(expr), expected) self.assertEqual( repr(expr), f"<LinearExpression: {expected[:DEFAULT_TRUNCATE]}...>")
def setUp(self): """Set up for the tests""" super().setUp() self.weights = [16, 9, 23] self.max_weight = 40 self.max_number_of_bins = 2 op = QuadraticProgram() for _ in range(12): op.binary_var() self.result = OptimizationResult( x=[1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1], fval=2.0, variables=op.variables, status=OptimizationResultStatus.SUCCESS, )
def setUp(self): """Set up for the tests""" super().setUp() self.values = [10, 40, 30, 50] self.weights = [5, 4, 6, 3] self.max_weight = 10 op = QuadraticProgram() for _ in range(4): op.binary_var() self.result = OptimizationResult( x=[0, 1, 0, 1], fval=90, variables=op.variables, status=OptimizationResultStatus.SUCCESS, )
def setUp(self) -> None: super().setUp() self.quadratic_program = QuadraticProgram() self.quadratic_program.binary_var_list(3, name="x") self.quadratic_program.linear_constraint({"x0": 1, "x1": -2}, "<=", 1) self.quadratic_program.linear_constraint({"x0": 1, "x1": -2}, "<", 1) self.quadratic_program.linear_constraint({"x0": 1, "x1": -2}, "LE", 1) self.quadratic_program.linear_constraint({"x0": 1, "x1": -2}, "L", 1) self.quadratic_program.linear_constraint({"x0": -1, "x1": 2}, "==", 2) self.quadratic_program.linear_constraint({"x0": -1, "x1": 2}, "=", 2) self.quadratic_program.linear_constraint({"x0": -1, "x1": 2}, "EQ", 2) self.quadratic_program.linear_constraint({"x0": -1, "x1": 2}, "E", 2) self.quadratic_program.linear_constraint({"x1": 2, "x2": -1}, ">=", 3) self.quadratic_program.linear_constraint({"x1": 2, "x2": -1}, ">", 3) self.quadratic_program.linear_constraint({"x1": 2, "x2": -1}, "GE", 3) self.quadratic_program.linear_constraint({"x1": 2, "x2": -1}, "G", 3)
def test_evaluate_gradient(self): """ test evaluate gradient. """ quadratic_program = QuadraticProgram() x = [quadratic_program.continuous_var() for _ in range(5)] coefficients_list = list(range(5)) linear = LinearExpression(quadratic_program, coefficients_list) values_list = list(range(len(x))) values_array = np.array(values_list) values_dict_int = {i: i for i in range(len(x))} values_dict_str = {'x{}'.format(i): i for i in range(len(x))} for values in [values_list, values_array, values_dict_int, values_dict_str]: np.testing.assert_almost_equal(linear.evaluate_gradient(values), coefficients_list)
def test_evaluate(self): """test evaluate.""" quadratic_program = QuadraticProgram() x = [quadratic_program.continuous_var() for _ in range(5)] coefficients_list = list(range(5)) linear = LinearExpression(quadratic_program, coefficients_list) values_list = list(range(len(x))) values_array = np.array(values_list) values_dict_int = {i: i for i in range(len(x))} values_dict_str = {f"x{i}": i for i in range(len(x))} for values in [values_list, values_array, values_dict_int, values_dict_str]: self.assertEqual(linear.evaluate(values), 30)
def test_binary_to_integer(self): """Test binary to integer""" op = QuadraticProgram() for i in range(0, 2): op.binary_var(name="x{}".format(i)) op.integer_var(name="x2", lowerbound=0, upperbound=5) linear = {"x0": 1, "x1": 2, "x2": 1} op.maximize(0, linear, {}) linear = {} for x in op.variables: linear[x.name] = 1 op.linear_constraint(linear, Constraint.Sense.EQ, 6, "x0x1x2") conv = IntegerToBinary() _ = conv.convert(op) new_x = conv.interpret([0, 1, 1, 1, 1]) np.testing.assert_array_almost_equal(new_x, [0, 1, 5])
def test_inequality_mode_auto(self): """ Test auto mode of InequalityToEqualityConverter() """ op = QuadraticProgram() for i in range(3): op.binary_var(name='x{}'.format(i)) # Linear constraints linear_constraint = {'x0': 1, 'x1': 1} op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') linear_constraint = {'x1': 1, 'x2': -1} op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, 'x1x2') linear_constraint = {'x0': 1.1, 'x2': 2.2} op.linear_constraint(linear_constraint, Constraint.Sense.GE, 3.3, 'x0x2') conv = InequalityToEquality(mode='auto') op2 = conv.convert(op) lst = [op2.variables[3].vartype, op2.variables[4].vartype] self.assertListEqual(lst, [Variable.Type.INTEGER, Variable.Type.CONTINUOUS])
def test_inequality_mode_continuous(self): """Test continuous mode of InequalityToEqualityConverter()""" op = QuadraticProgram() for i in range(3): op.binary_var(name="x{}".format(i)) # Linear constraints linear_constraint = {"x0": 1, "x1": 1} op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") linear_constraint = {"x1": 1, "x2": -1} op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, "x1x2") linear_constraint = {"x0": 1, "x2": 3} op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, "x0x2") conv = InequalityToEquality(mode="continuous") op2 = conv.convert(op) lst = [op2.variables[3].vartype, op2.variables[4].vartype] self.assertListEqual( lst, [Variable.Type.CONTINUOUS, Variable.Type.CONTINUOUS])
def test_inequality_mode_integer(self): """Test integer mode of InequalityToEqualityConverter()""" op = QuadraticProgram() for i in range(3): op.binary_var(name=f"x{i}") # Linear constraints linear_constraint = {"x0": 1, "x1": 1} op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") linear_constraint = {"x1": 1, "x2": -1} op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, "x1x2") linear_constraint = {"x0": 1, "x2": 3} op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, "x0x2") conv = InequalityToEquality(mode="integer") op2 = conv.convert(op) lst = [op2.variables[3].vartype, op2.variables[4].vartype] self.assertListEqual(lst, [Variable.Type.INTEGER, Variable.Type.INTEGER])
def test_linear_inequality_to_penalty3(self): """Test special constraint to penalty x1+x2+x3+... >= n-1 -> P(x1*x2+x1*x3+...)""" op = QuadraticProgram() lip = LinearInequalityToPenalty() op.binary_var_list(5) # Linear constraints n = 5 op.linear_constraint([1, 1, 1, 1, 1], Constraint.Sense.GE, n - 1, "") # Test with no max/min with self.subTest("No max/min"): self.assertEqual(op.get_num_linear_constraints(), 1) penalty = 5 lip.penalty = penalty constant = 10 linear = [n - 1, n - 1, n - 1, n - 1, n - 1] quadratic = [ [0, 1, 1, 1, 1], [0, 0, 1, 1, 1], [0, 0, 0, 1, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 0], ] op2 = lip.convert(op) cnst2 = op2.objective.constant / penalty ldct2 = op2.objective.linear.to_array() / penalty * -1 qdct2 = op2.objective.quadratic.to_array() / penalty self.assertEqual(cnst2, constant) self.assertEqual(ldct2.tolist(), linear) self.assertEqual(qdct2.tolist(), quadratic) self.assertEqual(op2.get_num_linear_constraints(), 0)
def test_ising_to_quadraticprogram_quadratic(self): """Test optimization problem to operators with linear=False""" op = QUBIT_OP_MAXIMIZE_SAMPLE offset = OFFSET_MAXIMIZE_SAMPLE quadratic = QuadraticProgram() quadratic.from_ising(op, offset, linear=False) self.assertEqual(quadratic.get_num_vars(), 4) self.assertEqual(quadratic.get_num_linear_constraints(), 0) self.assertEqual(quadratic.get_num_quadratic_constraints(), 0) self.assertEqual(quadratic.objective.sense, quadratic.objective.Sense.MINIMIZE) self.assertAlmostEqual(quadratic.objective.constant, 900000) quadratic_matrix = np.zeros((4, 4)) quadratic_matrix[0, 0] = -500001 quadratic_matrix[0, 1] = 400000 quadratic_matrix[0, 2] = 600000 quadratic_matrix[0, 3] = 800000 quadratic_matrix[1, 1] = -800001 quadratic_matrix[1, 2] = 1200000 quadratic_matrix[1, 3] = 1600000 quadratic_matrix[2, 2] = -900001 quadratic_matrix[2, 3] = 2400000 quadratic_matrix[3, 3] = -800001 np.testing.assert_array_almost_equal( quadratic.objective.quadratic.coefficients.toarray(), quadratic_matrix)
def qaoa(G): n = G.numberOfNodes() G = nw.nxadapter.nk2nx(G) w = nx.adjacency_matrix(G) problem = QuadraticProgram() _ = [problem.binary_var(f"x{i}") for i in range(n)] linear = w.dot(np.ones(n)) quadratic = -w problem.maximize(linear=linear, quadratic=quadratic) c = [1] for _ in range(n - 1): c.append(0) problem.linear_constraint(c, '==', 1) cobyla = COBYLA() backend = BasicAer.get_backend('qasm_simulator') qaoa = QAOA(optimizer=cobyla, reps=3, quantum_instance=backend) algorithm = MinimumEigenOptimizer(qaoa) result = algorithm.solve(problem) L = result.x i = 0 res = {} for x in L: res[i] = x i += 1 return res
def test_auto_penalty(self): """ Test auto penalty function""" op = QuadraticProgram() op.binary_var('x') op.binary_var('y') op.binary_var('z') op.minimize(constant=3, linear={'x': 1}, quadratic={('x', 'y'): 2}) op.linear_constraint(linear={'x': 1, 'y': 1, 'z': 1}, sense='EQ', rhs=2, name='xyz_eq') lineq2penalty = LinearEqualityToPenalty(penalty=1e5) lineq2penalty_auto = LinearEqualityToPenalty() qubo = lineq2penalty.convert(op) qubo_auto = lineq2penalty_auto.convert(op) exact_mes = NumPyMinimumEigensolver() exact = MinimumEigenOptimizer(exact_mes) result = exact.solve(qubo) result_auto = exact.solve(qubo_auto) self.assertEqual(result.fval, result_auto.fval) np.testing.assert_array_almost_equal(result.x, result_auto.x)
def test_get_item(self): """ test get_item. """ quadratic_program = QuadraticProgram() for _ in range(5): quadratic_program.continuous_var() coefficients = [[0 for _ in range(5)] for _ in range(5)] for i, v in enumerate(coefficients): for j, _ in enumerate(v): coefficients[min(i, j)][max(i, j)] += i * j quadratic = QuadraticExpression(quadratic_program, coefficients) for i, j_v in enumerate(coefficients): for j, _ in enumerate(j_v): if i == j: self.assertEqual(quadratic[i, j], coefficients[i][j]) else: self.assertEqual(quadratic[i, j], coefficients[i][j] + coefficients[j][i])
def test_auto_penalty_warning(self): """ Test warnings of auto penalty function""" op = QuadraticProgram() op.binary_var('x') op.binary_var('y') op.binary_var('z') op.minimize(linear={'x': 1, 'y': 2}) op.linear_constraint(linear={'x': 0.5, 'y': 0.5, 'z': 0.5}, sense='EQ', rhs=1, name='xyz') with self.assertLogs('qiskit_optimization', level='WARNING') as log: lineq2penalty = LinearEqualityToPenalty() _ = lineq2penalty.convert(op) warning = ( 'WARNING:qiskit_optimization.converters.linear_equality_to_penalty:' 'Warning: Using 100000.000000 for the penalty coefficient because a float ' 'coefficient exists in constraints. \nThe value could be too small. If so, ' 'set the penalty coefficient manually.' ) self.assertIn(warning, log.output)
def setUp(self): super().setUp() random.seed(123) low = 0 high = 100 pos = {i: (random.randint(low, high), random.randint(low, high)) for i in range(4)} self.graph = nx.random_geometric_graph(4, np.hypot(high-low, high-low)+1, pos=pos) for w, v in self.graph.edges: delta = [self.graph.nodes[w]['pos'][i] - self.graph.nodes[v]['pos'][i] for i in range(2)] self.graph.edges[w, v]['weight'] = np.rint(np.hypot(delta[0], delta[1])) op = QuadraticProgram() for i in range(16): op.binary_var() self.result = OptimizationResult( x=[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], fval=272, variables=op.variables, status=OptimizationResultStatus.SUCCESS)
def test_linear_equality_to_penalty_decode(self): """ Test decode func of LinearEqualityToPenalty""" qprog = QuadraticProgram() qprog.binary_var('x') qprog.binary_var('y') qprog.binary_var('z') qprog.maximize(linear={'x': 3, 'y': 1, 'z': 1}) qprog.linear_constraint(linear={'x': 1, 'y': 1, 'z': 1}, sense='EQ', rhs=2, name='xyz_eq') lineq2penalty = LinearEqualityToPenalty() qubo = lineq2penalty.convert(qprog) exact_mes = NumPyMinimumEigensolver() exact = MinimumEigenOptimizer(exact_mes) result = exact.solve(qubo) new_x = lineq2penalty.interpret(result.x) np.testing.assert_array_almost_equal(new_x, [1, 1, 0]) infeasible_x = lineq2penalty.interpret([1, 1, 1]) np.testing.assert_array_almost_equal(infeasible_x, [1, 1, 1])
def test_evaluate(self): """ test evaluate. """ quadratic_program = QuadraticProgram() x = [quadratic_program.continuous_var() for _ in range(5)] coefficients_list = [[0 for _ in range(5)] for _ in range(5)] for i, v in enumerate(coefficients_list): for j, _ in enumerate(v): coefficients_list[min(i, j)][max(i, j)] += i * j quadratic = QuadraticExpression(quadratic_program, coefficients_list) values_list = list(range(len(x))) values_array = np.array(values_list) values_dict_int = {i: i for i in range(len(x))} values_dict_str = {'x{}'.format(i): i for i in range(len(x))} for values in [values_list, values_array, values_dict_int, values_dict_str]: self.assertEqual(quadratic.evaluate(values), 900)
def test_objective_handling(self): """test objective handling""" q_p = QuadraticProgram() q_p.binary_var("x") q_p.binary_var("y") q_p.binary_var("z") q_p.minimize() obj = q_p.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MINIMIZE) self.assertEqual(obj.constant, 0) self.assertDictEqual(obj.linear.to_dict(), {}) self.assertDictEqual(obj.quadratic.to_dict(), {}) q_p.maximize(1, {"y": 1}, {("z", "x"): 1, ("y", "y"): 1}) obj = q_p.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MAXIMIZE) self.assertEqual(obj.constant, 1) self.assertDictEqual(obj.linear.to_dict(), {1: 1}) self.assertDictEqual(obj.linear.to_dict(use_name=True), {"y": 1}) self.assertListEqual(obj.linear.to_array().tolist(), [0, 1, 0]) self.assertDictEqual(obj.quadratic.to_dict(), {(0, 2): 1, (1, 1): 1}) self.assertDictEqual(obj.quadratic.to_dict(symmetric=True), { (0, 2): 0.5, (2, 0): 0.5, (1, 1): 1 }) self.assertDictEqual(obj.quadratic.to_dict(use_name=True), { ("x", "z"): 1, ("y", "y"): 1 }) self.assertDictEqual( obj.quadratic.to_dict(use_name=True, symmetric=True), { ("x", "z"): 0.5, ("z", "x"): 0.5, ("y", "y"): 1 }, ) self.assertListEqual(obj.quadratic.to_array().tolist(), [[0, 0, 1], [0, 1, 0], [0, 0, 0]]) self.assertListEqual( obj.quadratic.to_array(symmetric=True).tolist(), [[0, 0, 0.5], [0, 1, 0], [0.5, 0, 0]], )
def setUp(self): """Set up for the tests""" super().setUp() self.graph = nx.gnm_random_graph(5, 8, 123) op = QuadraticProgram() for _ in range(5): op.binary_var() self.result = OptimizationResult( x=[1, 0, 1, 1, 1], fval=4, variables=op.variables, status=OptimizationResultStatus.SUCCESS, ) self.result_c3 = OptimizationResult( x=[1, 0, 1, 1, 0], fval=0, variables=op.variables, status=OptimizationResultStatus.SUCCESS, )
def setUp(self): super().setUp() self._num_of_sites = 2 self._seed = 0 self._graph = nx.convert_matrix.from_numpy_matrix( np.array([[0, -1], [-1, 0]])) self._new_disorder_graph = nx.convert_matrix.from_numpy_matrix( np.array([[0, 1], [1, 0]])) op = QuadraticProgram() for _ in range(2): op.binary_var() self._result = OptimizationResult( x=[0, 0], fval=-1 / np.sqrt(2), variables=op.variables, status=OptimizationResultStatus.SUCCESS, )
def symmetrise_qubo(self): new_operator = [] operator, _ = self.qubo.to_ising() for op_1 in operator: coeff, op_1 = op_1.to_pauli_op().coeff, op_1.to_pauli_op().primitive op_1_str = op_1.to_label() Z_counts = op_1_str.count('Z') if Z_counts == 1: op_1_str = "Z" + op_1_str #Add a Z in the last qubit to single Z terms else: op_1_str = "I" + op_1_str #Add an I in the last qubit to ZZ terms (no change in operator) pauli = PauliOp( primitive = Pauli(op_1_str), coeff = coeff ) new_operator.append(pauli) symmetrised_qubo = QuadraticProgram() symmetrised_operator = sum(new_operator) symmetrised_qubo.from_ising(symmetrised_operator, self.offset, linear=True) self.qubo = symmetrised_qubo self.rename_qubo_variables() operator, _ = self.qubo.to_ising() self.operator = operator
def test_evaluate_gradient(self): """ test evaluate gradient. """ quadratic_program = QuadraticProgram() x = [quadratic_program.continuous_var() for _ in range(5)] coefficients_list = [[0 for _ in range(5)] for _ in range(5)] for i, v in enumerate(coefficients_list): for j, _ in enumerate(v): coefficients_list[min(i, j)][max(i, j)] += i * j quadratic = QuadraticExpression(quadratic_program, coefficients_list) values_list = list(range(len(x))) values_array = np.array(values_list) values_dict_int = {i: i for i in range(len(x))} values_dict_str = {'x{}'.format(i): i for i in range(len(x))} grad_values = [0., 60., 120., 180., 240.] for values in [values_list, values_array, values_dict_int, values_dict_str]: np.testing.assert_almost_equal(quadratic.evaluate_gradient(values), grad_values)
def test_continuous_variable_decode(self): """Test decode func of IntegerToBinaryConverter for continuous variables""" mdl = Model("test_continuous_varable_decode") c = mdl.continuous_var(lb=0, ub=10.9, name="c") x = mdl.binary_var(name="x") mdl.maximize(c + x * x) op = QuadraticProgram() op.from_docplex(mdl) converter = IntegerToBinary() op = converter.convert(op) admm_params = ADMMParameters() qubo_optimizer = MinimumEigenOptimizer(NumPyMinimumEigensolver()) continuous_optimizer = CplexOptimizer() solver = ADMMOptimizer( qubo_optimizer=qubo_optimizer, continuous_optimizer=continuous_optimizer, params=admm_params, ) result = solver.solve(op) new_x = converter.interpret(result.x) self.assertEqual(new_x[0], 10.9)
def test_quadratic_program_to_qubo_inequality_to_penalty(self): """Test QuadraticProgramToQubo, passing inequality pattern""" op = QuadraticProgram() conv = QuadraticProgramToQubo() op.binary_var(name="x") op.binary_var(name="y") # Linear constraints linear_constraint = {"x": 1, "y": 1} op.linear_constraint(linear_constraint, Constraint.Sense.GE, 1, "P(1-x-y+xy)") conv.penalty = 1 constant = 1 linear = {"x": -conv.penalty, "y": -conv.penalty} quadratic = {("x", "y"): conv.penalty} op2 = conv.convert(op) cnst = op2.objective.constant ldct = op2.objective.linear.to_dict(use_name=True) qdct = op2.objective.quadratic.to_dict(use_name=True) self.assertEqual(cnst, constant) self.assertEqual(ldct, linear) self.assertEqual(qdct, quadratic) self.assertEqual(op2.get_num_linear_constraints(), 0)
def build_qubo_unconstrained_from_edges_dict(G, all_edges_dict, variables): qubo = QuadraticProgram() linear, quadratic = get_linear_quadratic_coeffs(G, all_edges_dict) for var in variables: qubo.binary_var(var) qubo.minimize(linear=linear, quadratic=quadratic) return qubo, linear, quadratic
def test_penalty_recalculation_when_reusing(self): """ Test the penalty retrieval and recalculation of LinearEqualityToPenalty""" op = QuadraticProgram() op.binary_var('x') op.binary_var('y') op.binary_var('z') op.minimize(constant=3, linear={'x': 1}, quadratic={('x', 'y'): 2}) op.linear_constraint(linear={'x': 1, 'y': 1, 'z': 1}, sense='EQ', rhs=2, name='xyz_eq') # First, create a converter with no penalty lineq2penalty = LinearEqualityToPenalty() self.assertIsNone(lineq2penalty.penalty) # Then converter must calculate the penalty for the problem (should be 4.0) lineq2penalty.convert(op) self.assertEqual(4, lineq2penalty.penalty) # Re-use the converter with a newly defined penalty lineq2penalty.penalty = 3 lineq2penalty.convert(op) self.assertEqual(3, lineq2penalty.penalty) # Re-use the converter letting the penalty be calculated again lineq2penalty.penalty = None lineq2penalty.convert(op) self.assertEqual(4, lineq2penalty.penalty)