Reputation: 4672
I have a problem and using ortools is too slow.
The problem is that there is an item that stores have but at different prices. I need to select where to pick those items from to achieve the lowest total price as well as not exceeding 4 stores.
This is the code I have but it is slow if the supplied cost matrix has more than 4 stores. The problem lies in the store max limit constraint. Is there any other way that this can be coded differently to improve speed?
import numpy as np
from ortools.sat.python import cp_model
from ortools.linear_solver import pywraplp
#cost matrix, where j are stores, i are items
C = np.array([[38, 13, 73, 10, 76, 6, 80, 65, 17, 2],
[77, 72, 7, 26, 51, 21, 19, 85, 12, 29],
[30, 15, 51, 69, 88, 88, 95, 97, 87, 14],
[10, 8, 64, 62, 23, 58, 2, 1, 61, 82],
[ 9, 89, 14, 48, 73, 31, 72, 4, 71, 22],
[50, 58, 4, 69, 25, 44, 77, 27, 53, 81],
[42, 83, 16, 65, 69, 26, 99, 88, 8, 27],
[26, 23, 10, 68, 24, 28, 38, 58, 84, 39],
[ 9, 33, 35, 11, 24, 16, 88, 26, 72, 93],
[75, 63, 47, 33, 89, 24, 56, 66, 78, 4],
[ 1, 78, 7, 53, 86, 71, 3, 77, 92, 22],
[76, 8, 78, 73, 76, 77, 44, 21, 31, 37],
[ 8, 46, 69, 58, 83, 97, 14, 11, 24, 82],
[ 8, 25, 75, 93, 21, 33, 13, 66, 95, 61],
[25, 83, 98, 3, 93, 99, 11, 55, 97, 83],
[87, 71, 67, 72, 49, 55, 16, 6, 18, 43],
[21, 49, 23, 14, 98, 54, 85, 11, 97, 56],
[62, 57, 90, 22, 97, 84, 26, 15, 14, 85],
[44, 7, 78, 57, 60, 16, 25, 10, 67, 72],
[54, 70, 37, 22, 41, 78, 92, 50, 48, 78]])
# The solver:
def Solve_Cost_Matrix_2(cost):
model = cp_model.CpModel()
max_stops = 4
#generate ranges
num_items = len(cost)
num_shops = len(cost[0])
all_items = range(num_items)
all_shops = range(num_shops)
# Create bool Variable matrix
x = []
for i in all_items:
t=[]
for j in all_shops:
t.append(model.NewBoolVar(f'i{i}_j{j}'))
x.append(t)
# Constraints
# Each item only assigned once to any store.
[model.Add(sum(x[i][j] for j in all_shops) == 1) for i in all_items]
# Adding the intermediate variable to constrain the number of the stores.
s=[]
for j in all_shops:
s.append( model.NewBoolVar(f's_{j}') )
for j in all_shops:
model.Add(sum(x[i][j] for i in all_items) >= 1).OnlyEnforceIf(s[j])
model.Add(sum(x[i][j] for i in all_items) == 0).OnlyEnforceIf(s[j].Not())
model.Add(sum(s[j] for j in all_shops) <= max_stops)
# Create the Objective function Variable
total_cost = model.NewIntVar(0, 1000000, 'total_cost')
# Create the Objective function, Minimize (Sum of cost)
model.Add(total_cost == (sum(x[i][j] * cost[i][j] for j in all_shops for i in all_items )))
model.Minimize(total_cost)
#Initialize the Solver
solver = cp_model.CpSolver()
status = solver.Solve(model)
print(solver.ResponseStats())
Total_Cost,senario_cost = 0, 0
#printing the solution
if status == cp_model.OPTIMAL:
senario_cost={'Items':[],'Assigned_to':[],'Item_cost':[],'Num_stops':0,'cost':[]}
Total_Cost = solver.ObjectiveValue()
for i in range(num_items):
for j in range(num_shops):
if solver.Value(x[i][j]) == 1:
senario_cost['Items'].append(i)
senario_cost['Assigned_to'].append(j)
senario_cost['Item_cost'].append(cost[i][j])
senario_cost['Num_stops'] = len(set(senario_cost['Assigned_to']))
senario_cost['cost'] = cost
return Total_Cost,senario_cost
else: return None, None
Output:
CpSolverResponse:
status: OPTIMAL
objective: 213
best_bound: 213
booleans: 210
conflicts: 106343
branches: 158796
propagations: 4242079
integer_propagations: 7844526
restarts: 878
lp_iterations: 0
walltime: 6.90529
usertime: 6.90529
deterministic_time: 4.67974
primal_integral: 0
CPU times: user 6.86 s, sys: 41 ms, total: 6.9 s
Wall time: 6.95 s
Upvotes: 1
Views: 979
Reputation: 11064
When I run the supplied code on the master branch, without parallelism, I get:
CpSolverResponse:
status: OPTIMAL
objective: 213
best_bound: 213
booleans: 210
conflicts: 31
branches: 617
propagations: 5226
integer_propagations: 8220
restarts: 428
lp_iterations: 130
walltime: 0.021303
usertime: 0.021303
deterministic_time: 0.0011162
primal_integral: 0.00536794
Do you get a different result?
Upvotes: 1