1

Let's suppose we have X=variable(n, boolean=True) and an array B of length n containing repeated integers.

I want to write a constraint so that the element by element multiplication of the solution of X by B is an Array containing as elements 0 or a single value p, with p being an element of B.

Example of a solution with

B=[2,3,4,3,3,2,2,3,5,4,7,7,2]

would be X1=[0,1,0,1,1,0,0,0,0,0,0,0,0] Or X2=[1,0,0,0,0,0,1,0,0,0,0,0,1]

But not X3=[0,0,1,1,0,0,1,0,0,0,0,0,1]

I tried to set as constraint, using numpy along with cvxpy

constr=[cvxpy.sum(np.multiply(X,B))/sum(X)==max(np.multiply(X,B))]

But I can't solve the problem I create using the constraint above

  • Introduce a new variable ``p`` to represent one of the elements of ``B``. This can be done as in https://docs.mosek.com/modeling-cookbook/mio.html#fixed-set-of-values Then write the constraint ``x[i]=0 or x[i]-p=0`` for all ``i``. That can be done using methods shown earlier in the same cookbook, it is a standard MIP model of a disjunction. – Michal Adamaszek Jan 20 '23 at 07:15
  • I meant ``x[i]=0 or x[i]*b[i]-p=0`` – Michal Adamaszek Jan 20 '23 at 07:52
  • Hi Michael, thanks to your tips I could get to solve my issue, special thanks for guiding me to the Mosek cookbook, very useful. While the problem itself is trivial and basically works as the mode of the set, I neeed this as a part of a bigger problem where I have to solve a set cover problem with the additional constraint that each set contains only elements sharing a common attribute – Andrea Montalbani Jan 28 '23 at 14:42

1 Answers1

1

I could finally set up a solution, thanks to Michal Adamaszek.

Find below thefull code for the solver and a sample using three different arrays.

I had to use two distinct set of auxiliary variables, one for achieving the "Fixed set of values" constraint and one to express "Disjunctive constraints"

from typing import List
import pandas as pd
import numpy as np
import cvxpy as cp
M=1000**3


def aux_variable_constraint(aux:cp.Variable):
    return [cp.sum(aux,axis=0)==1]

def same_number_constraint(X:cp.Variable,arr:List,aux:cp.Variable,d_aux:cp.Variable):
    n=list(set(arr)) @ aux
    Z=cp.multiply(X,arr)

    constr=[]
    for j in range(X.shape[0]):
        constr+=[Z[j]<=n+M*(1-d_aux[j][0]),
        Z[j]>=n-M*(1-d_aux[j][0]),
        Z[j]<=M*(1-d_aux[j][1])
        ]
    return constr 


def pick_most_frequent_elements_solver(arr):
    aux=cp.Variable(len(set(arr)), boolean=True)
    d_aux=cp.Variable((len(arr),2), boolean=True)
    X=cp.Variable(len(arr),boolean=True)

    #obj definition, just to pick as much numbers as possible
    obj=cp.Maximize(cp.sum(X,axis=0))

    #constraints
    constraints=[]
    constraints+=aux_variable_constraint(aux)
    constraints+=[cp.sum(d_aux,axis=1)==np.ones(len(arr))]
    constraints+=same_number_constraint(X,arr,aux,d_aux)

    problem=cp.Problem(obj,constraints)

    problem.solve()
    print(X.value*arr)

A=[4,3,4,3,3,2,3,3,5,4,4,4,4]
B=[4,3,4,3,3,2,3,3,5,4,3,3,4]
C=[4,3,2,2,2,2,3,3,5,2,2,4,4]

pick_most_frequent_elements_solver(A)
pick_most_frequent_elements_solver(B)
pick_most_frequent_elements_solver(C)