Source code for test_solver

"""Test functions of solver.py."""

from typing import TYPE_CHECKING, Optional

import numpy as np
import pytest

from cobra.exceptions import OptimizationError
from cobra.util import solver as su


if TYPE_CHECKING:
    from cobra import Model


[docs]stable_optlang = ["glpk", "cplex", "gurobi"]
[docs]optlang_solvers = [f"optlang-{s}" for s in stable_optlang if s in su.solvers]
[docs]def test_solver_list() -> None: """Expect that at least the GLPK solver is found.""" assert len(su.solvers) >= 1 assert "glpk" in su.solvers
[docs]def test_interface_str() -> None: """Test the string representation of solver interfaces.""" assert su.interface_to_str("nonsense") == "nonsense" assert su.interface_to_str("optlang.glpk_interface") == "glpk" assert su.interface_to_str("optlang-cplex") == "cplex"
[docs]def test_solver_name() -> None: """Test that the default LP solver name is GLPK.""" assert su.get_solver_name() == "glpk"
[docs]def test_choose_solver(model: "Model") -> Optional[su.SolverNotFound]: """Test that solver switching is working.""" so = su.choose_solver(model, "glpk") assert su.interface_to_str(so) == "glpk" if any(s in su.solvers for s in su.qp_solvers): qp_choice = su.choose_solver(model, qp=True) assert su.interface_to_str(qp_choice) in su.qp_solvers else: with pytest.raises(su.SolverNotFound): su.choose_solver(model, qp=True)
[docs]def test_linear_reaction_coefficients(model: "Model") -> None: """Test that linear coefficients are identifiable in objective.""" coefficients = su.linear_reaction_coefficients(model) assert coefficients == {model.reactions.Biomass_Ecoli_core: 1}
[docs]def test_fail_non_linear_reaction_coefficients(model: "Model") -> None: """Test failure of non-linear coefficient identification in reaction.""" model.solver = "optlang-glpk" with pytest.raises(ValueError) as error: model.objective = model.problem.Objective( model.reactions.ATPM.flux_expression ** 2 ) coefficients = su.linear_reaction_coefficients(model) assert coefficients == {} assert "GLPK only supports linear objectives." in str(error.value)
[docs]def test_add_remove(model: "Model") -> None: """Test addition and removal of variables and constraints.""" v = model.variables new_var = model.problem.Variable("test_var", lb=-10, ub=-10) new_constraint = model.problem.Constraint( v.PGK - new_var, name="test_constraint", lb=0 ) su.add_cons_vars_to_problem(model, [new_var, new_constraint]) assert "test_var" in model.variables.keys() assert "test_constraint" in model.constraints.keys() su.remove_cons_vars_from_problem(model, [new_var, new_constraint]) assert "test_var" not in model.variables.keys() assert "test_constraint" not in model.constraints.keys()
[docs]def test_add_remove_in_context(model: "Model") -> None: """Test addition and removal of variables and constraints within context.""" v = model.variables new_var = model.problem.Variable("test_var", lb=-10, ub=-10) with model: su.add_cons_vars_to_problem(model, [new_var]) su.remove_cons_vars_from_problem(model, [v.PGM]) assert "test_var" in model.variables.keys() assert "PGM" not in model.variables.keys() assert "test_var" not in model.variables.keys() assert "PGM" in model.variables.keys()
[docs]def test_absolute_expression(model: "Model") -> None: """Test addition of an absolute expression.""" v = model.variables with model: parts = su.add_absolute_expression(model, 2 * v.PGM, name="test", ub=100) assert len(parts) == 3 assert "test" in model.variables.keys() assert "abs_pos_test" in model.constraints.keys() assert "abs_neg_test" in model.constraints.keys() assert "test" not in model.variables.keys() assert "abs_pos_test" not in model.constraints.keys() assert "abs_neg_test" not in model.constraints.keys()
@pytest.mark.parametrize("solver", optlang_solvers)
[docs]def test_fix_objective_as_constraint(solver: str, model: "Model") -> None: """Test fixing present objective as a constraint.""" model.solver = solver with model as m: su.fix_objective_as_constraint(model, 1.0) constraint_name = m.constraints[-1] assert abs(m.constraints[-1].expression - m.objective.expression) < 1e-6 assert constraint_name not in m.constraints su.fix_objective_as_constraint(model) constraint_name = model.constraints[-1] assert abs(model.constraints[-1].expression - model.objective.expression) < 1e-6 assert constraint_name in model.constraints
@pytest.mark.parametrize("solver", optlang_solvers)
[docs]def test_fix_objective_as_constraint_minimize(model: "Model", solver: str) -> None: """Test fixing present objective as a constraint but as a minimization.""" model.solver = solver model.reactions.Biomass_Ecoli_core.bounds = (0.1, 0.1) minimize_glucose = model.problem.Objective( model.reactions.EX_glc__D_e.flux_expression, direction="min" ) su.set_objective(model, minimize_glucose) su.fix_objective_as_constraint(model) fx_name = f"fixed_objective_{model.objective.name}" constr = model.constraints # Ensure that a solution exists on non-GLPK solvers. model.slim_optimize() assert (constr[fx_name].lb, constr[fx_name].ub) == ( None, model.solver.objective.value,
) @pytest.mark.parametrize("solver", optlang_solvers)
[docs]def test_add_lp_feasibility(model: "Model", solver: str) -> None: """Test functionality to ensure LP feasibility.""" model.solver = solver with model: with model: su.add_lp_feasibility(model) assert "s_plus_succoa_c" in model.variables assert np.isclose(model.slim_optimize(), 0.0) model.reactions.EX_glc__D_e.lower_bound = 1 assert np.isnan(model.slim_optimize()) assert "s_plus_succoa_c" not in model.variables su.add_lp_feasibility(model) assert np.isclose(model.slim_optimize(), 1.3872307692307695)
@pytest.mark.parametrize("solver", optlang_solvers)
[docs]def test_add_lexicographic_constraints(model: "Model", solver: str) -> None: """Test addition of lexicographic constraints.""" model.solver = solver rxns = ["Biomass_Ecoli_core", "EX_glc__D_e", "EX_o2_e"] with model: out = su.add_lexicographic_constraints(model, rxns, ["max", "min", "max"]) print(model.reactions.Biomass_Ecoli_core.bounds) assert np.isclose(model.constraints[-3].lb, out[rxns[0]]) assert np.isclose(model.constraints[-2].ub, out[rxns[1]]) assert np.isclose(model.constraints[-1].lb, out[rxns[2]]) with model: su.add_lexicographic_constraints(model, rxns, "max") with model: su.add_lexicographic_constraints(model, rxns)
[docs]def test_time_limit(large_model: "Model") -> None: """Test time limit while optimizing a model.""" if su.interface_to_str(large_model.problem) != "glpk": pytest.skip("requires GLPK") # It is done like this since optlang accepts inputs in seconds # whereas GLPK accepts milliseconds large_model.solver.configuration._smcp.tm_lim = 1 with pytest.warns(UserWarning): sol = large_model.optimize() assert sol.fluxes is not None with pytest.raises(OptimizationError): sol = large_model.optimize(raise_error=True)