coin-or / coin-or/CyLP

How to get the dualConstraintSolution from cbcModel?

Open
#130 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JetBrains MPS
Stars
192
Forks
70
PR merge metrics
No merged PRs in 30d

Description

Hello!

I'm trying to use `CyCbcModel` to solve a supply/demand problem with the enforcement of minimum supply. The most desirable outcome of the model is the marginal price. In case when I use `CyClpSimplex` to solve the problem without the minimum supply thresholds (so, without integer variables), I just take the elements of `dualConstraintSolution` that correspond to demand constraints. But `CyCbcModel` doesn't have that property.

I found a way to retrieve the `dualConstraintSolution` via `cbcModel.osiSolverInteface.clpModel` but this leads to an error when the script exits: `pointer being freed was not allocated`. Please advise what would be the right way to make `dualConstraintSolution` available in `CyCbcModel`.

I wrote a simple script, so you could easily reproduce the memory issue:
```python
from cylp.cy import CyClpSimplex
from cylp.py.modeling.CyLPModel import CyLPModel

#
# The very simplistic scenario in which the two suppliers have
# a minimum supply threshold. Intentionally, the one with lower
# threshold supplies at a higher cost. The model is supposed
# to choose which of the two will satisfy the demand.
#

# supplier 1
capacity_1 = 100
threshold_1 = 50
cost_1 = 10

# supplier 2
capacity_2 = 100
threshold_2 = 70
cost_2 = 8

# demand
demand = 60

# model
model = CyLPModel()

supply_1 = model.addVariable('supply_1', 1)
model += 0 <= supply_1 <= capacity_1
supply_2 = model.addVariable('supply_2', 1)
model += 0 <= supply_2 <= capacity_2

switch_1 = model.addVariable('switch_1', 1, isInt=True)
model += 0 <= switch_1 <= 1
switch_2 = model.addVariable('switch_2', 1, isInt=True)
model += 0 <= switch_2 <= 1

# enforce threshold
model += supply_1 - threshold_1 * switch_1 >= 0
model += supply_1 - capacity_1 * switch_1 <= 0
model += supply_2 - threshold_2 * switch_2 >= 0
model += supply_2 - capacity_2 * switch_2 <= 0

# demand
model += supply_1 + supply_2 == demand

# minimise cost
model.objective = cost_1 * supply_1 + cost_2 * supply_2

clpModel = CyClpSimplex(model)
cbcModel = clpModel.getCbcModel()
cbcModel.solve()

# So far everything works and we can see which supplier has been chosen
print(cbcModel.primalVariableSolution)

# I need to retrieve the dualConstraintSolution, which is necessary to get the marginal price.
# The only way I found is via cbcModel.osiSolverInteface.clpModel
# and it prints the right value
print(cbcModel.osiSolverInteface.clpModel.dualConstraintSolution[-1]) # for demand constraint
#
# but in the end of the script python fails with the error:
# pointer being freed was not allocated
#
# It seems that just accessing cbcModel.osiSolverInteface.clpModel
# somewhere in the code causes this failure upon objects destruction
# when the script is about to terminate
```

```
$ python --version
Python 3.9.7
$ pip list cylp
Package Version
---------- -------
cylp 0.91.4
numpy 1.21.2
pip 21.1.2
scipy 1.7.1
setuptools 57.0.0
wheel 0.36.2
$ cbc
Welcome to the CBC MILP Solver
Version: 2.10.3
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the CyCbcModel API and the osiSolverInteface.clpModel access shown in the reproduction script. Run the script to confirm the dual value and shutdown error, then inspect the binding and ownership behavior around these objects. Done means the supported API exposes the required result or clearly documents its absence without triggering an invalid free.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.