Skip to content

TypeError when LpAffineExpressions are Specified Using Decimals #815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
4 of 15 tasks
mark92223 opened this issue Mar 21, 2025 · 8 comments
Open
4 of 15 tasks

TypeError when LpAffineExpressions are Specified Using Decimals #815

mark92223 opened this issue Mar 21, 2025 · 8 comments

Comments

@mark92223
Copy link

mark92223 commented Mar 21, 2025

When a LpAffineExpression is extended using Decimals, as of pulp 2.9.0 (and before), it would work without raising any exception and returning accurate results. Beginning in 3.0.0, it now raises a TypeError in the addInPlace method:

self.constant += other.constant * sign

A reproducing pytest file which can be run, passing in 2.9.0, and failing in 3.0.2(or 3.0.0): https://gist.github.com/mark92223/e590c21df931eda1927ae4230fec118d .

Is using Decimals in this context unsupported in Pulp?

Thank you!

Details for the issue

What did you do?

What did you expect to see?

The test to pass

What did you see instead?

    def test_it(self):
        from pulp import LpProblem, LpVariable, LpMaximize, LpAffineExpression
        from pulp.apis import GLPK_CMD

        m1, m2, extra = 3, Decimal("8.1"), 5

        problem = LpProblem("graph", LpMaximize)
        y = LpVariable("y", lowBound=0, upBound=Decimal("32.24"), cat="Continuous")
        problem += y

        expression = LpAffineExpression()
        include_extra = LpVariable("include_extra1", cat="Binary")
        x = LpVariable("x", lowBound=0,
                       upBound=random.randint(3, 50), cat="Continuous")
        # y = 3x + 5 | y = 3x
        expression += x * m1 + include_extra*extra - y
        problem += expression == 0

        second_expression = LpAffineExpression()
        # y = 8.1x - 6
>       second_expression += x * m2 - 6 - y

test_pypi_packages_for_wms.py:125:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/mark/ve3.13/lib/python3.13/site-packages/pulp/pulp.py:928: in __iadd__
    return self.addInPlace(other)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = 0.0, other = 8.1*x + -1*y + -6.0, sign = 1

    def addInPlace(self, other, sign=1):
        """
        :param int sign: the sign of the operation to do other.
            if we add other => 1
            if we subtract other => -1
        """
        if isinstance(other, int) and (other == 0):
            return self
        if other is None:
            return self
        if isinstance(other, LpElement):
            # if a variable, we add it to the dictionary
            self.addterm(other, sign)
        elif isinstance(other, (LpAffineExpression, LpConstraint)):
            # if an expression, we add each variable and the constant
>           self.constant += other.constant * sign
E           TypeError: unsupported operand type(s) for +=: 'float' and 'decimal.Decimal'

/home/mark/ve3.13/lib/python3.13/site-packages/pulp/pulp.py:888: TypeError

Useful extra information

The info below often helps, please fill it out if you're able to. :)

What operating system are you using?

  • Windows: ( version: ___ )
  • Linux: (Arch Linux)
  • Mac OS: ( version: ___ )
  • Other: ___

I'm using python version:

  • 3.7
  • 3.8
  • 3.9
  • 3.10
  • 3.11
  • Other: 3.13

I installed PuLP via:

Did you also

@mark92223 mark92223 changed the title TypeError when Constraints are Specified Using Decimals TypeError when LpAffineExpressions are Specified Using Decimals Mar 21, 2025
@MBradbury
Copy link
Contributor

@pchtsp this looks like another one I broke, will take a look.

@MBradbury
Copy link
Contributor

After some investigation, the reason this happens is that Decimal allows operations with ints but not floats.

$ python
Python 3.12.3 (main, Feb  4 2025, 14:48:35) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from decimal import Decimal as D
>>> 0 + D("7")
Decimal('7')
>>> 0.0 + D("7")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal'

@mark92223 a quick fix to try, instead of declaring your constraints as you have can you either do:

        second_expression = LpAffineExpression(constant=Decimal("0"))
        # y = 8.1x - 6
        second_expression += x * m2 - 6 - y

or

        # y = 8.1x - 6
        second_expression = x * m2 - 6 - y

I expect this will not fully solve the issue, as in my tests I get an exception in assignConsSlack now.

MBradbury added a commit to MBradbury/pulp that referenced this issue Mar 24, 2025
@MBradbury
Copy link
Contributor

Taking a look GLPK does not use assignConsSlack, so that is probably why you have not hit an issue there before.

@mark92223
Copy link
Author

mark92223 commented Mar 31, 2025

The workaround does seem to resolve the crash, thanks (And obviously, it still returns the correct result)!

I personally still think that this ought to work as is, but appreciate the help regardless.

@pchtsp
Copy link
Collaborator

pchtsp commented Apr 8, 2025

I'm closing as the issue was with Decimals.

@pchtsp pchtsp closed this as completed Apr 8, 2025
pchtsp pushed a commit that referenced this issue Apr 8, 2025
* Add tests for #815

* Run black

* Move test_decimal_815 to run just for GLPK_CMD

* pulp/tests/test_pulp.py

Run black
@mark92223
Copy link
Author

@pchtsp So, are you saying that decimals are not supported in this context? This is not clear in the docs and previously worked successfully.

@pchtsp
Copy link
Collaborator

pchtsp commented Apr 10, 2025

I had no idea the Decimal library existed. The error seems pretty explicit: floats and Decimals do not seem to get along.

I'm unsure why this worked before.
Options:

  1. cast the Decimal into float (on your side)
  2. cast all values into floats (on pulp's side). [I imagine something of this was going on before.]
  3. any other thing?

Option 2 seems easy but then, why would you Decimal if the values are going to be converted to floats under the hood?

If you have any suggestion or better want to make a PR, go ahead!

@pchtsp pchtsp reopened this Apr 10, 2025
@MBradbury
Copy link
Contributor

For this issue, Decimal really will have only worked with a few of the backends. GLPK only works as it does not use assignConsSlack to assign outputs from the solver. GLPK still has an issue as it will read coefficients from the solver in as either int or float (https://github.com/coin-or/pulp/blob/master/pulp/apis/glpk_api.py#L134). So you will not get results in as a Decimal.

One option could be to have a global option which specifies what type the coefficients will be (e.g., int or float/Decimal).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants