Skip to content

Commit

Permalink
1.1.3
Browse files Browse the repository at this point in the history
fix: make sure that sloppy arg is being passed to super (#83)
Netlib tests test also cloned models (#79)
Add a Gitter chat badge to README.rst (#86) (#88)
  • Loading branch information
KristianJensen authored Mar 30, 2017
1 parent 33d846b commit 1948683
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 38 deletions.
5 changes: 4 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ optlang

*Sympy based mathematical programming language*

|PyPI| |License| |Travis| |Appveyor| |Coverage Status| |Documentation Status| |JOSS| |DOI|
|PyPI| |License| |Travis| |Appveyor| |Coverage Status| |Documentation Status| |Gitter| |JOSS| |DOI|

Optlang is a Python package for solving mathematical optimization
problems, i.e. maximizing or minimizing an objective function over a set
Expand Down Expand Up @@ -153,4 +153,7 @@ also provides a good overview of the project's roadmap.
:target: https://zenodo.org/badge/latestdoi/5031/biosustain/optlang
.. |AppVeyor| image:: https://ci.appveyor.com/api/projects/status/443yp8hf25c6748h/branch/master?svg=true
:target: https://ci.appveyor.com/project/phantomas1234/optlang/branch/master
.. |Gitter| image:: https://badges.gitter.im/biosustain/optlang.svg
:alt: Join the chat at https://gitter.im/biosustain/optlang
:target: https://gitter.im/biosustain/optlang?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge

6 changes: 3 additions & 3 deletions optlang/cplex_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ class Constraint(interface.Constraint):
_INDICATOR_CONSTRAINT_SUPPORT = True

def __init__(self, expression, sloppy=False, *args, **kwargs):
super(Constraint, self).__init__(expression, *args, **kwargs)
super(Constraint, self).__init__(expression, *args, sloppy=sloppy, **kwargs)

def set_linear_coefficients(self, coefficients):
if self.problem is not None:
Expand Down Expand Up @@ -314,7 +314,7 @@ def __iadd__(self, other):
@six.add_metaclass(inheritdocstring)
class Objective(interface.Objective):
def __init__(self, expression, sloppy=False, **kwargs):
super(Objective, self).__init__(expression, **kwargs)
super(Objective, self).__init__(expression, sloppy=sloppy, **kwargs)
self._expression_expired = False
if not (sloppy or self.is_Linear or self.is_Quadratic):
raise ValueError("Cplex only supports linear and quadratic objectives.")
Expand Down Expand Up @@ -577,7 +577,7 @@ def __init__(self, problem=None, *args, **kwargs):
elif sense == 'L':
constr = Constraint(lhs, ub=rhs, name=name, problem=self)
elif sense == 'R':
range_val = self.problem.linear_constraints.get_rhs(name)
range_val = self.problem.linear_constraints.get_range_values(name)
if range_val > 0:
constr = Constraint(lhs, lb=rhs, ub=rhs + range_val, name=name, problem=self)
else:
Expand Down
2 changes: 1 addition & 1 deletion optlang/glpk_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ def get_linear_coefficients(self, variables):
@six.add_metaclass(inheritdocstring)
class Objective(interface.Objective):
def __init__(self, expression, sloppy=False, **kwargs):
super(Objective, self).__init__(expression, **kwargs)
super(Objective, self).__init__(expression, sloppy=sloppy, **kwargs)
self._expression_expired = False
if not (sloppy or self.is_Linear):
raise ValueError(
Expand Down
2 changes: 1 addition & 1 deletion optlang/gurobi_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ def __iadd__(self, other):
@six.add_metaclass(inheritdocstring)
class Objective(interface.Objective):
def __init__(self, expression, sloppy=False, *args, **kwargs):
super(Objective, self).__init__(expression, *args, **kwargs)
super(Objective, self).__init__(expression, *args, sloppy=sloppy, **kwargs)
self._expression_expired = False
if not (sloppy or self.is_Linear): # or self.is_Quadratic: # QP is not yet supported
raise ValueError("The given objective is invalid. Must be linear or quadratic (not yet supported")
Expand Down
18 changes: 11 additions & 7 deletions optlang/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -738,17 +738,21 @@ def _canonicalize(self, expression):
return expression
assert len(lonely_coeffs) == 1
coeff = lonely_coeffs[0]
if self.lb is None and self.ub is None:
expression = expression - coeff
if self.lb is not None and self.ub is not None:
oldub = self.ub
self.ub = None
self.lb = self.lb - float(coeff)
self.ub = oldub - float(coeff)
elif self.lb is not None:
self.lb = self.lb - float(coeff)
elif self.ub is not None:
self.ub = self.ub - float(coeff)
else:
raise ValueError(
"%s cannot be shaped into canonical form if neither lower or upper constraint bounds are set."
% expression
)
else:
expression = expression - coeff
if self.lb is not None:
self.lb = self.lb - float(coeff)
if self.ub is not None:
self.ub = self.ub - float(coeff)
return expression

@property
Expand Down
2 changes: 1 addition & 1 deletion optlang/scipy_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ def get_linear_coefficients(self, variables):
@six.add_metaclass(inheritdocstring)
class Objective(interface.Objective):
def __init__(self, expression, sloppy=False, **kwargs):
super(Objective, self).__init__(expression, sloppy, **kwargs)
super(Objective, self).__init__(expression, sloppy=sloppy, **kwargs)
if not (sloppy or self.is_Linear):
raise ValueError(
"Scipy only supports linear objectives. %s is not linear." % self)
Expand Down
28 changes: 28 additions & 0 deletions optlang/tests/abstract_test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,34 @@ def test_constraint_get_linear_coefficients_raises(self):
def test_constraint_set_linear_coefficients_raises(self):
self.assertRaises(Exception, self.constraint.set_linear_coefficients, {})

def test_move_constant_to_rhs(self):
x = self.interface.Variable("x")
c1 = self.interface.Constraint(x + 3, lb=0, ub=0)
self.assertEqual(c1.expression - x, 0)
self.assertEqual(c1.lb, -3)
self.assertEqual(c1.ub, -3)

c2 = self.interface.Constraint(x - 3, lb=0, ub=0)
self.assertEqual(c2.expression - x, 0)
self.assertEqual(c2.lb, 3)
self.assertEqual(c2.ub, 3)

c3 = self.interface.Constraint(x - 3, lb=0)
self.assertEqual(c3.expression - x, 0)
self.assertEqual(c3.lb, 3)

c4 = self.interface.Constraint(x - 3, ub=0)
self.assertEqual(c4.expression - x, 0)
self.assertEqual(c4.ub, 3)

c5 = self.interface.Constraint(x + 3, lb=0)
self.assertEqual(c5.expression - x, 0)
self.assertEqual(c5.lb, -3)

c6 = self.interface.Constraint(x + 3, ub=0)
self.assertEqual(c6.expression - x, 0)
self.assertEqual(c6.ub, -3)


@six.add_metaclass(abc.ABCMeta)
class AbstractObjectiveTestCase(unittest.TestCase):
Expand Down
50 changes: 26 additions & 24 deletions optlang/tests/test_netlib_cplex_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ def test_netlib(netlib_tar_path=os.path.join(os.path.dirname(__file__), 'data/ne
netlib_id, os.path.basename(str(__file__)))
yield func

if not os.getenv('TRAVIS', False):
# check that a cloned model also gives the correct result
model = Model.clone(model, use_json=False, use_lp=False)
model.optimize()
if model.status == 'optimal':
model_objval = model.objective.value
else:
raise Exception('No optimal solution found for netlib model %s' % netlib_id)

func = partial(check_objval_against_the_final_netlib_results, netlib_id, model_objval)
func.description = "test_netlib_check_objective_value__against_the_final_netlib_results_after_cloning_%s (%s)" % (
netlib_id, os.path.basename(str(__file__)))
yield func

except ImportError as e:

if str(e).find('cplex') >= 0:
Expand All @@ -136,29 +150,17 @@ def test_fail(self):

if __name__ == '__main__':
# tar = tarfile.open('data/netlib_lp_problems.tar.gz')
# model_paths_in_tar = glob.fnmatch.filter(tar.getnames(), '*.SIF')
# for model_path_in_tar in model_paths_in_tar:
# try:
#
# fhandle = tar.extractfile(model_path_in_tar)
# fhandle = tar.extractfile('./netlib/SEBA.SIF')
# problem = read_netlib_sif_cplex(fhandle)
#
# except cplex.exceptions.CplexSolverError, e:
# print model_path_in_tar
# print problem
# print problem.get_problem_name()
# print e
# fhandle = tar.extractfile('./netlib/ADLITTLE.SIF')
# glpk_problem = read_netlib_sif_glpk(fhandle)
# glp_simplex(glpk_problem, None)
# print glp_get_obj_val(glpk_problem)
# print glpk_problem
# fhandle = tar.extractfile('./netlib/ADLITTLE.SIF')
# glpk_problem = read_netlib_sif_glpk(fhandle)
# model = Model(problem=glpk_problem)
# glp_simplex(glpk_problem, None)
# model.optimize()
# print model.objective.value
# print model
# test_netlib().next()
# model = Model(problem=problem)
# status = model.optimize()
# print(status)
# print(model.objective.value)
# print(model.constraints['VILLKOR7'])
# # print(model)
# model = Model.clone(model, use_lp=False, use_json=False)
# status = model.optimize()
# print(status)
# print(model.constraints['VILLKOR7'])
# print(model.objective.value)
nose.runmodule()
15 changes: 15 additions & 0 deletions optlang/tests/test_netlib_glpk_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@ def check_objval_against_the_final_netlib_results(netlib_id, model_objval):
netlib_id, os.path.basename(str(__file__)))
yield func

if not os.getenv('TRAVIS', False):
# check that a cloned model also gives the correct result
model = Model.clone(model, use_json=False, use_lp=False)
model.optimize()
if model.status == 'optimal':
model_objval = model.objective.value
else:
raise Exception('No optimal solution found for netlib model %s' % netlib_id)

if not os.getenv('TRAVIS', False):
func = partial(check_objval_against_the_final_netlib_results, netlib_id, model_objval)
func.description = "test_netlib_check_objective_value__against_the_final_netlib_results_after_cloning_%s (%s)" % (
netlib_id, os.path.basename(str(__file__)))
yield func


if __name__ == '__main__':
# tar = tarfile.open('data/netlib_lp_problems.tar.gz')
Expand Down
14 changes: 14 additions & 0 deletions optlang/tests/test_netlib_gurobi_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,20 @@ def test_netlib(netlib_tar_path=os.path.join(os.path.dirname(__file__), 'data/ne
netlib_id, os.path.basename(str(__file__)))
yield func

if not os.getenv('TRAVIS', False):
# check that a cloned model also gives the correct result
model = Model.clone(model, use_json=False, use_lp=False)
model.optimize()
if model.status == 'optimal':
model_objval = model.objective.value
else:
raise Exception('No optimal solution found for netlib model %s' % netlib_id)

func = partial(check_objval_against_the_final_netlib_results, netlib_id, model_objval)
func.description = "test_netlib_check_objective_value__against_the_final_netlib_results_after_cloning_%s (%s)" % (
netlib_id, os.path.basename(str(__file__)))
yield func

except ImportError as e:

if str(e).find('gurobipy') >= 0:
Expand Down

0 comments on commit 1948683

Please sign in to comment.