pythonlinear-programmingcoin-or

Initialize integer variables in CyLP


I'm currently using CyLP package in Python for mixed-integer linear programming. However, I'm not able to initialize integer variables. According to the documentation, I have passed isInt = True to addVariable method but it does nothing.

A minimal example of my program is shown below. The optimal value should be 2 when x = y = 1, but the result is not as expected.

import cylp
from cylp.cy import CyClpSimplex
print(cylp.__version__) # 0.91.0

s = CyClpSimplex()
x = s.addVariable('x', 1, isInt = True)
y = s.addVariable('y', 1, isInt = True)
s += x >= 0.5
s += y >= 0.7

s.objective = x + y
s.optimizationDirection = 'min'

s.primal()
# Clp3002W Empty problem - 0 rows, 2 columns and 0 elements
# Clp0000I Optimal - objective value 1.2

x_opt = s.primalVariableSolution['x'][0]
y_opt = s.primalVariableSolution['y'][0]
print(x_opt, y_opt) # 0.5, 0.7

Is there any other way to initialize integer variables in CyLP? Or I'm missing something about addVariable method?

By the way, I wonder what Clp3002W Empty problem means in the output of s.primal().

Thanks in advance.


Solution

  • This is a bit mean.

    Clp is a Linear-Programming solver while you are trying to do Mixed-Integer-Programming.

    You will need to use Cbc which is built on top of Clp (same person / same people). I think, that one can recognize that most of this is a one-man project over decades (unintuitive design; maybe).

    Yes, CyLp is based on Cbc, but usage still needs care!

    Assuming, that your variable-definitions are okay (did not check), you will need to do something like:

    # model = CyClpSimplex()                                           
    # ...
    
    cbcModel = model.getCbcModel()  # Clp -> Cbc model / LP -> MIP
    
    status = cbcModel.solve()           #-> "Call CbcMain. Solve the problem
                                        #   "using the same parameters used
                                        #   "by CbcSolver."
                                        # This deviates from cylp's docs which are sparse!
                                        # -> preprocessing will be used and is very important!
    

    See also this wrapper:

    if data[s.BOOL_IDX] or data[s.INT_IDX]:
        # MIP
        # Convert model
        cbcModel = model.getCbcModel()
    
        # cylp: /cylp/cy/CyCbcModel.pyx#L134
        # Call CbcMain. Solve the problem using the same parameters used by
        # CbcSolver. Equivalent to solving the model from the command line
        # using cbc's binary.
        cbcModel.solve()
        status = cbcModel.status
    else:
        # LP
        # cylp: /cylp/cy/CyClpSimplex.pyx
        # Run CLP's initialSolve. It does a presolve and uses primal or dual
        # Simplex to solve a problem.
        status = model.initialSolve()
    

    I wrote these things a lot of time ago, therefore i cannot give you exact details (background behind these calls and the comments).

    But in general: it's hard to grasp what exactly is happening internally in Cbc (although there was some work in improving the API i think; probably NOT reflected in Cylp though). For example: it's not trivial to use Cbc from code in the same way as the Cbc executable behaves.

    By the way, I wonder what Clp3002W Empty problem means in the output of s.primal().

    Clp3002W Empty problem - 0 rows, 2 columns and 0 elements

    My interpretation:

    Your (transformed) model has no constraints. You added constraints to enforce variable-bounds but Clp/Cbc is advanced enough (it's very very advanced) to transform these constraints into variable-bounds (no constraints anymore!), which are handled by special treatment in the Simplex-Routine.

    0 rows = 0 constraints
    
    0 elements = 0 non-zero elements in your constraint-matrix of size
            rows * cols = 0 * 2 = 0