Skip to content

How to get the dualConstraintSolution from cbcModel? #130

@grinya007

Description

@grinya007

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:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions