optimizationmathematical-optimizationcvxpycvxoptmosek

cvxpy integer variable - exclude certain integer values from the solution


I have the following problem and I can't figure out if cvxpy can do what I need. Context: I optimize portfolios. When buying bonds and optimizing the quantity of each bond to buy, it's only possible to buy each bond only in multiples of 1,000 units. However, the minimum piece required to be bought is most of the time 10,000. This means we either don't buy a bond at all or if we buy it, the quantity bought has to be either 10,000, 11,000, 12,000 and so on. Is there a way (it seems it doesn't) to restrict certain values from the possible solutions an integer variable can have? So let's assume we have an integer variable x that is non negative. We basically want to buy 1000x but we know that x can be x = {0, 10, 11, 12, ...} Is it possible to skip values 1.. 9 without adding other variables?

For example:

    import numpy as np
    import pandas as pd
    import cvxpy as cvx
    np.random.seed(1)
    # np.random.rand(3)
    p = pd.DataFrame({'bond_id': ['s1','s2', 's3', 's4', 's5', 's6', 's7','s8','s9', 's10'],
                      'er': np.random.rand(10),
                      'units': [10000,2000,3000,4000,27000,4000,0,0,0,0] })
    
    final_units = cvx.Variable( 10, integer=True)
    constraints = list()
    constraints.append( final_units >= 0)
    constraints.append(sum(final_units*1000) <= 50000)
    constraints.append(sum(final_units*1000) >= 50000)
    constraints.append(final_units <= 15)
                      
    obj = cvx.Maximize(   final_units @   np.array(list(p['er']))    )
    prob = cvx.Problem(obj, constraints)
    solve_val = prob.solve()
    print("\n* solve_val = {}".format(solve_val))
    

    solution_value = prob.value
    solution = str(prob.status).lower() 
    print("\n** SOLUTION 3:  {}     Value: {} ".format(solution, solution_value))
    print("\n* final_units -> \n{}\n".format(final_units.value))
    p['FINAL_SOL'] = final_units.value * 1000
    print("\n* Final Portfolio: \n{}\n".format(p))

This solution is a very simplified version of the problem I face. The final vector final_units can suggest values like in this example where we have to buy 5,000 units of bond s9, however I can't since the min I can buy is 10,000. I know I could add an additional integer vector to express an OR condition, but in reality my real problem is way bigger than this, I have thousand of integer variables already. Hence, I wonder if there's a way to exclude values from 1 to 9 without adding additional variables to the problem. Thank you


Solution

  • No, not with CVXPY. You can model it with an integer variable x[i] plus a binary variable y[i], and using the constraints (in math notation):

     y[i] * 10 <= x[i] <= y[i] * 15
    

    This results in x[i] ∈ {0, 10..15}.

    Some solvers have a variable type for this: semi-integer variables. Using this you don't need to have an extra binary variable and these 2 constraints. CVXPY does not support this variable type AFAIK.