diff --git a/README.rst b/README.rst
index 4bc54392..98bd32c9 100644
--- a/README.rst
+++ b/README.rst
@@ -44,7 +44,7 @@ The following dependencies are needed.
The following are optional dependencies that allow other solvers to be used.
- `cplex `__ (LP, MILP, QP, MIQP)
-- `gurobipy `__ (LP, MILP (QP and MIQP support will be added in the future))
+- `gurobipy `__ (LP, MILP, QP, MIQP)
- `scipy `__ (LP)
@@ -134,9 +134,6 @@ Future outlook
`CPLEX `__
etc.)
-The optlang `trello board `__
-also provides a good overview of the project's roadmap.
-
.. |PyPI| image:: https://img.shields.io/pypi/v/optlang.svg?maxAge=2592000
:target: https://pypi.python.org/pypi/optlang
.. |License| image:: http://img.shields.io/badge/license-APACHE2-blue.svg
diff --git a/optlang/cplex_interface.py b/optlang/cplex_interface.py
index 20868731..2ce5cfed 100644
--- a/optlang/cplex_interface.py
+++ b/optlang/cplex_interface.py
@@ -470,7 +470,7 @@ class ResultsStreamHandler(StreamHandler):
def flush(self):
self.logger.debug(self.getvalue())
- logger = logging.getLogger()
+ logger = logging.getLogger(__name__ + ".Model") # TODO: Make the logger name specific to each solver instance
logger.setLevel(logging.CRITICAL)
error_stream_handler = ErrorStreamHandler(logger)
warning_stream_handler = WarningStreamHandler(logger)
diff --git a/optlang/expression_parsing.py b/optlang/expression_parsing.py
index b83c68f6..1dc0b4cc 100644
--- a/optlang/expression_parsing.py
+++ b/optlang/expression_parsing.py
@@ -13,7 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from optlang.symbolics import One
+from optlang.symbolics import Integer
+
+one = Integer(1)
def parse_optimization_expression(obj, linear=True, quadratic=False, expression=None, **kwargs):
@@ -84,7 +86,7 @@ def _parse_linear_expression(expression, expanded=False, **kwargs):
for var in coefficients:
if not (var.is_Symbol):
- if var == One:
+ if var == one:
constant = var
offset = float(coefficients[var])
elif expanded:
diff --git a/optlang/gurobi_interface.py b/optlang/gurobi_interface.py
index c8f4a345..0cba066f 100644
--- a/optlang/gurobi_interface.py
+++ b/optlang/gurobi_interface.py
@@ -402,10 +402,10 @@ class Configuration(interface.MathematicalProgrammingConfiguration):
def __init__(self, lp_method='primal', qp_method='primal', presolve=False,
verbosity=0, timeout=None, *args, **kwargs):
super(Configuration, self).__init__(*args, **kwargs)
+ self.verbosity = verbosity
self.lp_method = lp_method
self.qp_method = qp_method
self.presolve = presolve
- self.verbosity = verbosity
self.timeout = timeout
@property
diff --git a/optlang/interface.py b/optlang/interface.py
index c86144ce..3576c7c0 100644
--- a/optlang/interface.py
+++ b/optlang/interface.py
@@ -33,6 +33,7 @@
import six
+import optlang
from optlang.exceptions import IndicatorConstraintsNotSupported
from optlang.util import parse_expr, expr_to_json, is_numeric, SolverTolerances
@@ -463,7 +464,8 @@ def _canonicalize(self, expression):
elif isinstance(expression, int):
return symbolics.Integer(expression)
else:
- # expression = expression.expand() This would be a good way to canonicalize, but is quite slow
+ if optlang._USING_SYMENGINE:
+ expression = expression.expand() # This is a good way to canonicalize, but is quite slow for sympy
return expression
@property
@@ -1630,6 +1632,4 @@ def __setstate__(self, state):
# model.remove(x1)
- import optlang
-
model.interface = optlang.glpk_interface
diff --git a/optlang/scipy_interface.py b/optlang/scipy_interface.py
index 0d82b878..dea8c556 100644
--- a/optlang/scipy_interface.py
+++ b/optlang/scipy_interface.py
@@ -418,6 +418,7 @@ def coefficient_dict(self, names=True):
if self.expression.is_Add:
coefficient_dict = {variable: coef for variable, coef in
self.expression.as_coefficients_dict().items() if variable.is_Symbol}
+ coefficient_dict = {var: float(coef) for var, coef in coefficient_dict.items()}
elif self.expression.is_Atom and self.expression.is_Symbol:
coefficient_dict = {self.expression: 1}
elif self.expression.is_Mul and len(self.expression.args) <= 2:
@@ -496,7 +497,7 @@ def coefficient_dict(self):
coefficient_dict = {}
else:
raise ValueError("Invalid expression: " + str(self.expression))
- coefficient_dict = {var.name: coef for var, coef in coefficient_dict.items()}
+ coefficient_dict = {var.name: float(coef) for var, coef in coefficient_dict.items()}
return coefficient_dict
def set_linear_coefficients(self, coefficients):
@@ -509,7 +510,7 @@ def set_linear_coefficients(self, coefficients):
def get_linear_coefficients(self, variables):
if self.problem is not None:
self.problem.update()
- return {v: self.problem.problem.objective.get(v.name, 0) for v in variables}
+ return {v: float(self.problem.problem.objective.get(v.name, 0)) for v in variables}
else:
raise Exception("Can't get coefficients from solver if objective is not in a model")
diff --git a/optlang/symbolics.py b/optlang/symbolics.py
index f9bdeb5e..228eda86 100644
--- a/optlang/symbolics.py
+++ b/optlang/symbolics.py
@@ -58,9 +58,9 @@
Real = symengine.RealDouble
Basic = symengine.Basic
Number = symengine.Number
- Zero = Integer(0)
- One = Integer(1)
- NegativeOne = Integer(-1)
+ Zero = Real(0)
+ One = Real(1)
+ NegativeOne = Real(-1)
sympify = symengine.sympy_compat.sympify
Add = symengine.Add
@@ -113,9 +113,9 @@ def mul(*args):
Real = sympy.RealNumber
Basic = sympy.Basic
Number = sympy.Number
- Zero = Integer(0)
- One = Integer(1)
- NegativeOne = Integer(-1)
+ Zero = Real(0)
+ One = Real(1)
+ NegativeOne = Real(-1)
sympify = sympy.sympify
Add = sympy.Add
diff --git a/optlang/tests/abstract_test_cases.py b/optlang/tests/abstract_test_cases.py
index 9dc49a75..133cf53d 100644
--- a/optlang/tests/abstract_test_cases.py
+++ b/optlang/tests/abstract_test_cases.py
@@ -20,6 +20,7 @@
import six
from optlang import interface
+from optlang import symbolics
import optlang
import pickle
import json
@@ -350,6 +351,18 @@ def test_new_invalid_name_raises(self):
with self.assertRaises(Exception):
const.name = "This\ttab"
+ def test_construct_with_sloppy(self):
+ x, y, z, w = self.model.variables[:4]
+ const = self.interface.Constraint(
+ symbolics.add([symbolics.mul(symbolics.One, var) for var in [x, y, z]]),
+ lb=0,
+ sloppy=True
+ )
+ self.model.add(const)
+ self.model.update()
+
+ self.assertTrue(const.get_linear_coefficients([x, y, z, w]) == {x: 1, y: 1, z: 1, w: 0})
+
@six.add_metaclass(abc.ABCMeta)
class AbstractObjectiveTestCase(unittest.TestCase):
@@ -390,6 +403,17 @@ def test_new_invalid_name_raises(self):
with self.assertRaises(Exception):
obj.name = "This\ttab"
+ def test_construct_with_sloppy(self):
+ x, y, z, w = self.model.variables[:4]
+ obj = self.interface.Objective(
+ symbolics.add([symbolics.mul((symbolics.One, var)) for var in [x, y, z]]),
+ direction="min",
+ sloppy=True
+ )
+ self.model.objective = obj
+
+ self.assertTrue(obj.get_linear_coefficients([x, y, z, w]) == {x: 1, y: 1, z: 1, w: 0})
+
@six.add_metaclass(abc.ABCMeta)
class AbstractModelTestCase(unittest.TestCase):
diff --git a/optlang/tests/test_expression_parsing.py b/optlang/tests/test_expression_parsing.py
index 2b307b82..f03f6eb3 100644
--- a/optlang/tests/test_expression_parsing.py
+++ b/optlang/tests/test_expression_parsing.py
@@ -68,10 +68,18 @@ def test_parse_non_expanded_quadratic_expression(self):
target = {frozenset([x]): 1, frozenset([y]): 1, frozenset([x, y]): 2, frozenset([z]): -1}
linear_target = {z: 4}
- offset_const, linear_terms_const, quad_terms_const = parse_optimization_expression(Constraint(expr, lb=0), quadratic=True)
- offset_obj, linear_terms_obj, quad_terms_obj = parse_optimization_expression(Objective(expr), linear=False)
-
- self.assertEqual(offset_const, -4)
+ constraint = Constraint(expr, lb=0)
+ offset_const, linear_terms_const, quad_terms_const = parse_optimization_expression(
+ constraint,
+ quadratic=True
+ )
+ offset_obj, linear_terms_obj, quad_terms_obj = parse_optimization_expression(
+ Objective(expr),
+ expression=expr,
+ linear=False
+ )
+
+ self.assertEqual(offset_const - constraint.lb, -4 + offset)
self.assertEqual(offset_obj, -4 + offset)
_compare_term_dicts(self, linear_terms_const, linear_target)
_compare_term_dicts(self, linear_terms_obj, linear_target)
diff --git a/optlang/tests/test_symbolics.py b/optlang/tests/test_symbolics.py
index 22655344..92fd06d7 100644
--- a/optlang/tests/test_symbolics.py
+++ b/optlang/tests/test_symbolics.py
@@ -5,7 +5,7 @@
class SymbolicsTestCase(unittest.TestCase):
def test_add_identity(self):
- self.assertEqual(optlang.symbolics.add(), 0)
+ self.assertEqual(optlang.symbolics.add(), 0.0)
def test_mul_identity(self):
- self.assertEqual(optlang.symbolics.mul(), 1)
+ self.assertEqual(optlang.symbolics.mul(), 1.0)