Skip to content

Commit

Permalink
Merge pull request #211 from jonls/doc-chained-relation
Browse files Browse the repository at this point in the history
lp: Raise error on certain use of Expression/Relation
  • Loading branch information
jonls committed Mar 2, 2017
2 parents 62ae44e + 08a07a1 commit 13dfea2
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 3 deletions.
19 changes: 18 additions & 1 deletion psamm/lpsolver/lp.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with PSAMM. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright 2014-2015 Jon Lund Steffensen <jon_steffensen@uri.edu>
# Copyright 2014-2017 Jon Lund Steffensen <jon_steffensen@uri.edu>

"""Base objects for representation of LP problems.
Expand Down Expand Up @@ -166,6 +166,12 @@ class Expression(object):
>>> rel = Expression({'x': 2}) >= Expression({'y': 3})
>>> str(rel)
'2*x - 3*y >= 0'
.. warning::
Chained relations cannot be converted to multiple
relations, e.g. ``4 <= e <= 10`` will fail to produce the intended
relations!
"""

def __init__(self, variables={}, offset=0):
Expand Down Expand Up @@ -492,6 +498,17 @@ def __str__(self):
def __repr__(self):
return str('<{} {}>').format(self.__class__.__name__, repr(str(self)))

def __nonzero__(self):
# Override __nonzero__ (and __bool__) here so we can avoid bugs when a
# user mistakenly thinks that "4 <= x <= 100" should work.
# This kind of expression expands to "4 <= x and x <= 100" and since we
# cannot override the "and" operator, the relation "4 <= x" is
# converted to a bool. Override here to raise an error to make sure
# that this syntax always fails.
raise ValueError('Unable to convert relation to bool')

__bool__ = __nonzero__


@enum.unique
class ObjectiveSense(enum.Enum):
Expand Down
17 changes: 15 additions & 2 deletions psamm/tests/test_lpsolver_lp.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@
# You should have received a copy of the GNU General Public License
# along with PSAMM. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright 2015 Jon Lund Steffensen <jon_steffensen@uri.edu>
# Copyright 2015-2017 Jon Lund Steffensen <jon_steffensen@uri.edu>

import math
import unittest

from psamm.lpsolver import lp

# Beware: Expressions cannot be tested for equality using normal
# assertEqual because the equality operator is overloaded!


class DummyRangedPropertyContainer(object):
def __init__(self):
Expand Down Expand Up @@ -306,10 +309,15 @@ def test_expression_contains_product(self):


class TestRelation(unittest.TestCase):
def assertExpressionEqual(self, e1, e2):
"""Assert that expressions are equal."""
self.assertEqual(e1.offset, e2.offset)
self.assertEqual(dict(e1.values()), dict(e2.values()))

def test_create_relation(self):
e = lp.Expression({'x1': 4})
r = lp.Relation(lp.RelationSense.Greater, e)
self.assertEqual(r.expression, e)
self.assertExpressionEqual(r.expression, e)
self.assertEqual(r.sense, lp.RelationSense.Greater)

def test_relation_with_offset_to_string(self):
Expand All @@ -322,6 +330,11 @@ def test_relation_without_offset_to_string(self):
r = lp.Relation(lp.RelationSense.Equals, e)
self.assertEqual(str(r), 'x1 == 0')

def test_chained_relation(self):
e = lp.Expression({'x1': 1})
with self.assertRaises(ValueError):
self.assertFalse(4 <= e <= 10)


class MockResult(lp.Result):
def __init__(self, values):
Expand Down

0 comments on commit 13dfea2

Please sign in to comment.